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.
- package/README.md +423 -39
- package/dist/index.cjs +62 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/md/parse.d.ts +1 -0
- package/dist/md/parse.js +12 -0
- package/dist/ofs/parser.js +31 -10
- package/dist/tokens/auto-fix.d.ts +10 -0
- package/dist/tokens/auto-fix.js +56 -0
- package/dist/tokens/cognitive-cost.d.ts +10 -0
- package/dist/tokens/cognitive-cost.js +205 -0
- package/dist/tokens/compliance.d.ts +10 -0
- package/dist/tokens/compliance.js +70 -0
- package/dist/tokens/confidence.d.ts +6 -0
- package/dist/tokens/confidence.js +332 -0
- package/dist/tokens/estimator.d.ts +12 -0
- package/dist/tokens/estimator.js +138 -0
- package/dist/tokens/improvements.d.ts +10 -0
- package/dist/tokens/improvements.js +697 -0
- package/dist/tokens/index.d.ts +24 -0
- package/dist/tokens/index.js +31 -0
- package/dist/tokens/parser.d.ts +3 -0
- package/dist/tokens/parser.js +97 -0
- package/dist/tokens/patterns.d.ts +9 -0
- package/dist/tokens/patterns.js +20 -0
- package/dist/tokens/smart-report.d.ts +10 -0
- package/dist/tokens/smart-report.js +187 -0
- package/dist/tokens/spec-estimator.d.ts +7 -0
- package/dist/tokens/spec-estimator.js +68 -0
- package/dist/tokens/types.d.ts +185 -0
- package/dist/tokens/validator.d.ts +16 -0
- package/dist/tokens/validator.js +59 -0
- package/docs/Recommended New Strategies for AI Request Builder.md +691 -0
- package/package.json +7 -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.d.ts +0 -1
- package/dist/test-runner.js +0 -331
- package/dist/test-strictness.d.ts +0 -1
- package/dist/test-strictness.js +0 -213
- package/dist/util.d.ts +0 -5
- package/dist/util.js +0 -64
- package/dist/validate/policy.d.ts +0 -10
- package/dist/validate/policy.js +0 -17
- package/dist/validator.d.ts +0 -2
- package/dist/validator.js +0 -80
- /package/dist/{test-pipeline.d.ts → tokens/types.js} +0 -0
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detect FlexMD and other structured objects in arbitrary text.
|
|
3
|
-
* Returns all detected objects with confidence scores and byte ranges.
|
|
4
|
-
*/
|
|
5
|
-
export function detectObjects(text) {
|
|
6
|
-
const detected = [];
|
|
7
|
-
// Tier A: Detect ```flexmd fenced blocks (highest confidence)
|
|
8
|
-
detected.push(...detectFlexMdFences(text));
|
|
9
|
-
// Tier B: Detect ```json blocks with FlexDocument shape
|
|
10
|
-
detected.push(...detectFlexDocJsonFences(text));
|
|
11
|
-
// Tier C: Detect raw FlexMD markers (best effort)
|
|
12
|
-
detected.push(...detectRawFlexMd(text));
|
|
13
|
-
// Sort by start position
|
|
14
|
-
detected.sort((a, b) => a.start - b.start);
|
|
15
|
-
return detected;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Tier A: Detect ```flexmd fenced blocks.
|
|
19
|
-
*/
|
|
20
|
-
function detectFlexMdFences(text) {
|
|
21
|
-
const detected = [];
|
|
22
|
-
const regex = /```flexmd\n([\s\S]*?)```/g;
|
|
23
|
-
let match;
|
|
24
|
-
while ((match = regex.exec(text)) !== null) {
|
|
25
|
-
detected.push({
|
|
26
|
-
kind: "flexmd_fence",
|
|
27
|
-
confidence: 1.0,
|
|
28
|
-
start: match.index,
|
|
29
|
-
end: match.index + match[0].length,
|
|
30
|
-
raw: match[0],
|
|
31
|
-
inner: match[1]
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
return detected;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Tier B: Detect ```json blocks that match FlexDocument shape.
|
|
38
|
-
*/
|
|
39
|
-
function detectFlexDocJsonFences(text) {
|
|
40
|
-
const detected = [];
|
|
41
|
-
const regex = /```json\n([\s\S]*?)```/g;
|
|
42
|
-
let match;
|
|
43
|
-
while ((match = regex.exec(text)) !== null) {
|
|
44
|
-
const inner = match[1];
|
|
45
|
-
// Try to parse as JSON
|
|
46
|
-
try {
|
|
47
|
-
const parsed = JSON.parse(inner);
|
|
48
|
-
// Check if it has the FlexDocument shape (has "frames" array)
|
|
49
|
-
if (parsed && typeof parsed === "object" && Array.isArray(parsed.frames)) {
|
|
50
|
-
detected.push({
|
|
51
|
-
kind: "flexdoc_json_fence",
|
|
52
|
-
confidence: 0.9,
|
|
53
|
-
start: match.index,
|
|
54
|
-
end: match.index + match[0].length,
|
|
55
|
-
raw: match[0],
|
|
56
|
-
inner
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
// Not valid JSON, skip
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return detected;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Tier C: Detect raw FlexMD markers (best effort).
|
|
68
|
-
* Looks for at least 2 strong markers within first 500 chars:
|
|
69
|
-
* - [[...]]
|
|
70
|
-
* - @key:
|
|
71
|
-
* - @payload:name:
|
|
72
|
-
*/
|
|
73
|
-
function detectRawFlexMd(text) {
|
|
74
|
-
const detected = [];
|
|
75
|
-
// Look for frame markers
|
|
76
|
-
const frameRegex = /\[\[([^\]]+)\]\]/g;
|
|
77
|
-
const metaRegex = /@[a-zA-Z_][a-zA-Z0-9_]*:/g;
|
|
78
|
-
const payloadRegex = /@payload:[a-zA-Z_][a-zA-Z0-9_]*:/g;
|
|
79
|
-
let match;
|
|
80
|
-
const markers = [];
|
|
81
|
-
// Collect all marker positions
|
|
82
|
-
while ((match = frameRegex.exec(text)) !== null) {
|
|
83
|
-
markers.push(match.index);
|
|
84
|
-
}
|
|
85
|
-
while ((match = metaRegex.exec(text)) !== null) {
|
|
86
|
-
markers.push(match.index);
|
|
87
|
-
}
|
|
88
|
-
while ((match = payloadRegex.exec(text)) !== null) {
|
|
89
|
-
markers.push(match.index);
|
|
90
|
-
}
|
|
91
|
-
// If we have at least 2 markers, consider it raw FlexMD
|
|
92
|
-
if (markers.length >= 2) {
|
|
93
|
-
const start = Math.min(...markers);
|
|
94
|
-
const end = text.length;
|
|
95
|
-
detected.push({
|
|
96
|
-
kind: "raw_flexmd",
|
|
97
|
-
confidence: 0.7,
|
|
98
|
-
start,
|
|
99
|
-
end,
|
|
100
|
-
raw: text.substring(start, end)
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
return detected;
|
|
104
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { FlexDocument } from "../types.js";
|
|
2
|
-
export interface ParseAnyResult {
|
|
3
|
-
flexDocs: FlexDocument[];
|
|
4
|
-
markdownSnippets: string[];
|
|
5
|
-
remainder: string;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Parse any text and extract all FlexMD documents and Markdown snippets.
|
|
9
|
-
*/
|
|
10
|
-
export declare function parseAny(text: string): ParseAnyResult;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { detectObjects } from "./detector.js";
|
|
2
|
-
import { parseFlexMd } from "../parser.js";
|
|
3
|
-
/**
|
|
4
|
-
* Parse any text and extract all FlexMD documents and Markdown snippets.
|
|
5
|
-
*/
|
|
6
|
-
export function parseAny(text) {
|
|
7
|
-
const detected = detectObjects(text);
|
|
8
|
-
const flexDocs = [];
|
|
9
|
-
const markdownSnippets = [];
|
|
10
|
-
for (const obj of detected) {
|
|
11
|
-
if (obj.kind === "flexmd_fence" && obj.inner) {
|
|
12
|
-
// Parse fenced FlexMD
|
|
13
|
-
try {
|
|
14
|
-
const doc = parseFlexMd(obj.inner);
|
|
15
|
-
flexDocs.push(doc);
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
// Parse failed, treat as markdown snippet
|
|
19
|
-
markdownSnippets.push(obj.inner);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
else if (obj.kind === "flexdoc_json_fence" && obj.inner) {
|
|
23
|
-
// Parse JSON FlexDocument
|
|
24
|
-
try {
|
|
25
|
-
const doc = JSON.parse(obj.inner);
|
|
26
|
-
flexDocs.push(doc);
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
// Parse failed, skip
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
else if (obj.kind === "raw_flexmd") {
|
|
33
|
-
// Parse raw FlexMD
|
|
34
|
-
try {
|
|
35
|
-
const doc = parseFlexMd(obj.raw);
|
|
36
|
-
flexDocs.push(doc);
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
// Parse failed, treat as markdown snippet
|
|
40
|
-
markdownSnippets.push(obj.raw);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
// Calculate remainder (text not covered by detected objects)
|
|
45
|
-
let remainder = text;
|
|
46
|
-
for (const obj of detected) {
|
|
47
|
-
remainder = remainder.replace(obj.raw, "");
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
flexDocs,
|
|
51
|
-
markdownSnippets,
|
|
52
|
-
remainder: remainder.trim()
|
|
53
|
-
};
|
|
54
|
-
}
|
package/dist/issues/build.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { StrictnessOptions } from "../strictness/types.js";
|
|
2
|
-
export type IssuesStatusCode = "missing_input" | "unclear_instructions" | "invalid_format" | "unsupported" | (string & {});
|
|
3
|
-
export interface IssueItem {
|
|
4
|
-
issue: string;
|
|
5
|
-
path?: string;
|
|
6
|
-
expected?: string;
|
|
7
|
-
got?: string;
|
|
8
|
-
hint?: string;
|
|
9
|
-
[k: string]: string | undefined;
|
|
10
|
-
}
|
|
11
|
-
export interface BuildIssuesEnvelopeInput {
|
|
12
|
-
status: "error" | "partial";
|
|
13
|
-
code: IssuesStatusCode;
|
|
14
|
-
message: string;
|
|
15
|
-
issues: IssueItem[];
|
|
16
|
-
missingInputs?: string[];
|
|
17
|
-
clarificationsNeeded?: string[];
|
|
18
|
-
/** If provided, wraps output in a single container fence when strictness requires it. */
|
|
19
|
-
strictness?: StrictnessOptions;
|
|
20
|
-
/** Override fence language: "markdown" | "flexmd" (defaults from strictness.container) */
|
|
21
|
-
fence?: "markdown" | "flexmd";
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Build a Markdown-only issues envelope.
|
|
25
|
-
*/
|
|
26
|
-
export declare function buildIssuesEnvelope(input: BuildIssuesEnvelopeInput): string;
|
package/dist/issues/build.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { strictnessDefaults } from "../strictness/types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Build a Markdown-only issues envelope.
|
|
4
|
-
*/
|
|
5
|
-
export function buildIssuesEnvelope(input) {
|
|
6
|
-
const strict = input.strictness ? { ...strictnessDefaults(input.strictness.level), ...input.strictness } : undefined;
|
|
7
|
-
const fence = input.fence ?? resolveFence(strict);
|
|
8
|
-
const lines = [];
|
|
9
|
-
lines.push(`## Status`);
|
|
10
|
-
lines.push(`- status: ${input.status}`);
|
|
11
|
-
lines.push(`- code: ${input.code}`);
|
|
12
|
-
lines.push(`- message: ${input.message}`);
|
|
13
|
-
lines.push(``);
|
|
14
|
-
if (input.missingInputs && input.missingInputs.length) {
|
|
15
|
-
lines.push(`## Missing inputs`);
|
|
16
|
-
for (const mi of input.missingInputs)
|
|
17
|
-
lines.push(`- ${mi}`);
|
|
18
|
-
lines.push(``);
|
|
19
|
-
}
|
|
20
|
-
if (input.clarificationsNeeded && input.clarificationsNeeded.length) {
|
|
21
|
-
lines.push(`## Clarifications needed`);
|
|
22
|
-
for (const q of input.clarificationsNeeded)
|
|
23
|
-
lines.push(`- ${q}`);
|
|
24
|
-
lines.push(``);
|
|
25
|
-
}
|
|
26
|
-
lines.push(`## Issues`);
|
|
27
|
-
if (!input.issues.length) {
|
|
28
|
-
lines.push(`- issue: none`);
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
for (const it of input.issues) {
|
|
32
|
-
lines.push(`- issue: ${it.issue}`);
|
|
33
|
-
emitField(lines, "path", it.path);
|
|
34
|
-
emitField(lines, "expected", it.expected);
|
|
35
|
-
emitField(lines, "got", it.got);
|
|
36
|
-
emitField(lines, "hint", it.hint);
|
|
37
|
-
for (const [k, v] of Object.entries(it)) {
|
|
38
|
-
if (k === "issue" || k === "path" || k === "expected" || k === "got" || k === "hint")
|
|
39
|
-
continue;
|
|
40
|
-
emitField(lines, k, v);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
let md = lines.join("\n").trimEnd() + "\n";
|
|
45
|
-
if (strict && strict.level >= 2) {
|
|
46
|
-
const lang = (fence === "flexmd") ? "flexmd" : "markdown";
|
|
47
|
-
md = `\`\`\`${lang}\n${md}\`\`\`\n`;
|
|
48
|
-
}
|
|
49
|
-
return md;
|
|
50
|
-
}
|
|
51
|
-
function emitField(lines, key, value) {
|
|
52
|
-
if (value == null || value === "")
|
|
53
|
-
return;
|
|
54
|
-
lines.push(` ${key}: ${value}`);
|
|
55
|
-
}
|
|
56
|
-
function resolveFence(strict) {
|
|
57
|
-
if (!strict)
|
|
58
|
-
return "markdown";
|
|
59
|
-
if (strict.container === "flexmd_fence")
|
|
60
|
-
return "flexmd";
|
|
61
|
-
return "markdown";
|
|
62
|
-
}
|
package/dist/md/lists.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface ListItem {
|
|
2
|
-
text: string;
|
|
3
|
-
index?: number;
|
|
4
|
-
children: ListItem[];
|
|
5
|
-
}
|
|
6
|
-
export interface ParsedList {
|
|
7
|
-
kind: "list";
|
|
8
|
-
ordered: boolean;
|
|
9
|
-
items: ListItem[];
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Parses a flat list segment from Markdown into a nested structure.
|
|
13
|
-
*/
|
|
14
|
-
export declare function parseNestedList(sectionMd: string): ParsedList | null;
|
package/dist/md/lists.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parses a flat list segment from Markdown into a nested structure.
|
|
3
|
-
*/
|
|
4
|
-
export function parseNestedList(sectionMd) {
|
|
5
|
-
const lines = sectionMd.split("\n");
|
|
6
|
-
const listLines = lines
|
|
7
|
-
.map(l => ({ raw: l, indent: (l.match(/^\s*/)?.[0].length ?? 0) }))
|
|
8
|
-
.filter(x => /^\s*(-\s+|\d+\.\s+)/.test(x.raw));
|
|
9
|
-
if (!listLines.length)
|
|
10
|
-
return null;
|
|
11
|
-
const ordered = listLines.some(x => /^\s*\d+\.\s+/.test(x.raw));
|
|
12
|
-
const root = [];
|
|
13
|
-
const stack = [];
|
|
14
|
-
for (const { raw, indent } of listLines) {
|
|
15
|
-
const mO = raw.match(/^\s*(\d+)\.\s+(.*)$/);
|
|
16
|
-
const mU = raw.match(/^\s*-\s+(.*)$/);
|
|
17
|
-
const text = (mO?.[2] ?? mU?.[1] ?? "").trim();
|
|
18
|
-
const item = { text, children: [] };
|
|
19
|
-
if (mO)
|
|
20
|
-
item.index = Number(mO[1]);
|
|
21
|
-
while (stack.length && stack[stack.length - 1].indent >= indent) {
|
|
22
|
-
stack.pop();
|
|
23
|
-
}
|
|
24
|
-
if (!stack.length) {
|
|
25
|
-
root.push(item);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
stack[stack.length - 1].item.children.push(item);
|
|
29
|
-
}
|
|
30
|
-
stack.push({ indent, item });
|
|
31
|
-
}
|
|
32
|
-
return { kind: "list", ordered, items: root };
|
|
33
|
-
}
|
package/dist/md/tables.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export interface ParsedTable {
|
|
2
|
-
kind: "table" | "ordered_table";
|
|
3
|
-
by?: string;
|
|
4
|
-
columns: string[];
|
|
5
|
-
rows: string[][];
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Extracts all pipe table blocks from a markdown string.
|
|
9
|
-
*/
|
|
10
|
-
export declare function extractPipeTables(md: string): string[];
|
|
11
|
-
/**
|
|
12
|
-
* Parses a single pipe table block into columns and rows.
|
|
13
|
-
*/
|
|
14
|
-
export declare function parsePipeTable(block: string): {
|
|
15
|
-
columns: string[];
|
|
16
|
-
rows: string[][];
|
|
17
|
-
} | null;
|
|
18
|
-
export declare function normalizeHeader(s: string): string;
|
|
19
|
-
/**
|
|
20
|
-
* Enforces ordered table constraints (must have '#' column numbered 1..N).
|
|
21
|
-
*/
|
|
22
|
-
export declare function enforceOrderedTable(table: {
|
|
23
|
-
columns: string[];
|
|
24
|
-
rows: string[][];
|
|
25
|
-
}): string[];
|
package/dist/md/tables.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extracts all pipe table blocks from a markdown string.
|
|
3
|
-
*/
|
|
4
|
-
export function extractPipeTables(md) {
|
|
5
|
-
const lines = md.split("\n");
|
|
6
|
-
const blocks = [];
|
|
7
|
-
let i = 0;
|
|
8
|
-
while (i < lines.length) {
|
|
9
|
-
const h = lines[i] ?? "";
|
|
10
|
-
const s = lines[i + 1] ?? "";
|
|
11
|
-
const looksHeader = h.includes("|");
|
|
12
|
-
const looksSep = /^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(s);
|
|
13
|
-
if (looksHeader && looksSep) {
|
|
14
|
-
const start = i;
|
|
15
|
-
i += 2;
|
|
16
|
-
while (i < lines.length && (lines[i] ?? "").includes("|") && (lines[i] ?? "").trim() !== "") {
|
|
17
|
-
i++;
|
|
18
|
-
}
|
|
19
|
-
const block = lines.slice(start, i).join("\n");
|
|
20
|
-
blocks.push(block);
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
i++;
|
|
24
|
-
}
|
|
25
|
-
return blocks;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Parses a single pipe table block into columns and rows.
|
|
29
|
-
*/
|
|
30
|
-
export function parsePipeTable(block) {
|
|
31
|
-
const lines = block.split("\n").map(l => l.trim()).filter(Boolean);
|
|
32
|
-
if (lines.length < 2)
|
|
33
|
-
return null;
|
|
34
|
-
const parseRow = (row) => {
|
|
35
|
-
let content = row;
|
|
36
|
-
if (content.startsWith("|"))
|
|
37
|
-
content = content.slice(1);
|
|
38
|
-
if (content.endsWith("|"))
|
|
39
|
-
content = content.slice(0, -1);
|
|
40
|
-
return content.split("|").map(c => c.trim());
|
|
41
|
-
};
|
|
42
|
-
const columns = parseRow(lines[0]);
|
|
43
|
-
const rows = lines.slice(2).map(parseRow);
|
|
44
|
-
for (const r of rows) {
|
|
45
|
-
while (r.length < columns.length)
|
|
46
|
-
r.push("");
|
|
47
|
-
}
|
|
48
|
-
return { columns, rows };
|
|
49
|
-
}
|
|
50
|
-
export function normalizeHeader(s) {
|
|
51
|
-
return s.trim().toLowerCase();
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Enforces ordered table constraints (must have '#' column numbered 1..N).
|
|
55
|
-
*/
|
|
56
|
-
export function enforceOrderedTable(table) {
|
|
57
|
-
const issues = [];
|
|
58
|
-
if (!table.columns.length || normalizeHeader(table.columns[0]) !== "#") {
|
|
59
|
-
issues.push("ordered_table_missing_hash_column");
|
|
60
|
-
return issues;
|
|
61
|
-
}
|
|
62
|
-
// enforce 1..N
|
|
63
|
-
for (let i = 0; i < table.rows.length; i++) {
|
|
64
|
-
const cell = (table.rows[i]?.[0] ?? "").trim();
|
|
65
|
-
const n = Number(cell);
|
|
66
|
-
if (!Number.isInteger(n) || n !== i + 1) {
|
|
67
|
-
issues.push("ordered_table_bad_index_sequence");
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return issues;
|
|
72
|
-
}
|
package/dist/ofs/extractor.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { OutputFormatSpec, ExtractedResult } from "../types.js";
|
|
2
|
-
export interface ExtractOptions {
|
|
3
|
-
/** Parse lists even for prose sections */
|
|
4
|
-
parseAllLists?: boolean;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Extract structured data from Markdown based on an OutputFormatSpec.
|
|
8
|
-
*/
|
|
9
|
-
export declare function extractOutput(md: string, spec: OutputFormatSpec, opts?: ExtractOptions): ExtractedResult;
|
package/dist/ofs/extractor.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { buildOutline } from "../outline/builder.js";
|
|
2
|
-
import { parseList } from "../parsers/lists.js";
|
|
3
|
-
import { extractAllTables } from "../parsers/tables.js";
|
|
4
|
-
/**
|
|
5
|
-
* Extract structured data from Markdown based on an OutputFormatSpec.
|
|
6
|
-
*/
|
|
7
|
-
export function extractOutput(md, spec, opts = {}) {
|
|
8
|
-
const outline = buildOutline(md);
|
|
9
|
-
const tables = extractAllTables(md);
|
|
10
|
-
// Index nodes by normalized title
|
|
11
|
-
const matches = collectMatches(outline.nodes);
|
|
12
|
-
const sectionsByName = {};
|
|
13
|
-
// Extract each section
|
|
14
|
-
for (const section of spec.sections) {
|
|
15
|
-
const key = normalizeTitle(section.name);
|
|
16
|
-
const nodes = matches.get(key) ?? [];
|
|
17
|
-
if (nodes.length === 0) {
|
|
18
|
-
// Section not found - could add to a "missing" array if needed
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
// Choose best node (highest level)
|
|
22
|
-
const chosen = chooseBestNode(nodes);
|
|
23
|
-
const body = chosen.content_md.trim();
|
|
24
|
-
const extracted = {
|
|
25
|
-
nodeKey: chosen.key,
|
|
26
|
-
nodeLevel: chosen.level,
|
|
27
|
-
md: body
|
|
28
|
-
};
|
|
29
|
-
// Parse lists if required by section kind or if parseAllLists is enabled
|
|
30
|
-
if (section.kind === "list" || section.kind === "ordered_list" || opts.parseAllLists) {
|
|
31
|
-
const list = parseList(body);
|
|
32
|
-
if (list) {
|
|
33
|
-
extracted.list = list;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
sectionsByName[section.name] = extracted;
|
|
37
|
-
}
|
|
38
|
-
return {
|
|
39
|
-
outline,
|
|
40
|
-
sectionsByName,
|
|
41
|
-
tables
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Collect all nodes by normalized title.
|
|
46
|
-
*/
|
|
47
|
-
function collectMatches(nodes) {
|
|
48
|
-
const map = new Map();
|
|
49
|
-
function visit(node) {
|
|
50
|
-
const key = normalizeTitle(node.title);
|
|
51
|
-
const existing = map.get(key) ?? [];
|
|
52
|
-
existing.push(node);
|
|
53
|
-
map.set(key, existing);
|
|
54
|
-
node.children.forEach(visit);
|
|
55
|
-
}
|
|
56
|
-
nodes.forEach(visit);
|
|
57
|
-
return map;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Choose the best node from multiple matches.
|
|
61
|
-
* Prefer highest-level heading (smallest level number).
|
|
62
|
-
*/
|
|
63
|
-
function chooseBestNode(nodes) {
|
|
64
|
-
if (nodes.length === 1)
|
|
65
|
-
return nodes[0];
|
|
66
|
-
// Sort by level (ascending) and take first
|
|
67
|
-
const sorted = [...nodes].sort((a, b) => a.level - b.level);
|
|
68
|
-
return sorted[0];
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Normalize title for comparison: lowercase, remove trailing punctuation.
|
|
72
|
-
*/
|
|
73
|
-
function normalizeTitle(t) {
|
|
74
|
-
return t.trim().replace(/[:\-–—]\s*$/, "").trim().toLowerCase();
|
|
75
|
-
}
|
package/dist/ofs/issues.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { IssuesEnvelope, FlexMdResponseMode } from "../types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Detects if the response is an Issues envelope (Mode B).
|
|
4
|
-
*/
|
|
5
|
-
export declare function detectIssuesEnvelope(md: string): boolean;
|
|
6
|
-
/**
|
|
7
|
-
* Historical alias for processResponseMarkdown.
|
|
8
|
-
* @deprecated Use detectIssuesEnvelope
|
|
9
|
-
*/
|
|
10
|
-
export declare function detectResponseMode(md: string): FlexMdResponseMode;
|
|
11
|
-
/**
|
|
12
|
-
* Parses a Markdown Issues envelope into a structured JSON object.
|
|
13
|
-
*/
|
|
14
|
-
export declare function parseIssuesEnvelope(md: string): IssuesEnvelope | null;
|
package/dist/ofs/issues.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detects if the response is an Issues envelope (Mode B).
|
|
3
|
-
*/
|
|
4
|
-
export function detectIssuesEnvelope(md) {
|
|
5
|
-
const hasStatus = /(^|\n)##\s+Status\s*$/im.test(md);
|
|
6
|
-
const hasIssuesHeading = /(^|\n)##\s+Issues\s*$/im.test(md);
|
|
7
|
-
const hasStatusLine = /(^|\n)-\s*status:\s*(error|partial)\s*$/im.test(md);
|
|
8
|
-
return hasStatus && hasIssuesHeading && hasStatusLine;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Historical alias for processResponseMarkdown.
|
|
12
|
-
* @deprecated Use detectIssuesEnvelope
|
|
13
|
-
*/
|
|
14
|
-
export function detectResponseMode(md) {
|
|
15
|
-
return detectIssuesEnvelope(md) ? "issues" : "answer";
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Parses a Markdown Issues envelope into a structured JSON object.
|
|
19
|
-
*/
|
|
20
|
-
export function parseIssuesEnvelope(md) {
|
|
21
|
-
if (detectResponseMode(md) !== "issues")
|
|
22
|
-
return null;
|
|
23
|
-
const statusBlock = sectionBody(md, "Status");
|
|
24
|
-
const issuesBlock = sectionBody(md, "Issues");
|
|
25
|
-
if (!statusBlock || !issuesBlock)
|
|
26
|
-
return null;
|
|
27
|
-
const statusKv = parseTopLevelKvBullets(statusBlock);
|
|
28
|
-
const status = {
|
|
29
|
-
status: statusKv["status"] ?? "error",
|
|
30
|
-
code: statusKv["code"] ?? "invalid_format",
|
|
31
|
-
message: statusKv["message"] ?? "An error occurred."
|
|
32
|
-
};
|
|
33
|
-
const missingInputs = parseSimpleList(sectionBody(md, "Missing inputs"));
|
|
34
|
-
const clarificationsNeeded = parseSimpleList(sectionBody(md, "Clarifications needed"));
|
|
35
|
-
const issues = parseIssueItems(issuesBlock);
|
|
36
|
-
return {
|
|
37
|
-
mode: "issues",
|
|
38
|
-
status,
|
|
39
|
-
missingInputs: missingInputs.length ? missingInputs : undefined,
|
|
40
|
-
clarificationsNeeded: clarificationsNeeded.length ? clarificationsNeeded : undefined,
|
|
41
|
-
issues
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
function sectionBody(md, heading) {
|
|
45
|
-
const rx = new RegExp(`(^|\\n)##\\s+${escapeRx(heading)}\\s*\\n([\\s\\S]*?)(\\n##\\s+|$)`, "i");
|
|
46
|
-
const m = md.match(rx);
|
|
47
|
-
return m ? (m[2] ?? "").trim() : null;
|
|
48
|
-
}
|
|
49
|
-
function parseTopLevelKvBullets(block) {
|
|
50
|
-
const out = {};
|
|
51
|
-
for (const line of block.split("\n")) {
|
|
52
|
-
const m = line.match(/^\s*-\s*([a-zA-Z0-9_-]+)\s*:\s*(.+?)\s*$/);
|
|
53
|
-
if (m)
|
|
54
|
-
out[m[1]] = m[2];
|
|
55
|
-
}
|
|
56
|
-
return out;
|
|
57
|
-
}
|
|
58
|
-
function parseSimpleList(block) {
|
|
59
|
-
if (!block)
|
|
60
|
-
return [];
|
|
61
|
-
const items = [];
|
|
62
|
-
for (const line of block.split("\n")) {
|
|
63
|
-
const m = line.match(/^\s*-\s+(.+?)\s*$/);
|
|
64
|
-
if (m)
|
|
65
|
-
items.push(m[1].trim());
|
|
66
|
-
}
|
|
67
|
-
return items;
|
|
68
|
-
}
|
|
69
|
-
function parseIssueItems(block) {
|
|
70
|
-
const lines = block.split("\n");
|
|
71
|
-
const items = [];
|
|
72
|
-
let current = null;
|
|
73
|
-
for (const line of lines) {
|
|
74
|
-
const top = line.match(/^\s*-\s*issue:\s*(.+?)\s*$/i);
|
|
75
|
-
if (top) {
|
|
76
|
-
if (current)
|
|
77
|
-
items.push(current);
|
|
78
|
-
current = { issue: top[1].trim() };
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const sub = line.match(/^\s{2,}([a-zA-Z0-9_-]+)\s*:\s*(.+?)\s*$/);
|
|
82
|
-
if (sub && current) {
|
|
83
|
-
current[sub[1]] = sub[2].trim();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (current)
|
|
87
|
-
items.push(current);
|
|
88
|
-
return items;
|
|
89
|
-
}
|
|
90
|
-
function escapeRx(s) {
|
|
91
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
92
|
-
}
|
package/dist/ofs/validator.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { OutputFormatSpec } from "../types.js";
|
|
2
|
-
export interface OfsValidationResult {
|
|
3
|
-
ok: boolean;
|
|
4
|
-
errors: string[];
|
|
5
|
-
warnings: string[];
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Validate Markdown output against an OutputFormatSpec.
|
|
9
|
-
*/
|
|
10
|
-
export declare function validateOutput(md: string, spec: OutputFormatSpec): OfsValidationResult;
|