flex-md 4.5.3 → 4.5.4
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/dist/__tests__/diagnostics.test.js +47 -45
- package/dist/__tests__/ofs.test.js +30 -28
- package/dist/__tests__/structural.test.js +21 -19
- package/dist/__tests__/validate.test.js +29 -27
- package/dist/cli/index.js +15 -13
- package/dist/detect/json/detectIntent.js +4 -1
- package/dist/detect/json/detectMarkdown.js +35 -29
- package/dist/detect/json/detectPresence.js +6 -2
- package/dist/detect/json/index.js +31 -10
- package/dist/detect/json/types.js +2 -1
- package/dist/extract/extract.js +14 -11
- package/dist/extract/types.js +2 -1
- package/dist/index.js +67 -22
- package/dist/logger.js +6 -3
- package/dist/md/match.js +4 -1
- package/dist/md/normalize.js +4 -1
- package/dist/md/outline.js +8 -4
- package/dist/md/parse.js +20 -11
- package/dist/ofs/adapter.js +49 -45
- package/dist/ofs/enricher.js +8 -3
- package/dist/ofs/infer.js +7 -4
- package/dist/ofs/issuesEnvelope.js +10 -5
- package/dist/ofs/memory.js +10 -6
- package/dist/ofs/parser.js +8 -4
- package/dist/ofs/stringify.js +4 -1
- package/dist/pipeline/enforce.js +15 -12
- package/dist/pipeline/kind.js +6 -3
- package/dist/pipeline/repair.js +11 -8
- package/dist/strictness/container.js +4 -1
- package/dist/strictness/processor.js +10 -7
- package/dist/strictness/types.js +4 -1
- package/dist/tokens/auto-fix.js +4 -1
- package/dist/tokens/cognitive-cost.js +10 -6
- package/dist/tokens/compliance.js +8 -4
- package/dist/tokens/confidence.js +9 -6
- package/dist/tokens/estimator.js +23 -18
- package/dist/tokens/improvements.js +16 -12
- package/dist/tokens/index.js +33 -16
- package/dist/tokens/parser.js +7 -4
- package/dist/tokens/patterns.js +5 -2
- package/dist/tokens/smart-report.js +14 -10
- package/dist/tokens/spec-estimator.js +11 -8
- package/dist/tokens/types.js +2 -1
- package/dist/tokens/validator.js +6 -3
- package/dist/types.js +2 -1
- package/dist/validate/compliance.js +6 -2
- package/dist/validate/connection.js +8 -5
- package/dist/validate/types.js +2 -1
- package/dist/validate/validate.js +19 -16
- package/package.json +2 -2
|
@@ -1,59 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const index_js_1 = require("../index.js");
|
|
5
|
+
(0, vitest_1.describe)("Diagnostics", () => {
|
|
6
|
+
(0, vitest_1.describe)("checkCompliance", () => {
|
|
7
|
+
(0, vitest_1.it)("should return meetsCompliance: true for L0 if Markdown is mentioned", async () => {
|
|
8
|
+
const res = await (0, index_js_1.checkCompliance)("Reply in Markdown.", "L0");
|
|
9
|
+
(0, vitest_1.expect)(res.meetsCompliance).toBe(true);
|
|
10
|
+
(0, vitest_1.expect)(res.issues).toHaveLength(0);
|
|
11
|
+
});
|
|
12
|
+
(0, vitest_1.it)("should return issues for L2 if container is missing", async () => {
|
|
13
|
+
const res = await (0, index_js_1.checkCompliance)("Reply in Markdown. Include headings.", "L2");
|
|
14
|
+
(0, vitest_1.expect)(res.meetsCompliance).toBe(false);
|
|
15
|
+
(0, vitest_1.expect)(res.issues.some(i => i.type === "missing-container")).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.it)("should return false for hasFlexMdContract if requirements not met", async () => {
|
|
18
|
+
const ok = await (0, index_js_1.hasFlexMdContract)("Hello", "L0");
|
|
19
|
+
(0, vitest_1.expect)(ok).toBe(false);
|
|
18
20
|
});
|
|
19
21
|
});
|
|
20
|
-
describe("enrichInstructionsWithFlexMd", () => {
|
|
21
|
-
it("should append guidance to existing instructions", async () => {
|
|
22
|
-
const res = await enrichInstructionsWithFlexMd("Do a good job.", "L0");
|
|
23
|
-
expect(res.enriched).toContain("Do a good job.");
|
|
24
|
-
expect(res.enriched).toContain("Reply in Markdown.");
|
|
25
|
-
expect(res.changes).toHaveLength(1);
|
|
26
|
-
expect(res.metadata?.guidanceAdded).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
it("should track container addition for L2", async () => {
|
|
29
|
-
const res = await enrichInstructionsWithFlexMd("Do a good job.", "L2");
|
|
30
|
-
expect(res.changes.some(c => c.type === "added-container")).toBe(true);
|
|
31
|
-
expect(res.metadata?.containerAdded).toBe(true);
|
|
22
|
+
(0, vitest_1.describe)("enrichInstructionsWithFlexMd", () => {
|
|
23
|
+
(0, vitest_1.it)("should append guidance to existing instructions", async () => {
|
|
24
|
+
const res = await (0, index_js_1.enrichInstructionsWithFlexMd)("Do a good job.", "L0");
|
|
25
|
+
(0, vitest_1.expect)(res.enriched).toContain("Do a good job.");
|
|
26
|
+
(0, vitest_1.expect)(res.enriched).toContain("Reply in Markdown.");
|
|
27
|
+
(0, vitest_1.expect)(res.changes).toHaveLength(1);
|
|
28
|
+
(0, vitest_1.expect)(res.metadata?.guidanceAdded).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
(0, vitest_1.it)("should track container addition for L2", async () => {
|
|
31
|
+
const res = await (0, index_js_1.enrichInstructionsWithFlexMd)("Do a good job.", "L2");
|
|
32
|
+
(0, vitest_1.expect)(res.changes.some(c => c.type === "added-container")).toBe(true);
|
|
33
|
+
(0, vitest_1.expect)(res.metadata?.containerAdded).toBe(true);
|
|
32
34
|
});
|
|
33
35
|
});
|
|
34
|
-
describe("validateFormat", () => {
|
|
35
|
-
it("should validate a correct flex-md OFS", async () => {
|
|
36
|
+
(0, vitest_1.describe)("validateFormat", () => {
|
|
37
|
+
(0, vitest_1.it)("should validate a correct flex-md OFS", async () => {
|
|
36
38
|
const ofs = `## Output format\n- Summary — prose\n- Actions — list`;
|
|
37
|
-
const res = await validateFormat(ofs);
|
|
38
|
-
expect(res.valid).toBe(true);
|
|
39
|
-
expect(res.metadata?.sectionCount).toBe(2);
|
|
39
|
+
const res = await (0, index_js_1.validateFormat)(ofs);
|
|
40
|
+
(0, vitest_1.expect)(res.valid).toBe(true);
|
|
41
|
+
(0, vitest_1.expect)(res.metadata?.sectionCount).toBe(2);
|
|
40
42
|
});
|
|
41
|
-
it("should return errors for missing heading", async () => {
|
|
43
|
+
(0, vitest_1.it)("should return errors for missing heading", async () => {
|
|
42
44
|
const ofs = `- Summary — prose`;
|
|
43
|
-
const res = await validateFormat(ofs);
|
|
44
|
-
expect(res.valid).toBe(false);
|
|
45
|
-
expect(res.issues.some(i => i.type === "missing-heading")).toBe(true);
|
|
45
|
+
const res = await (0, index_js_1.validateFormat)(ofs);
|
|
46
|
+
(0, vitest_1.expect)(res.valid).toBe(false);
|
|
47
|
+
(0, vitest_1.expect)(res.issues.some(i => i.type === "missing-heading")).toBe(true);
|
|
46
48
|
});
|
|
47
|
-
it("should validate JSON schema", async () => {
|
|
49
|
+
(0, vitest_1.it)("should validate JSON schema", async () => {
|
|
48
50
|
const schema = `{"type": "object"}`;
|
|
49
|
-
const res = await validateFormat(schema, "json-schema");
|
|
50
|
-
expect(res.valid).toBe(true);
|
|
51
|
+
const res = await (0, index_js_1.validateFormat)(schema, "json-schema");
|
|
52
|
+
(0, vitest_1.expect)(res.valid).toBe(true);
|
|
51
53
|
});
|
|
52
|
-
it("should fail on invalid JSON schema", async () => {
|
|
54
|
+
(0, vitest_1.it)("should fail on invalid JSON schema", async () => {
|
|
53
55
|
const schema = `{"type": "object"`;
|
|
54
|
-
const res = await validateFormat(schema, "json-schema");
|
|
55
|
-
expect(res.valid).toBe(false);
|
|
56
|
-
expect(res.issues[0].type).toBe("syntax-error");
|
|
56
|
+
const res = await (0, index_js_1.validateFormat)(schema, "json-schema");
|
|
57
|
+
(0, vitest_1.expect)(res.valid).toBe(false);
|
|
58
|
+
(0, vitest_1.expect)(res.issues[0].type).toBe("syntax-error");
|
|
57
59
|
});
|
|
58
60
|
});
|
|
59
61
|
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const stringify_js_1 = require("../ofs/stringify.js");
|
|
5
|
+
const enricher_js_1 = require("../ofs/enricher.js");
|
|
6
|
+
(0, vitest_1.describe)("Instructions Output Format Block Generation", () => {
|
|
5
7
|
const spec = {
|
|
6
8
|
description: "Standard report format for technical analysis.",
|
|
7
9
|
sections: [
|
|
@@ -21,31 +23,31 @@ describe("Instructions Output Format Block Generation", () => {
|
|
|
21
23
|
],
|
|
22
24
|
emptySectionValue: "N/A"
|
|
23
25
|
};
|
|
24
|
-
it("should stringify an OFS object correctly", () => {
|
|
25
|
-
const md = stringifyOutputFormatSpec(spec);
|
|
26
|
-
expect(md).toContain("## Output format (Markdown)");
|
|
27
|
-
expect(md).toContain("Standard report format for technical analysis.");
|
|
28
|
-
expect(md).toContain("- Summary — text (required)");
|
|
29
|
-
expect(md).toContain("Description: A brief summary of the topic.");
|
|
30
|
-
expect(md).toContain("Instruction: Keep it under 3 sentences.");
|
|
31
|
-
expect(md).toContain("- Key Points — list (optional)");
|
|
32
|
-
expect(md).toContain("Description: Important takeaways.");
|
|
33
|
-
expect(md).toContain("If a section is empty, write `N/A`.");
|
|
26
|
+
(0, vitest_1.it)("should stringify an OFS object correctly", () => {
|
|
27
|
+
const md = (0, stringify_js_1.stringifyOutputFormatSpec)(spec);
|
|
28
|
+
(0, vitest_1.expect)(md).toContain("## Output format (Markdown)");
|
|
29
|
+
(0, vitest_1.expect)(md).toContain("Standard report format for technical analysis.");
|
|
30
|
+
(0, vitest_1.expect)(md).toContain("- Summary — text (required)");
|
|
31
|
+
(0, vitest_1.expect)(md).toContain("Description: A brief summary of the topic.");
|
|
32
|
+
(0, vitest_1.expect)(md).toContain("Instruction: Keep it under 3 sentences.");
|
|
33
|
+
(0, vitest_1.expect)(md).toContain("- Key Points — list (optional)");
|
|
34
|
+
(0, vitest_1.expect)(md).toContain("Description: Important takeaways.");
|
|
35
|
+
(0, vitest_1.expect)(md).toContain("If a section is empty, write `N/A`.");
|
|
34
36
|
});
|
|
35
|
-
it("should build markdown guidance (L1) correctly", () => {
|
|
36
|
-
const guidance = buildMarkdownGuidance(spec, { level: 1 });
|
|
37
|
-
expect(guidance).toContain("Include these section headings somewhere");
|
|
38
|
-
expect(guidance).toContain("- Summary");
|
|
39
|
-
expect(guidance).toContain("Description: A brief summary of the topic.");
|
|
40
|
-
expect(guidance).toContain("Instruction: Keep it under 3 sentences.");
|
|
41
|
-
expect(guidance).toContain("- Key Points");
|
|
42
|
-
expect(guidance).toContain("Description: Important takeaways.");
|
|
37
|
+
(0, vitest_1.it)("should build markdown guidance (L1) correctly", () => {
|
|
38
|
+
const guidance = (0, enricher_js_1.buildMarkdownGuidance)(spec, { level: 1 });
|
|
39
|
+
(0, vitest_1.expect)(guidance).toContain("Include these section headings somewhere");
|
|
40
|
+
(0, vitest_1.expect)(guidance).toContain("- Summary");
|
|
41
|
+
(0, vitest_1.expect)(guidance).toContain("Description: A brief summary of the topic.");
|
|
42
|
+
(0, vitest_1.expect)(guidance).toContain("Instruction: Keep it under 3 sentences.");
|
|
43
|
+
(0, vitest_1.expect)(guidance).toContain("- Key Points");
|
|
44
|
+
(0, vitest_1.expect)(guidance).toContain("Description: Important takeaways.");
|
|
43
45
|
});
|
|
44
|
-
it("should build markdown guidance (L3) correctly", () => {
|
|
45
|
-
const guidance = buildMarkdownGuidance(spec, { level: 3 });
|
|
46
|
-
expect(guidance).toContain("Return your entire answer inside a single ```markdown fenced block");
|
|
47
|
-
expect(guidance).toContain("- Summary");
|
|
48
|
-
expect(guidance).toContain("- Key Points (list)");
|
|
49
|
-
expect(guidance).toContain("Do not return JSON");
|
|
46
|
+
(0, vitest_1.it)("should build markdown guidance (L3) correctly", () => {
|
|
47
|
+
const guidance = (0, enricher_js_1.buildMarkdownGuidance)(spec, { level: 3 });
|
|
48
|
+
(0, vitest_1.expect)(guidance).toContain("Return your entire answer inside a single ```markdown fenced block");
|
|
49
|
+
(0, vitest_1.expect)(guidance).toContain("- Summary");
|
|
50
|
+
(0, vitest_1.expect)(guidance).toContain("- Key Points (list)");
|
|
51
|
+
(0, vitest_1.expect)(guidance).toContain("Do not return JSON");
|
|
50
52
|
});
|
|
51
53
|
});
|
|
@@ -1,28 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const index_js_1 = require("../index.js");
|
|
5
|
+
(0, vitest_1.describe)("Structural Diagnostics", () => {
|
|
6
|
+
(0, vitest_1.describe)("===key support", () => {
|
|
7
|
+
(0, vitest_1.it)("should parse ===key as a top-level section", () => {
|
|
6
8
|
const md = `===skills\nThis is the skills section.\n\n## Subskill\nDetails.`;
|
|
7
|
-
const outline = buildOutline(md);
|
|
8
|
-
expect(outline.nodes).toHaveLength(1);
|
|
9
|
-
expect(outline.nodes[0].title).toBe("skills");
|
|
10
|
-
expect(outline.nodes[0].children).toHaveLength(1);
|
|
11
|
-
expect(outline.nodes[0].children[0].title).toBe("Subskill");
|
|
9
|
+
const outline = (0, index_js_1.buildOutline)(md);
|
|
10
|
+
(0, vitest_1.expect)(outline.nodes).toHaveLength(1);
|
|
11
|
+
(0, vitest_1.expect)(outline.nodes[0].title).toBe("skills");
|
|
12
|
+
(0, vitest_1.expect)(outline.nodes[0].children).toHaveLength(1);
|
|
13
|
+
(0, vitest_1.expect)(outline.nodes[0].children[0].title).toBe("Subskill");
|
|
12
14
|
});
|
|
13
15
|
});
|
|
14
|
-
describe("checkConnection", () => {
|
|
15
|
-
it("should find a section by name", () => {
|
|
16
|
+
(0, vitest_1.describe)("checkConnection", () => {
|
|
17
|
+
(0, vitest_1.it)("should find a section by name", () => {
|
|
16
18
|
const md = `# Overview\n## Details\n===skills\nContent.`;
|
|
17
|
-
const res = checkConnection(md, "skills");
|
|
18
|
-
expect(res.connected).toBe(true);
|
|
19
|
-
expect(res.path).toContain("skills");
|
|
19
|
+
const res = (0, index_js_1.checkConnection)(md, "skills");
|
|
20
|
+
(0, vitest_1.expect)(res.connected).toBe(true);
|
|
21
|
+
(0, vitest_1.expect)(res.path).toContain("skills");
|
|
20
22
|
});
|
|
21
|
-
it("should fail gracefully for missing sections", () => {
|
|
23
|
+
(0, vitest_1.it)("should fail gracefully for missing sections", () => {
|
|
22
24
|
const md = `# Overview`;
|
|
23
|
-
const res = checkConnection(md, "missing");
|
|
24
|
-
expect(res.connected).toBe(false);
|
|
25
|
-
expect(res.suggestion).toContain("Overview");
|
|
25
|
+
const res = (0, index_js_1.checkConnection)(md, "missing");
|
|
26
|
+
(0, vitest_1.expect)(res.connected).toBe(false);
|
|
27
|
+
(0, vitest_1.expect)(res.suggestion).toContain("Overview");
|
|
26
28
|
});
|
|
27
29
|
});
|
|
28
30
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const validate_js_1 = require("../validate/validate.js");
|
|
5
|
+
const issuesEnvelope_js_1 = require("../ofs/issuesEnvelope.js");
|
|
4
6
|
const SPEC = {
|
|
5
7
|
emptySectionValue: "None",
|
|
6
8
|
sections: [
|
|
@@ -30,8 +32,8 @@ function mdL1Good() {
|
|
|
30
32
|
"- U",
|
|
31
33
|
].join("\n");
|
|
32
34
|
}
|
|
33
|
-
describe("parseIssuesEnvelope()", () => {
|
|
34
|
-
it("detects envelope and extracts bullets", () => {
|
|
35
|
+
(0, vitest_1.describe)("parseIssuesEnvelope()", () => {
|
|
36
|
+
(0, vitest_1.it)("detects envelope and extracts bullets", () => {
|
|
35
37
|
const env = [
|
|
36
38
|
"## Status",
|
|
37
39
|
"Non-compliant output (cannot be repaired to the required format).",
|
|
@@ -48,17 +50,17 @@ describe("parseIssuesEnvelope()", () => {
|
|
|
48
50
|
"## How to fix",
|
|
49
51
|
"- Do X",
|
|
50
52
|
].join("\n");
|
|
51
|
-
const parsed = parseIssuesEnvelope(env);
|
|
52
|
-
expect(parsed.isIssuesEnvelope).toBe(true);
|
|
53
|
-
expect(parsed.sections["issues"].bullets[0]).toContain("Missing required section");
|
|
53
|
+
const parsed = (0, issuesEnvelope_js_1.parseIssuesEnvelope)(env);
|
|
54
|
+
(0, vitest_1.expect)(parsed.isIssuesEnvelope).toBe(true);
|
|
55
|
+
(0, vitest_1.expect)(parsed.sections["issues"].bullets[0]).toContain("Missing required section");
|
|
54
56
|
});
|
|
55
57
|
});
|
|
56
|
-
describe("validateMarkdownAgainstOfs()", () => {
|
|
57
|
-
it("L0 accepts anything", () => {
|
|
58
|
-
const r = validateMarkdownAgainstOfs("whatever", SPEC, 0);
|
|
59
|
-
expect(r.ok).toBe(true);
|
|
58
|
+
(0, vitest_1.describe)("validateMarkdownAgainstOfs()", () => {
|
|
59
|
+
(0, vitest_1.it)("L0 accepts anything", () => {
|
|
60
|
+
const r = (0, validate_js_1.validateMarkdownAgainstOfs)("whatever", SPEC, 0);
|
|
61
|
+
(0, vitest_1.expect)(r.ok).toBe(true);
|
|
60
62
|
});
|
|
61
|
-
it("L1 fails when required section missing", () => {
|
|
63
|
+
(0, vitest_1.it)("L1 fails when required section missing", () => {
|
|
62
64
|
const md = [
|
|
63
65
|
"## Short answer",
|
|
64
66
|
"Yes.",
|
|
@@ -66,21 +68,21 @@ describe("validateMarkdownAgainstOfs()", () => {
|
|
|
66
68
|
"## Reasoning",
|
|
67
69
|
"1. A",
|
|
68
70
|
].join("\n");
|
|
69
|
-
const r = validateMarkdownAgainstOfs(md, SPEC, 1);
|
|
70
|
-
expect(r.ok).toBe(false);
|
|
71
|
-
expect(r.issues.some(i => i.code === "MISSING_SECTION")).toBe(true);
|
|
71
|
+
const r = (0, validate_js_1.validateMarkdownAgainstOfs)(md, SPEC, 1);
|
|
72
|
+
(0, vitest_1.expect)(r.ok).toBe(false);
|
|
73
|
+
(0, vitest_1.expect)(r.issues.some(i => i.code === "MISSING_SECTION")).toBe(true);
|
|
72
74
|
});
|
|
73
|
-
it("L2 requires a single fenced markdown block", () => {
|
|
74
|
-
const r = validateMarkdownAgainstOfs(mdL1Good(), SPEC, 2);
|
|
75
|
-
expect(r.ok).toBe(false);
|
|
76
|
-
expect(r.issues.some(i => i.code === "CONTAINER_MISSING")).toBe(true);
|
|
75
|
+
(0, vitest_1.it)("L2 requires a single fenced markdown block", () => {
|
|
76
|
+
const r = (0, validate_js_1.validateMarkdownAgainstOfs)(mdL1Good(), SPEC, 2);
|
|
77
|
+
(0, vitest_1.expect)(r.ok).toBe(false);
|
|
78
|
+
(0, vitest_1.expect)(r.issues.some(i => i.code === "CONTAINER_MISSING")).toBe(true);
|
|
77
79
|
});
|
|
78
|
-
it("L2 passes with one fence containing valid L1", () => {
|
|
80
|
+
(0, vitest_1.it)("L2 passes with one fence containing valid L1", () => {
|
|
79
81
|
const md = ["```markdown", mdL1Good(), "```"].join("\n");
|
|
80
|
-
const r = validateMarkdownAgainstOfs(md, SPEC, 2);
|
|
81
|
-
expect(r.ok).toBe(true);
|
|
82
|
+
const r = (0, validate_js_1.validateMarkdownAgainstOfs)(md, SPEC, 2);
|
|
83
|
+
(0, vitest_1.expect)(r.ok).toBe(true);
|
|
82
84
|
});
|
|
83
|
-
it("L3 enforces section kinds (Reasoning must be ordered list)", () => {
|
|
85
|
+
(0, vitest_1.it)("L3 enforces section kinds (Reasoning must be ordered list)", () => {
|
|
84
86
|
const bad = [
|
|
85
87
|
"```markdown",
|
|
86
88
|
[
|
|
@@ -101,8 +103,8 @@ describe("validateMarkdownAgainstOfs()", () => {
|
|
|
101
103
|
].join("\n"),
|
|
102
104
|
"```",
|
|
103
105
|
].join("\n");
|
|
104
|
-
const r = validateMarkdownAgainstOfs(bad, SPEC, 3);
|
|
105
|
-
expect(r.ok).toBe(false);
|
|
106
|
-
expect(r.issues.some(i => i.code === "WRONG_SECTION_KIND")).toBe(true);
|
|
106
|
+
const r = (0, validate_js_1.validateMarkdownAgainstOfs)(bad, SPEC, 3);
|
|
107
|
+
(0, vitest_1.expect)(r.ok).toBe(false);
|
|
108
|
+
(0, vitest_1.expect)(r.issues.some(i => i.code === "WRONG_SECTION_KIND")).toBe(true);
|
|
107
109
|
});
|
|
108
110
|
});
|
package/dist/cli/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const index_js_1 = require("../index.js");
|
|
5
7
|
const [, , command, ...args] = process.argv;
|
|
6
8
|
async function main() {
|
|
7
9
|
if (!command || command === "--help" || command === "-h") {
|
|
@@ -55,8 +57,8 @@ async function handleCheck(args) {
|
|
|
55
57
|
if (!file)
|
|
56
58
|
throw new Error("Missing file argument.");
|
|
57
59
|
const level = (args.includes("--level") ? args[args.indexOf("--level") + 1] : "L2");
|
|
58
|
-
const content = readFileSync(resolve(file), "utf-8");
|
|
59
|
-
const result = await checkCompliance(content, level);
|
|
60
|
+
const content = (0, fs_1.readFileSync)((0, path_1.resolve)(file), "utf-8");
|
|
61
|
+
const result = await (0, index_js_1.checkCompliance)(content, level);
|
|
60
62
|
if (result.meetsCompliance) {
|
|
61
63
|
console.log("✅ Content meets compliance.");
|
|
62
64
|
}
|
|
@@ -72,10 +74,10 @@ async function handleDiagnose(args) {
|
|
|
72
74
|
const key = args[1];
|
|
73
75
|
if (!file || !key)
|
|
74
76
|
throw new Error("Usage: flex-md diagnose <file> <key>");
|
|
75
|
-
if (!existsSync(file))
|
|
77
|
+
if (!(0, fs_1.existsSync)(file))
|
|
76
78
|
throw new Error(`File not found: ${file}`);
|
|
77
|
-
const content = readFileSync(resolve(file), "utf-8");
|
|
78
|
-
const result = checkConnection(content, key);
|
|
79
|
+
const content = (0, fs_1.readFileSync)((0, path_1.resolve)(file), "utf-8");
|
|
80
|
+
const result = (0, index_js_1.checkConnection)(content, key);
|
|
79
81
|
if (result.connected) {
|
|
80
82
|
console.log(`✅ Key "${key}" found at path: ${result.path?.join(" -> ")}`);
|
|
81
83
|
}
|
|
@@ -89,9 +91,9 @@ async function handleCreate(args) {
|
|
|
89
91
|
const key = args[1];
|
|
90
92
|
if (!file || !key)
|
|
91
93
|
throw new Error("Usage: flex-md create <file> <key>");
|
|
92
|
-
const content = existsSync(file) ? readFileSync(file, "utf-8") : "";
|
|
94
|
+
const content = (0, fs_1.existsSync)(file) ? (0, fs_1.readFileSync)(file, "utf-8") : "";
|
|
93
95
|
const newContent = content + `\n\n## ${key}\n\n[Content for ${key}]\n`;
|
|
94
|
-
writeFileSync(file, newContent);
|
|
96
|
+
(0, fs_1.writeFileSync)(file, newContent);
|
|
95
97
|
console.log(`✅ Section "${key}" created in ${file}`);
|
|
96
98
|
}
|
|
97
99
|
async function handleSync(args) {
|
|
@@ -99,10 +101,10 @@ async function handleSync(args) {
|
|
|
99
101
|
const target = args[1];
|
|
100
102
|
if (!source || !target)
|
|
101
103
|
throw new Error("Usage: flex-md sync <source> <target>");
|
|
102
|
-
if (!existsSync(source))
|
|
104
|
+
if (!(0, fs_1.existsSync)(source))
|
|
103
105
|
throw new Error(`Source file not found: ${source}`);
|
|
104
|
-
const content = readFileSync(source, "utf-8");
|
|
105
|
-
writeFileSync(target, content);
|
|
106
|
+
const content = (0, fs_1.readFileSync)(source, "utf-8");
|
|
107
|
+
(0, fs_1.writeFileSync)(target, content);
|
|
106
108
|
console.log(`✅ Content synced from ${source} to ${target}`);
|
|
107
109
|
}
|
|
108
110
|
main();
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectJsonIntent = detectJsonIntent;
|
|
1
4
|
const HARD_PATTERNS = [
|
|
2
5
|
/\breturn\s+only\s+json\b/i,
|
|
3
6
|
/\boutput\s+only\s+json\b/i,
|
|
@@ -25,7 +28,7 @@ const TOOLING_PATTERNS = [
|
|
|
25
28
|
/\btool\s+call\b/i,
|
|
26
29
|
/\barguments\b.*\bjson\b/i,
|
|
27
30
|
];
|
|
28
|
-
|
|
31
|
+
function detectJsonIntent(text) {
|
|
29
32
|
const signals = [];
|
|
30
33
|
const add = (rx, strength, idx, len) => {
|
|
31
34
|
signals.push({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Helpers for "framed vs frameless" Markdown handling before Flex-MD.
|
|
3
4
|
*
|
|
@@ -8,24 +9,29 @@
|
|
|
8
9
|
* Notes:
|
|
9
10
|
* - This is heuristic by design; tune thresholds as you learn your data.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.detectMarkdown = detectMarkdown;
|
|
14
|
+
exports.stripSingleFence = stripSingleFence;
|
|
15
|
+
exports.forceWrapAsMarkdown = forceWrapAsMarkdown;
|
|
16
|
+
exports.normalizeForFlexMd = normalizeForFlexMd;
|
|
17
|
+
const logger_js_1 = require("../../logger.js");
|
|
12
18
|
const SINGLE_FENCE_BLOCK_RE = /^```([a-zA-Z0-9_-]+)?([^\n]*)\n([\s\S]*?)\n```$/;
|
|
13
19
|
const FENCE_OPEN_RE = /(^|\n)```/g;
|
|
14
|
-
|
|
15
|
-
logger.debug("Starting markdown detection", {
|
|
20
|
+
function detectMarkdown(text) {
|
|
21
|
+
logger_js_1.logger.debug("Starting markdown detection", {
|
|
16
22
|
inputType: typeof text,
|
|
17
23
|
inputLength: typeof text === "string" ? text.length : JSON.stringify(text ?? "").length
|
|
18
24
|
});
|
|
19
25
|
const reasons = [];
|
|
20
26
|
const raw = typeof text === "string" ? text : JSON.stringify(text ?? "");
|
|
21
27
|
const s = raw.replace(/\r\n/g, "\n");
|
|
22
|
-
logger.verbose("Input normalized for processing", {
|
|
28
|
+
logger_js_1.logger.verbose("Input normalized for processing", {
|
|
23
29
|
originalType: typeof text,
|
|
24
30
|
normalizedLength: s.length,
|
|
25
31
|
hasNewlines: s.includes('\n')
|
|
26
32
|
});
|
|
27
33
|
const codeFences = [...s.matchAll(FENCE_OPEN_RE)].length;
|
|
28
|
-
logger.debug("Code fence analysis", {
|
|
34
|
+
logger_js_1.logger.debug("Code fence analysis", {
|
|
29
35
|
totalFences: codeFences,
|
|
30
36
|
fenceRegex: FENCE_OPEN_RE.source
|
|
31
37
|
});
|
|
@@ -33,7 +39,7 @@ export function detectMarkdown(text) {
|
|
|
33
39
|
const m = s.match(SINGLE_FENCE_BLOCK_RE);
|
|
34
40
|
const isFramed = !!m && codeFences === 2;
|
|
35
41
|
const frameLanguage = isFramed ? (m?.[1] ?? null) : null;
|
|
36
|
-
logger.debug("Framed markdown detection", {
|
|
42
|
+
logger_js_1.logger.debug("Framed markdown detection", {
|
|
37
43
|
regexMatch: !!m,
|
|
38
44
|
expectedFences: 2,
|
|
39
45
|
actualFences: codeFences,
|
|
@@ -47,24 +53,24 @@ export function detectMarkdown(text) {
|
|
|
47
53
|
});
|
|
48
54
|
if (isFramed) {
|
|
49
55
|
reasons.push("Single fenced code block detected (framed payload).");
|
|
50
|
-
logger.info("Detected framed markdown", { language: frameLanguage });
|
|
56
|
+
logger_js_1.logger.info("Detected framed markdown", { language: frameLanguage });
|
|
51
57
|
}
|
|
52
58
|
else if (codeFences > 2) {
|
|
53
59
|
reasons.push("Multiple fenced code blocks detected.");
|
|
54
|
-
logger.debug("Multiple fences detected, not treating as single framed block", { fenceCount: codeFences });
|
|
60
|
+
logger_js_1.logger.debug("Multiple fences detected, not treating as single framed block", { fenceCount: codeFences });
|
|
55
61
|
}
|
|
56
62
|
else if (codeFences === 1) {
|
|
57
63
|
reasons.push("Single fence marker found but not properly framed.");
|
|
58
|
-
logger.debug("Single fence found but regex didn't match", {
|
|
64
|
+
logger_js_1.logger.debug("Single fence found but regex didn't match", {
|
|
59
65
|
regex: SINGLE_FENCE_BLOCK_RE.source,
|
|
60
66
|
hasMatch: !!m
|
|
61
67
|
});
|
|
62
68
|
}
|
|
63
69
|
else {
|
|
64
|
-
logger.debug("No fence markers detected");
|
|
70
|
+
logger_js_1.logger.debug("No fence markers detected");
|
|
65
71
|
}
|
|
66
72
|
const lines = s.split("\n");
|
|
67
|
-
logger.verbose("Line-by-line analysis started", { totalLines: lines.length });
|
|
73
|
+
logger_js_1.logger.verbose("Line-by-line analysis started", { totalLines: lines.length });
|
|
68
74
|
const atxHeadings = lines.filter((l) => /^#{1,6}\s+\S/.test(l.trim())).length;
|
|
69
75
|
let setextHeadings = 0;
|
|
70
76
|
for (let i = 0; i < lines.length - 1; i++) {
|
|
@@ -89,7 +95,7 @@ export function detectMarkdown(text) {
|
|
|
89
95
|
(s.match(/__[^_\n]+__/g) ?? []).length +
|
|
90
96
|
(s.match(/(^|[^*])\*[^*\n]+\*([^*]|$)/g) ?? []).length +
|
|
91
97
|
(s.match(/(^|[^_])_[^_\n]+_([^_]|$)/g) ?? []).length;
|
|
92
|
-
logger.debug("Markdown structure analysis", {
|
|
98
|
+
logger_js_1.logger.debug("Markdown structure analysis", {
|
|
93
99
|
atxHeadings,
|
|
94
100
|
setextHeadings,
|
|
95
101
|
totalHeadings: headings,
|
|
@@ -104,7 +110,7 @@ export function detectMarkdown(text) {
|
|
|
104
110
|
const hasList = unorderedListLines + orderedListLines >= 2;
|
|
105
111
|
const hasTable = tableRows >= 2;
|
|
106
112
|
const hasOtherSignals = inlineCodeSpans + mdLinks + emphasisTokens >= 2;
|
|
107
|
-
logger.debug("Heuristic analysis", {
|
|
113
|
+
logger_js_1.logger.debug("Heuristic analysis", {
|
|
108
114
|
hasList,
|
|
109
115
|
hasTable,
|
|
110
116
|
hasOtherSignals,
|
|
@@ -115,17 +121,17 @@ export function detectMarkdown(text) {
|
|
|
115
121
|
let isMarkdownLikely = false;
|
|
116
122
|
if (isFramed) {
|
|
117
123
|
isMarkdownLikely = true;
|
|
118
|
-
logger.debug("Markdown likelihood determined: framed content is always considered markdown");
|
|
124
|
+
logger_js_1.logger.debug("Markdown likelihood determined: framed content is always considered markdown");
|
|
119
125
|
}
|
|
120
126
|
else if (headings >= 2) {
|
|
121
127
|
isMarkdownLikely = true;
|
|
122
128
|
reasons.push(`Detected ${headings} markdown heading(s) (>=2).`);
|
|
123
|
-
logger.debug("Markdown likelihood: sufficient headings detected", { headingCount: headings });
|
|
129
|
+
logger_js_1.logger.debug("Markdown likelihood: sufficient headings detected", { headingCount: headings });
|
|
124
130
|
}
|
|
125
131
|
else if (headings >= 1 && (hasList || hasTable)) {
|
|
126
132
|
isMarkdownLikely = true;
|
|
127
133
|
reasons.push(`Detected heading(s) plus ${hasList ? "list" : "table"} structure.`);
|
|
128
|
-
logger.debug("Markdown likelihood: heading plus structural element", {
|
|
134
|
+
logger_js_1.logger.debug("Markdown likelihood: heading plus structural element", {
|
|
129
135
|
hasHeading: headings >= 1,
|
|
130
136
|
hasList,
|
|
131
137
|
hasTable
|
|
@@ -134,7 +140,7 @@ export function detectMarkdown(text) {
|
|
|
134
140
|
else if ((hasList && hasTable) || (hasTable && hasOtherSignals) || (hasList && hasOtherSignals)) {
|
|
135
141
|
isMarkdownLikely = true;
|
|
136
142
|
reasons.push("Detected multiple markdown structural signals (lists/tables/links/code/emphasis).");
|
|
137
|
-
logger.debug("Markdown likelihood: multiple structural signals", {
|
|
143
|
+
logger_js_1.logger.debug("Markdown likelihood: multiple structural signals", {
|
|
138
144
|
listAndTable: hasList && hasTable,
|
|
139
145
|
tableAndOther: hasTable && hasOtherSignals,
|
|
140
146
|
listAndOther: hasList && hasOtherSignals
|
|
@@ -142,7 +148,7 @@ export function detectMarkdown(text) {
|
|
|
142
148
|
}
|
|
143
149
|
else {
|
|
144
150
|
reasons.push("Insufficient markdown structure signals; treating as plain text.");
|
|
145
|
-
logger.debug("Markdown likelihood: insufficient signals, treating as plain text", {
|
|
151
|
+
logger_js_1.logger.debug("Markdown likelihood: insufficient signals, treating as plain text", {
|
|
146
152
|
headings,
|
|
147
153
|
hasList,
|
|
148
154
|
hasTable,
|
|
@@ -152,7 +158,7 @@ export function detectMarkdown(text) {
|
|
|
152
158
|
if (/^\s*#{1,6}\s+\S/.test(lines[0] ?? "")) {
|
|
153
159
|
reasons.push("Text starts with an ATX heading (#...).");
|
|
154
160
|
isMarkdownLikely = true;
|
|
155
|
-
logger.debug("Additional check: starts with heading, upgrading to markdown");
|
|
161
|
+
logger_js_1.logger.debug("Additional check: starts with heading, upgrading to markdown");
|
|
156
162
|
}
|
|
157
163
|
return {
|
|
158
164
|
isMarkdownLikely,
|
|
@@ -179,7 +185,7 @@ export function detectMarkdown(text) {
|
|
|
179
185
|
*
|
|
180
186
|
* - Also handles ```md / ```markdown / ```json etc.
|
|
181
187
|
*/
|
|
182
|
-
|
|
188
|
+
function stripSingleFence(input) {
|
|
183
189
|
const s = input.replace(/\r\n/g, "\n").trim();
|
|
184
190
|
const fenceCount = [...s.matchAll(FENCE_OPEN_RE)].length;
|
|
185
191
|
if (fenceCount !== 2) {
|
|
@@ -199,7 +205,7 @@ export function stripSingleFence(input) {
|
|
|
199
205
|
* Choose a heading that exists in your OFS to maximize alignment.
|
|
200
206
|
* Default: "Full Answer" (common sink section).
|
|
201
207
|
*/
|
|
202
|
-
|
|
208
|
+
function forceWrapAsMarkdown(plainText, opts) {
|
|
203
209
|
const heading = opts?.heading ?? "Full Answer";
|
|
204
210
|
const level = opts?.level ?? 3;
|
|
205
211
|
const hashes = "#".repeat(level);
|
|
@@ -215,15 +221,15 @@ export function forceWrapAsMarkdown(plainText, opts) {
|
|
|
215
221
|
* - Else: if markdown-likely: keep as-is
|
|
216
222
|
* - Else: wrap as markdown under a chosen heading
|
|
217
223
|
*/
|
|
218
|
-
|
|
219
|
-
logger.info("Starting Flex-MD normalization", {
|
|
224
|
+
function normalizeForFlexMd(input, opts) {
|
|
225
|
+
logger_js_1.logger.info("Starting Flex-MD normalization", {
|
|
220
226
|
inputType: typeof input,
|
|
221
227
|
inputLength: typeof input === "string" ? input.length : JSON.stringify(input ?? "").length,
|
|
222
228
|
options: opts
|
|
223
229
|
});
|
|
224
230
|
const raw = typeof input === "string" ? input : JSON.stringify(input ?? "");
|
|
225
231
|
const detection = detectMarkdown(raw);
|
|
226
|
-
logger.debug("Detection completed", {
|
|
232
|
+
logger_js_1.logger.debug("Detection completed", {
|
|
227
233
|
isFramed: detection.isFramed,
|
|
228
234
|
isMarkdownLikely: detection.isMarkdownLikely,
|
|
229
235
|
frameLanguage: detection.frameLanguage,
|
|
@@ -231,14 +237,14 @@ export function normalizeForFlexMd(input, opts) {
|
|
|
231
237
|
});
|
|
232
238
|
// 1) Strip if framed
|
|
233
239
|
const { stripped, wasFramed, language } = stripSingleFence(raw);
|
|
234
|
-
logger.debug("Strip fence check", {
|
|
240
|
+
logger_js_1.logger.debug("Strip fence check", {
|
|
235
241
|
wasFramed,
|
|
236
242
|
language,
|
|
237
243
|
strippedLength: stripped.length,
|
|
238
244
|
originalLength: raw.length
|
|
239
245
|
});
|
|
240
246
|
if (wasFramed) {
|
|
241
|
-
logger.info("Normalization: stripped framed markdown", {
|
|
247
|
+
logger_js_1.logger.info("Normalization: stripped framed markdown", {
|
|
242
248
|
language,
|
|
243
249
|
contentLength: stripped.length
|
|
244
250
|
});
|
|
@@ -252,7 +258,7 @@ export function normalizeForFlexMd(input, opts) {
|
|
|
252
258
|
}
|
|
253
259
|
// 2) Keep if markdown-likely
|
|
254
260
|
if (detection.isMarkdownLikely) {
|
|
255
|
-
logger.info("Normalization: keeping as-is (markdown-likely)", {
|
|
261
|
+
logger_js_1.logger.info("Normalization: keeping as-is (markdown-likely)", {
|
|
256
262
|
reasons: detection.reasons
|
|
257
263
|
});
|
|
258
264
|
return {
|
|
@@ -266,7 +272,7 @@ export function normalizeForFlexMd(input, opts) {
|
|
|
266
272
|
// 3) Wrap if not markdown-likely
|
|
267
273
|
const fallbackHeading = opts?.fallbackHeading ?? "Full Answer";
|
|
268
274
|
const fallbackLevel = opts?.fallbackHeadingLevel ?? 3;
|
|
269
|
-
logger.info("Normalization: wrapping as markdown", {
|
|
275
|
+
logger_js_1.logger.info("Normalization: wrapping as markdown", {
|
|
270
276
|
fallbackHeading,
|
|
271
277
|
fallbackLevel,
|
|
272
278
|
reasons: detection.reasons
|
|
@@ -275,7 +281,7 @@ export function normalizeForFlexMd(input, opts) {
|
|
|
275
281
|
heading: fallbackHeading,
|
|
276
282
|
level: fallbackLevel,
|
|
277
283
|
});
|
|
278
|
-
logger.debug("Wrapping completed", {
|
|
284
|
+
logger_js_1.logger.debug("Wrapping completed", {
|
|
279
285
|
wrappedLength: wrapped.length,
|
|
280
286
|
originalLength: raw.length
|
|
281
287
|
});
|