flex-md 3.2.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/__tests__/structural.test.js +28 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +108 -0
- package/dist/index.cjs +62 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/md/outline.d.ts +6 -3
- package/dist/md/outline.js +28 -50
- package/dist/md/parse.js +15 -4
- 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/dist/validate/connection.d.ts +12 -0
- package/dist/validate/connection.js +44 -0
- package/docs/Recommended New Strategies for AI Request Builder.md +691 -0
- package/package.json +8 -3
- 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.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 → __tests__/structural.test.d.ts} +0 -0
- /package/dist/{test-runner.d.ts → tokens/types.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.enforceFlexMd = exports.repairToMarkdownLevel = exports.detectResponseKind = exports.buildIssuesEnvelopeAuto = exports.buildIssuesEnvelope = exports.parseIssuesEnvelope = exports.processResponseMarkdown = exports.extractFromMarkdown = exports.checkConnection = exports.hasFlexMdContract = exports.checkCompliance = exports.validateMarkdownAgainstOfs = exports.enrichInstructionsWithFlexMd = exports.enrichInstructions = exports.buildMarkdownGuidance = exports.stringifyOutputFormatSpec = exports.validateFormat = exports.parseOutputFormatSpec = exports.buildOutline = void 0;
|
|
18
|
+
// Core SFMD Types
|
|
19
|
+
__exportStar(require("./types.js"), exports);
|
|
20
|
+
__exportStar(require("./strictness/types.js"), exports);
|
|
21
|
+
// Shared MD Parsing
|
|
22
|
+
__exportStar(require("./md/parse.js"), exports);
|
|
23
|
+
var outline_js_1 = require("./md/outline.js");
|
|
24
|
+
Object.defineProperty(exports, "buildOutline", { enumerable: true, get: function () { return outline_js_1.buildOutline; } });
|
|
25
|
+
// Output Format Spec (OFS)
|
|
26
|
+
var parser_js_1 = require("./ofs/parser.js");
|
|
27
|
+
Object.defineProperty(exports, "parseOutputFormatSpec", { enumerable: true, get: function () { return parser_js_1.parseOutputFormatSpec; } });
|
|
28
|
+
Object.defineProperty(exports, "validateFormat", { enumerable: true, get: function () { return parser_js_1.validateFormat; } });
|
|
29
|
+
var stringify_js_1 = require("./ofs/stringify.js");
|
|
30
|
+
Object.defineProperty(exports, "stringifyOutputFormatSpec", { enumerable: true, get: function () { return stringify_js_1.stringifyOutputFormatSpec; } });
|
|
31
|
+
var enricher_js_1 = require("./ofs/enricher.js");
|
|
32
|
+
Object.defineProperty(exports, "buildMarkdownGuidance", { enumerable: true, get: function () { return enricher_js_1.buildMarkdownGuidance; } });
|
|
33
|
+
Object.defineProperty(exports, "enrichInstructions", { enumerable: true, get: function () { return enricher_js_1.enrichInstructions; } });
|
|
34
|
+
Object.defineProperty(exports, "enrichInstructionsWithFlexMd", { enumerable: true, get: function () { return enricher_js_1.enrichInstructionsWithFlexMd; } });
|
|
35
|
+
// Validation & Extraction
|
|
36
|
+
var validate_js_1 = require("./validate/validate.js");
|
|
37
|
+
Object.defineProperty(exports, "validateMarkdownAgainstOfs", { enumerable: true, get: function () { return validate_js_1.validateMarkdownAgainstOfs; } });
|
|
38
|
+
var compliance_js_1 = require("./validate/compliance.js");
|
|
39
|
+
Object.defineProperty(exports, "checkCompliance", { enumerable: true, get: function () { return compliance_js_1.checkCompliance; } });
|
|
40
|
+
Object.defineProperty(exports, "hasFlexMdContract", { enumerable: true, get: function () { return compliance_js_1.hasFlexMdContract; } });
|
|
41
|
+
var connection_js_1 = require("./validate/connection.js");
|
|
42
|
+
Object.defineProperty(exports, "checkConnection", { enumerable: true, get: function () { return connection_js_1.checkConnection; } });
|
|
43
|
+
var extract_js_1 = require("./extract/extract.js");
|
|
44
|
+
Object.defineProperty(exports, "extractFromMarkdown", { enumerable: true, get: function () { return extract_js_1.extractFromMarkdown; } });
|
|
45
|
+
// Processor & Fallback
|
|
46
|
+
var processor_js_1 = require("./strictness/processor.js");
|
|
47
|
+
Object.defineProperty(exports, "processResponseMarkdown", { enumerable: true, get: function () { return processor_js_1.processResponseMarkdown; } });
|
|
48
|
+
var issuesEnvelope_js_1 = require("./ofs/issuesEnvelope.js");
|
|
49
|
+
Object.defineProperty(exports, "parseIssuesEnvelope", { enumerable: true, get: function () { return issuesEnvelope_js_1.parseIssuesEnvelope; } });
|
|
50
|
+
Object.defineProperty(exports, "buildIssuesEnvelope", { enumerable: true, get: function () { return issuesEnvelope_js_1.buildIssuesEnvelope; } });
|
|
51
|
+
Object.defineProperty(exports, "buildIssuesEnvelopeAuto", { enumerable: true, get: function () { return issuesEnvelope_js_1.buildIssuesEnvelopeAuto; } });
|
|
52
|
+
// Pipeline
|
|
53
|
+
var kind_js_1 = require("./pipeline/kind.js");
|
|
54
|
+
Object.defineProperty(exports, "detectResponseKind", { enumerable: true, get: function () { return kind_js_1.detectResponseKind; } });
|
|
55
|
+
var repair_js_1 = require("./pipeline/repair.js");
|
|
56
|
+
Object.defineProperty(exports, "repairToMarkdownLevel", { enumerable: true, get: function () { return repair_js_1.repairToMarkdownLevel; } });
|
|
57
|
+
var enforce_js_1 = require("./pipeline/enforce.js");
|
|
58
|
+
Object.defineProperty(exports, "enforceFlexMd", { enumerable: true, get: function () { return enforce_js_1.enforceFlexMd; } });
|
|
59
|
+
// JSON Detection
|
|
60
|
+
__exportStar(require("./detect/json/index.js"), exports);
|
|
61
|
+
// Token Estimation
|
|
62
|
+
__exportStar(require("./tokens/index.js"), exports);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export * from "./types.js";
|
|
2
2
|
export * from "./strictness/types.js";
|
|
3
3
|
export * from "./md/parse.js";
|
|
4
|
+
export { buildOutline } from "./md/outline.js";
|
|
4
5
|
export { parseOutputFormatSpec, validateFormat } from "./ofs/parser.js";
|
|
5
6
|
export { stringifyOutputFormatSpec } from "./ofs/stringify.js";
|
|
6
7
|
export { buildMarkdownGuidance, enrichInstructions, enrichInstructionsWithFlexMd } from "./ofs/enricher.js";
|
|
7
8
|
export { validateMarkdownAgainstOfs } from "./validate/validate.js";
|
|
8
9
|
export { checkCompliance, hasFlexMdContract } from "./validate/compliance.js";
|
|
10
|
+
export { checkConnection } from "./validate/connection.js";
|
|
9
11
|
export { extractFromMarkdown } from "./extract/extract.js";
|
|
10
12
|
export { processResponseMarkdown } from "./strictness/processor.js";
|
|
11
13
|
export { parseIssuesEnvelope, buildIssuesEnvelope, buildIssuesEnvelopeAuto } from "./ofs/issuesEnvelope.js";
|
|
@@ -13,3 +15,4 @@ export { detectResponseKind } from "./pipeline/kind.js";
|
|
|
13
15
|
export { repairToMarkdownLevel } from "./pipeline/repair.js";
|
|
14
16
|
export { enforceFlexMd } from "./pipeline/enforce.js";
|
|
15
17
|
export * from "./detect/json/index.js";
|
|
18
|
+
export * from "./tokens/index.js";
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./types.js";
|
|
|
3
3
|
export * from "./strictness/types.js";
|
|
4
4
|
// Shared MD Parsing
|
|
5
5
|
export * from "./md/parse.js";
|
|
6
|
+
export { buildOutline } from "./md/outline.js";
|
|
6
7
|
// Output Format Spec (OFS)
|
|
7
8
|
export { parseOutputFormatSpec, validateFormat } from "./ofs/parser.js";
|
|
8
9
|
export { stringifyOutputFormatSpec } from "./ofs/stringify.js";
|
|
@@ -10,6 +11,7 @@ export { buildMarkdownGuidance, enrichInstructions, enrichInstructionsWithFlexMd
|
|
|
10
11
|
// Validation & Extraction
|
|
11
12
|
export { validateMarkdownAgainstOfs } from "./validate/validate.js";
|
|
12
13
|
export { checkCompliance, hasFlexMdContract } from "./validate/compliance.js";
|
|
14
|
+
export { checkConnection } from "./validate/connection.js";
|
|
13
15
|
export { extractFromMarkdown } from "./extract/extract.js";
|
|
14
16
|
// Processor & Fallback
|
|
15
17
|
export { processResponseMarkdown } from "./strictness/processor.js";
|
|
@@ -20,3 +22,5 @@ export { repairToMarkdownLevel } from "./pipeline/repair.js";
|
|
|
20
22
|
export { enforceFlexMd } from "./pipeline/enforce.js";
|
|
21
23
|
// JSON Detection
|
|
22
24
|
export * from "./detect/json/index.js";
|
|
25
|
+
// Token Estimation
|
|
26
|
+
export * from "./tokens/index.js";
|
package/dist/md/outline.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { MdOutline } from "../types.js";
|
|
2
2
|
/**
|
|
3
|
-
* Builds a nested outline
|
|
4
|
-
* Supports any heading level and captures content between headings.
|
|
3
|
+
* Builds a nested tree of headings (outline) from Markdown text.
|
|
5
4
|
*/
|
|
6
5
|
export declare function buildOutline(md: string): MdOutline;
|
|
6
|
+
/**
|
|
7
|
+
* Slugifies a string into an internal key.
|
|
8
|
+
*/
|
|
9
|
+
export declare function slugify(text: string): string;
|
package/dist/md/outline.js
CHANGED
|
@@ -1,67 +1,45 @@
|
|
|
1
|
+
import { parseHeadingsAndSections } from "./parse.js";
|
|
1
2
|
/**
|
|
2
|
-
* Builds a nested outline
|
|
3
|
-
* Supports any heading level and captures content between headings.
|
|
3
|
+
* Builds a nested tree of headings (outline) from Markdown text.
|
|
4
4
|
*/
|
|
5
5
|
export function buildOutline(md) {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
for (let i = 0; i < lines.length; i++) {
|
|
9
|
-
const m = (lines[i] ?? "").match(/^(#{1,6})\s+(.+?)\s*$/);
|
|
10
|
-
if (m) {
|
|
11
|
-
headings.push({ idx: i, level: m[1].length, title: cleanTitle(m[2]) });
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
if (!headings.length) {
|
|
15
|
-
return { type: "md_outline", nodes: [] };
|
|
16
|
-
}
|
|
17
|
-
const nodes = [];
|
|
6
|
+
const sections = parseHeadingsAndSections(md);
|
|
7
|
+
const roots = [];
|
|
18
8
|
const stack = [];
|
|
19
|
-
for (
|
|
20
|
-
const
|
|
21
|
-
const next = headings[h + 1];
|
|
22
|
-
const contentStart = cur.idx + 1;
|
|
23
|
-
const contentEnd = next ? next.idx : lines.length;
|
|
24
|
-
const content_md = lines.slice(contentStart, contentEnd).join("\n").trimEnd() + "\n";
|
|
9
|
+
for (const s of sections) {
|
|
10
|
+
const h = s.heading;
|
|
25
11
|
const node = {
|
|
26
|
-
title:
|
|
27
|
-
level:
|
|
28
|
-
key:
|
|
29
|
-
content_md,
|
|
12
|
+
title: h.name,
|
|
13
|
+
level: h.level,
|
|
14
|
+
key: h.norm,
|
|
15
|
+
content_md: s.body,
|
|
30
16
|
children: []
|
|
31
17
|
};
|
|
32
|
-
|
|
18
|
+
// Pop from stack until we find a parent (level < node.level)
|
|
19
|
+
while (stack.length > 0 && stack[stack.length - 1].level >= node.level) {
|
|
33
20
|
stack.pop();
|
|
34
21
|
}
|
|
35
|
-
if (
|
|
36
|
-
|
|
22
|
+
if (stack.length === 0) {
|
|
23
|
+
roots.push(node);
|
|
37
24
|
}
|
|
38
25
|
else {
|
|
39
26
|
stack[stack.length - 1].children.push(node);
|
|
40
27
|
}
|
|
41
28
|
stack.push(node);
|
|
42
29
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
function cleanTitle(t) {
|
|
47
|
-
return t.trim().replace(/[:\-–—]\s*$/, "").trim();
|
|
48
|
-
}
|
|
49
|
-
function slugify(t) {
|
|
50
|
-
return t.toLowerCase()
|
|
51
|
-
.replace(/[:\-–—]+$/g, "")
|
|
52
|
-
.replace(/\s+/g, "_")
|
|
53
|
-
.replace(/[^a-z0-9_]/g, "")
|
|
54
|
-
.replace(/_+/g, "_")
|
|
55
|
-
.replace(/^_+|_+$/g, "");
|
|
56
|
-
}
|
|
57
|
-
function assignKeys(nodes) {
|
|
58
|
-
const seen = new Map();
|
|
59
|
-
const visit = (n) => {
|
|
60
|
-
const base = slugify(n.title) || "section";
|
|
61
|
-
const count = (seen.get(base) ?? 0) + 1;
|
|
62
|
-
seen.set(base, count);
|
|
63
|
-
n.key = count === 1 ? base : `${base}__${count}`;
|
|
64
|
-
n.children.forEach(visit);
|
|
30
|
+
return {
|
|
31
|
+
type: "md_outline",
|
|
32
|
+
nodes: roots
|
|
65
33
|
};
|
|
66
|
-
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Slugifies a string into an internal key.
|
|
37
|
+
*/
|
|
38
|
+
export function slugify(text) {
|
|
39
|
+
return text
|
|
40
|
+
.toLowerCase()
|
|
41
|
+
.trim()
|
|
42
|
+
.replace(/[^\w\s-]/g, "")
|
|
43
|
+
.replace(/[\s_-]+/g, "-")
|
|
44
|
+
.replace(/^-+|-+$/g, "");
|
|
67
45
|
}
|
package/dist/md/parse.js
CHANGED
|
@@ -28,17 +28,28 @@ export function extractFencedBlocks(text) {
|
|
|
28
28
|
return blocks;
|
|
29
29
|
}
|
|
30
30
|
export function parseHeadingsAndSections(md) {
|
|
31
|
-
|
|
31
|
+
// Standard headings #... and alternative ===key
|
|
32
|
+
const rx = /^((?:#{1,6})\s+(.+?)\s*|===(.+?)\s*)$/gm;
|
|
32
33
|
const headings = [];
|
|
33
34
|
let m;
|
|
34
35
|
while ((m = rx.exec(md)) !== null) {
|
|
35
|
-
const
|
|
36
|
-
|
|
36
|
+
const full = m[1] ?? "";
|
|
37
|
+
let level;
|
|
38
|
+
let name;
|
|
39
|
+
if (full.startsWith("===")) {
|
|
40
|
+
level = 1; // Treat ===key as a top-level heading
|
|
41
|
+
name = (m[3] ?? "").trim();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const hashes = (full.match(/^#+/) ?? [""])[0];
|
|
45
|
+
level = hashes.length;
|
|
46
|
+
name = (m[2] ?? "").trim();
|
|
47
|
+
}
|
|
37
48
|
const raw = m[0] ?? "";
|
|
38
49
|
const start = m.index;
|
|
39
50
|
const end = start + raw.length;
|
|
40
51
|
headings.push({
|
|
41
|
-
level
|
|
52
|
+
level,
|
|
42
53
|
raw,
|
|
43
54
|
name,
|
|
44
55
|
norm: normalizeName(name),
|
package/dist/ofs/parser.js
CHANGED
|
@@ -96,36 +96,53 @@ export function parseOutputFormatSpec(md, opts = {}) {
|
|
|
96
96
|
const tables = [];
|
|
97
97
|
let emptySectionValue;
|
|
98
98
|
let inTables = false;
|
|
99
|
+
let currentSection = null;
|
|
99
100
|
for (const rawLine of block.split("\n")) {
|
|
100
101
|
const line = rawLine.trim();
|
|
101
102
|
if (/^tables\b/i.test(line)) {
|
|
102
103
|
inTables = true;
|
|
104
|
+
currentSection = null;
|
|
103
105
|
continue;
|
|
104
106
|
}
|
|
105
107
|
if (/^empty sections\b/i.test(line)) {
|
|
106
108
|
inTables = false;
|
|
109
|
+
currentSection = null;
|
|
107
110
|
continue;
|
|
108
111
|
}
|
|
109
112
|
// Empty section rule
|
|
110
113
|
const mNone = line.match(/write\s+`([^`]+)`/i);
|
|
111
114
|
if (/empty/i.test(line) && mNone) {
|
|
112
115
|
emptySectionValue = mNone[1];
|
|
116
|
+
currentSection = null;
|
|
113
117
|
continue;
|
|
114
118
|
}
|
|
115
119
|
// bullet items
|
|
116
120
|
const bullet = line.match(/^- (.+)$/);
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
if (bullet) {
|
|
122
|
+
const item = bullet[1];
|
|
123
|
+
if (inTables) {
|
|
124
|
+
const t = parseTableDecl(item);
|
|
125
|
+
if (t)
|
|
126
|
+
tables.push(t);
|
|
127
|
+
currentSection = null;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const s = parseSectionDecl(item, !!opts.allowDelimiterFallbacks);
|
|
131
|
+
if (s) {
|
|
132
|
+
sections.push(s);
|
|
133
|
+
currentSection = s;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
currentSection = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
124
139
|
continue;
|
|
125
140
|
}
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
141
|
+
// If not a bullet and we have a current section, it's an instruction
|
|
142
|
+
if (currentSection && line.length > 0) {
|
|
143
|
+
const existing = currentSection.instruction || "";
|
|
144
|
+
currentSection.instruction = existing ? `${existing} ${line}` : line;
|
|
145
|
+
}
|
|
129
146
|
}
|
|
130
147
|
if (!sections.length)
|
|
131
148
|
return null;
|
|
@@ -169,6 +186,10 @@ function normalizeSectionKind(rest) {
|
|
|
169
186
|
return "list";
|
|
170
187
|
if (r.includes("prose") || r.includes("text"))
|
|
171
188
|
return "text";
|
|
189
|
+
if (r.includes("ordered") && r.includes("table"))
|
|
190
|
+
return "ordered_table";
|
|
191
|
+
if (r.includes("table"))
|
|
192
|
+
return "table";
|
|
172
193
|
return null;
|
|
173
194
|
}
|
|
174
195
|
function parseTableDecl(item) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AutoFixResult, Improvement } from './types.js';
|
|
2
|
+
import type { OutputFormatSpec } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Automatically apply fixable improvements
|
|
5
|
+
*/
|
|
6
|
+
export declare function autoFix(spec: OutputFormatSpec, improvements: Improvement[], options?: {
|
|
7
|
+
applyQuickWinsOnly?: boolean;
|
|
8
|
+
maxPriority?: Improvement['priority'];
|
|
9
|
+
skipManual?: boolean;
|
|
10
|
+
}): AutoFixResult;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatically apply fixable improvements
|
|
3
|
+
*/
|
|
4
|
+
export function autoFix(spec, improvements, options = {}) {
|
|
5
|
+
const { applyQuickWinsOnly = false, maxPriority = 'low', skipManual = true } = options;
|
|
6
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
7
|
+
const maxPriorityLevel = priorityOrder[maxPriority] ?? 3;
|
|
8
|
+
const appliedFixes = [];
|
|
9
|
+
const skippedFixes = [];
|
|
10
|
+
const fixedSections = spec.sections.map(section => {
|
|
11
|
+
const sectionImprovements = improvements.filter(imp => imp.sectionName === section.name);
|
|
12
|
+
let currentInstruction = section.instruction;
|
|
13
|
+
let modified = false;
|
|
14
|
+
// Sort improvements by priority for this section
|
|
15
|
+
const sortedImps = [...sectionImprovements].sort((a, b) => (priorityOrder[a.priority] ?? 3) - (priorityOrder[b.priority] ?? 3));
|
|
16
|
+
for (const improvement of sortedImps) {
|
|
17
|
+
// Check if should apply
|
|
18
|
+
if ((priorityOrder[improvement.priority] ?? 3) > maxPriorityLevel)
|
|
19
|
+
continue;
|
|
20
|
+
if (applyQuickWinsOnly && improvement.effort !== 'trivial' && improvement.effort !== 'easy')
|
|
21
|
+
continue;
|
|
22
|
+
if (!improvement.autoFixable) {
|
|
23
|
+
skippedFixes.push({
|
|
24
|
+
sectionName: section.name,
|
|
25
|
+
reason: 'Requires manual review',
|
|
26
|
+
manualActionNeeded: improvement.suggestion
|
|
27
|
+
});
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
// Apply fix (only the first one for simplicity, or could chain)
|
|
31
|
+
// For now, we chain them by using the 'after' of the previous as 'before' of the next
|
|
32
|
+
// But 'before' in improvement object is relative to the ORIGINAL state.
|
|
33
|
+
// So we just apply the one with the highest priority for the section.
|
|
34
|
+
appliedFixes.push({
|
|
35
|
+
sectionName: section.name,
|
|
36
|
+
improvement: improvement.issue,
|
|
37
|
+
before: currentInstruction || '',
|
|
38
|
+
after: improvement.after
|
|
39
|
+
});
|
|
40
|
+
currentInstruction = improvement.after;
|
|
41
|
+
modified = true;
|
|
42
|
+
break; // Only apply one fix per section in this pass to avoid conflicts
|
|
43
|
+
}
|
|
44
|
+
return modified ? { ...section, instruction: currentInstruction } : section;
|
|
45
|
+
});
|
|
46
|
+
const summary = `Applied ${appliedFixes.length} fixes, skipped ${skippedFixes.length}`;
|
|
47
|
+
return {
|
|
48
|
+
fixed: {
|
|
49
|
+
...spec,
|
|
50
|
+
sections: fixedSections
|
|
51
|
+
},
|
|
52
|
+
appliedFixes,
|
|
53
|
+
skippedFixes,
|
|
54
|
+
summary
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CognitiveCost, LevelCostComparison } from './types.js';
|
|
2
|
+
import type { OutputFormatSpec } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Calculate cognitive cost of creating/maintaining spec
|
|
5
|
+
*/
|
|
6
|
+
export declare function calculateCognitiveCost(spec: OutputFormatSpec): CognitiveCost;
|
|
7
|
+
/**
|
|
8
|
+
* Compare cognitive cost across different compliance levels
|
|
9
|
+
*/
|
|
10
|
+
export declare function compareLevelCosts(spec: OutputFormatSpec): LevelCostComparison;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { parseSystemPart } from './parser.js';
|
|
2
|
+
import { detectSystemPartLevel } from './compliance.js';
|
|
3
|
+
/**
|
|
4
|
+
* Calculate cognitive cost of creating/maintaining spec
|
|
5
|
+
*/
|
|
6
|
+
export function calculateCognitiveCost(spec) {
|
|
7
|
+
const perSection = [];
|
|
8
|
+
let totalCost = 0;
|
|
9
|
+
let complexityCost = 0;
|
|
10
|
+
let decisionCost = 0;
|
|
11
|
+
for (const section of spec.sections) {
|
|
12
|
+
const sectionCost = calculateSectionCost(section);
|
|
13
|
+
perSection.push(sectionCost);
|
|
14
|
+
totalCost += sectionCost.cost;
|
|
15
|
+
// Track breakdown
|
|
16
|
+
complexityCost += getComplexityCost(section.kind || 'text');
|
|
17
|
+
decisionCost += getDecisionCost(sectionCost.level);
|
|
18
|
+
}
|
|
19
|
+
const baseCost = spec.sections.length * 1; // Base effort per section
|
|
20
|
+
return {
|
|
21
|
+
totalCost: Math.min(100, totalCost),
|
|
22
|
+
perSection,
|
|
23
|
+
breakdown: {
|
|
24
|
+
baseCost,
|
|
25
|
+
complexityCost,
|
|
26
|
+
decisionCost,
|
|
27
|
+
totalSections: spec.sections.length
|
|
28
|
+
},
|
|
29
|
+
recommendation: generateCostRecommendation(totalCost, spec.sections.length)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Calculate cost for individual section
|
|
34
|
+
*/
|
|
35
|
+
function calculateSectionCost(section) {
|
|
36
|
+
const kind = section.kind || 'text';
|
|
37
|
+
const systemPart = parseSystemPart(section.instruction, kind);
|
|
38
|
+
const level = systemPart ? detectSystemPartLevel(systemPart, kind) : null;
|
|
39
|
+
const factors = [];
|
|
40
|
+
let cost = 0;
|
|
41
|
+
// Base cost by level
|
|
42
|
+
const levelCosts = { 0: 0, 1: 1, 2: 3, 3: 5 };
|
|
43
|
+
const levelCost = levelCosts[level || 0];
|
|
44
|
+
cost += levelCost;
|
|
45
|
+
if (level !== null && level > 0) {
|
|
46
|
+
factors.push({
|
|
47
|
+
factor: 'system_part_level',
|
|
48
|
+
impact: levelCost,
|
|
49
|
+
description: `L${level} requires ${getLevelEffortDescription(level)}`
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Complexity by section kind
|
|
53
|
+
const kindCost = getComplexityCost(kind);
|
|
54
|
+
cost += kindCost;
|
|
55
|
+
if (kindCost > 0) {
|
|
56
|
+
factors.push({
|
|
57
|
+
factor: 'section_complexity',
|
|
58
|
+
impact: kindCost,
|
|
59
|
+
description: getKindComplexityDescription(kind)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// Additional cost for specific patterns
|
|
63
|
+
if (systemPart) {
|
|
64
|
+
const patternCost = getPatternCost(systemPart);
|
|
65
|
+
cost += patternCost.cost;
|
|
66
|
+
if (patternCost.cost > 0) {
|
|
67
|
+
factors.push({
|
|
68
|
+
factor: 'pattern_complexity',
|
|
69
|
+
impact: patternCost.cost,
|
|
70
|
+
description: patternCost.description
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
sectionName: section.name,
|
|
76
|
+
cost,
|
|
77
|
+
level,
|
|
78
|
+
factors
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function getLevelEffortDescription(level) {
|
|
82
|
+
const descriptions = {
|
|
83
|
+
0: 'no thinking',
|
|
84
|
+
1: 'picking from 4 simple options',
|
|
85
|
+
2: 'thinking about ranges',
|
|
86
|
+
3: 'considering minimums and approximations'
|
|
87
|
+
};
|
|
88
|
+
return descriptions[level];
|
|
89
|
+
}
|
|
90
|
+
function getComplexityCost(kind) {
|
|
91
|
+
const costs = {
|
|
92
|
+
'text': 0, // Simplest
|
|
93
|
+
'list': 1,
|
|
94
|
+
'ordered_list': 1,
|
|
95
|
+
'code': 1,
|
|
96
|
+
'table': 2, // Most complex (2 dimensions)
|
|
97
|
+
'ordered_table': 2
|
|
98
|
+
};
|
|
99
|
+
return costs[kind] || 0;
|
|
100
|
+
}
|
|
101
|
+
function getKindComplexityDescription(kind) {
|
|
102
|
+
const descriptions = {
|
|
103
|
+
'text': 'Simple single value',
|
|
104
|
+
'list': 'Need to estimate item count',
|
|
105
|
+
'ordered_list': 'Need to estimate item count',
|
|
106
|
+
'code': 'Need to estimate line count',
|
|
107
|
+
'table': 'Need to think about rows AND columns',
|
|
108
|
+
'ordered_table': 'Need to think about rows AND columns'
|
|
109
|
+
};
|
|
110
|
+
return descriptions[kind] || '';
|
|
111
|
+
}
|
|
112
|
+
function getDecisionCost(level) {
|
|
113
|
+
if (level === null)
|
|
114
|
+
return 0;
|
|
115
|
+
const costs = { 0: 0, 1: 0.5, 2: 1.5, 3: 2.5 };
|
|
116
|
+
return costs[level];
|
|
117
|
+
}
|
|
118
|
+
function getPatternCost(systemPart) {
|
|
119
|
+
const { parsed } = systemPart;
|
|
120
|
+
switch (parsed.type) {
|
|
121
|
+
case 'items':
|
|
122
|
+
case 'lines':
|
|
123
|
+
if (parsed.atLeast) {
|
|
124
|
+
return { cost: 1, description: 'Deciding "at least" minimum requires judgment' };
|
|
125
|
+
}
|
|
126
|
+
if (parsed.max !== null) {
|
|
127
|
+
return { cost: 0.5, description: 'Ranges require estimating both min and max' };
|
|
128
|
+
}
|
|
129
|
+
return { cost: 0, description: '' };
|
|
130
|
+
case 'table':
|
|
131
|
+
let tableCost = 0;
|
|
132
|
+
let desc = [];
|
|
133
|
+
if (parsed.rows.atLeast || parsed.columns.atLeast) {
|
|
134
|
+
tableCost += 1;
|
|
135
|
+
desc.push('minimums require judgment');
|
|
136
|
+
}
|
|
137
|
+
if (parsed.rows.max !== null || parsed.columns.max !== null) {
|
|
138
|
+
tableCost += 0.5;
|
|
139
|
+
desc.push('ranges require estimation');
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
cost: tableCost,
|
|
143
|
+
description: desc.length > 0 ? `Two dimensions: ${desc.join(', ')}` : ''
|
|
144
|
+
};
|
|
145
|
+
default:
|
|
146
|
+
return { cost: 0, description: '' };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function generateCostRecommendation(totalCost, sectionCount) {
|
|
150
|
+
if (sectionCount === 0)
|
|
151
|
+
return 'Empty spec';
|
|
152
|
+
const avgCost = totalCost / sectionCount;
|
|
153
|
+
if (avgCost < 2) {
|
|
154
|
+
return 'Low cognitive load - easy to write and maintain';
|
|
155
|
+
}
|
|
156
|
+
else if (avgCost < 4) {
|
|
157
|
+
return 'Moderate cognitive load - reasonable effort required';
|
|
158
|
+
}
|
|
159
|
+
else if (avgCost < 6) {
|
|
160
|
+
return 'High cognitive load - consider simplifying some sections';
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
return 'Very high cognitive load - strongly consider reducing to L2 or L1 for some sections';
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Compare cognitive cost across different compliance levels
|
|
168
|
+
*/
|
|
169
|
+
export function compareLevelCosts(spec) {
|
|
170
|
+
const costs = { 0: 0, 1: 0, 2: 0, 3: 0 };
|
|
171
|
+
// Simulate cost at each level
|
|
172
|
+
for (let level = 0; level <= 3; level++) {
|
|
173
|
+
costs[level] = estimateCostAtLevel(spec, level);
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
byLevel: costs,
|
|
177
|
+
recommendation: getBestLevelForCost(costs)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function estimateCostAtLevel(spec, level) {
|
|
181
|
+
const levelBaseCosts = { 0: 0, 1: 1, 2: 3, 3: 5 };
|
|
182
|
+
const baseCost = levelBaseCosts[level];
|
|
183
|
+
let total = spec.sections.length * baseCost;
|
|
184
|
+
// Add complexity cost
|
|
185
|
+
for (const section of spec.sections) {
|
|
186
|
+
total += getComplexityCost(section.kind || 'text');
|
|
187
|
+
total += getDecisionCost(level);
|
|
188
|
+
}
|
|
189
|
+
return total;
|
|
190
|
+
}
|
|
191
|
+
function getBestLevelForCost(costs) {
|
|
192
|
+
// L2 is usually the sweet spot
|
|
193
|
+
if (costs[2] < 50) {
|
|
194
|
+
return 'L2 recommended - good balance of precision and effort';
|
|
195
|
+
}
|
|
196
|
+
else if (costs[1] < 30) {
|
|
197
|
+
return 'L1 recommended - keep it simple for this spec';
|
|
198
|
+
}
|
|
199
|
+
else if (costs[3] < 70) {
|
|
200
|
+
return 'L3 acceptable - complexity is manageable';
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
return 'Consider reducing spec size or using L1/L2';
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ComplianceLevel, ComplianceReport, SystemPart } from './types.js';
|
|
2
|
+
import type { OutputFormatSpec, SectionKind } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Detect the compliance level of a system part
|
|
5
|
+
*/
|
|
6
|
+
export declare function detectSystemPartLevel(systemPart: SystemPart, kind: SectionKind | 'code'): ComplianceLevel;
|
|
7
|
+
/**
|
|
8
|
+
* Check if a spec complies with a target level
|
|
9
|
+
*/
|
|
10
|
+
export declare function checkSpecCompliance(spec: OutputFormatSpec, targetLevel?: ComplianceLevel): ComplianceReport;
|