poe-code 3.0.312 → 3.0.313
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/index.js +80 -33
- package/dist/index.js.map +3 -3
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/markdown-reader/dist/core/document.js +6 -0
- package/packages/markdown-reader/dist/core/read-section.js +4 -0
- package/packages/markdown-reader/dist/core/resolve.js +6 -3
- package/packages/markdown-reader/dist/core/scan.js +28 -1
- package/packages/markdown-reader/dist/mcp/tools.js +11 -4
package/package.json
CHANGED
|
@@ -20,6 +20,9 @@ export async function loadMarkdownDocument(file, dependencies = {}) {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
export function resolveMarkdownPath(file, cwd = process.cwd()) {
|
|
23
|
+
if (file.trim().length === 0) {
|
|
24
|
+
throw new UserError("invalid file: expected a non-empty path");
|
|
25
|
+
}
|
|
23
26
|
return path.isAbsolute(file) ? file : path.resolve(cwd, file);
|
|
24
27
|
}
|
|
25
28
|
export function sliceMarkdownBytes(source, start, end) {
|
|
@@ -58,6 +61,9 @@ function hasYamlLikeLeadingFrontmatter(source) {
|
|
|
58
61
|
if (lines[0] !== "---") {
|
|
59
62
|
return false;
|
|
60
63
|
}
|
|
64
|
+
if (lines[1]?.trim().length === 0) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
61
67
|
for (const line of lines.slice(1)) {
|
|
62
68
|
const trimmed = line.trim();
|
|
63
69
|
if (trimmed.length === 0) {
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { loadMarkdownDocument, sliceMarkdownBytes } from "./document.js";
|
|
2
2
|
import { resolveSection } from "./resolve.js";
|
|
3
|
+
import { UserError } from "toolcraft";
|
|
3
4
|
export function createReadSection(dependencies = {}) {
|
|
4
5
|
return async function readSection(params) {
|
|
6
|
+
if (params.section.trim().length === 0) {
|
|
7
|
+
throw new UserError("invalid section: expected a non-empty section id");
|
|
8
|
+
}
|
|
5
9
|
const { source, sections } = await loadMarkdownDocument(params.file, dependencies);
|
|
6
10
|
const section = resolveSection(sections, params.section);
|
|
7
11
|
const end = params.includeChildren === false ? section.bodyEndNoChildren : section.bodyEnd;
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { UserError } from "toolcraft";
|
|
2
2
|
export function resolveSection(sections, id) {
|
|
3
3
|
const trimmedId = id.trim();
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return unnumberedTitleMatch;
|
|
4
|
+
if (trimmedId.length === 0) {
|
|
5
|
+
throw new UserError("invalid section: expected a non-empty section id");
|
|
7
6
|
}
|
|
8
7
|
const sectionByNumber = sections.find((section) => section.number === trimmedId);
|
|
9
8
|
if (sectionByNumber !== undefined) {
|
|
10
9
|
return sectionByNumber;
|
|
11
10
|
}
|
|
11
|
+
const unnumberedTitleMatch = sections.find((section) => section.number === null && section.title === trimmedId);
|
|
12
|
+
if (unnumberedTitleMatch !== undefined) {
|
|
13
|
+
return unnumberedTitleMatch;
|
|
14
|
+
}
|
|
12
15
|
const titleMatches = sections.filter((section) => section.title === trimmedId);
|
|
13
16
|
if (titleMatches.length === 1) {
|
|
14
17
|
return titleMatches[0];
|
|
@@ -4,7 +4,10 @@ export function scanMarkdown(source) {
|
|
|
4
4
|
if (ast.type !== "root") {
|
|
5
5
|
return [];
|
|
6
6
|
}
|
|
7
|
-
const
|
|
7
|
+
const htmlCommentRanges = collectHtmlCommentRanges(source);
|
|
8
|
+
const headings = ast.children
|
|
9
|
+
.filter(isHeadingNode)
|
|
10
|
+
.filter((heading) => !isInsideHtmlComment(getRequiredRange(heading).start, htmlCommentRanges));
|
|
8
11
|
if (headings.length === 0) {
|
|
9
12
|
return [];
|
|
10
13
|
}
|
|
@@ -25,6 +28,30 @@ export function scanMarkdown(source) {
|
|
|
25
28
|
applyNumbers(sections, baselineDepth);
|
|
26
29
|
return sections;
|
|
27
30
|
}
|
|
31
|
+
function collectHtmlCommentRanges(source) {
|
|
32
|
+
const ranges = [];
|
|
33
|
+
let searchStart = 0;
|
|
34
|
+
while (searchStart < source.length) {
|
|
35
|
+
const commentStart = source.indexOf("<!--", searchStart);
|
|
36
|
+
if (commentStart === -1) {
|
|
37
|
+
return ranges;
|
|
38
|
+
}
|
|
39
|
+
const commentEndMarker = source.indexOf("-->", commentStart + "<!--".length);
|
|
40
|
+
const commentEnd = commentEndMarker === -1 ? source.length : commentEndMarker + "-->".length;
|
|
41
|
+
ranges.push({
|
|
42
|
+
start: byteOffset(source, commentStart),
|
|
43
|
+
end: byteOffset(source, commentEnd)
|
|
44
|
+
});
|
|
45
|
+
searchStart = commentEnd;
|
|
46
|
+
}
|
|
47
|
+
return ranges;
|
|
48
|
+
}
|
|
49
|
+
function isInsideHtmlComment(offset, ranges) {
|
|
50
|
+
return ranges.some((range) => offset >= range.start && offset < range.end);
|
|
51
|
+
}
|
|
52
|
+
function byteOffset(source, index) {
|
|
53
|
+
return Buffer.byteLength(source.slice(0, index), "utf8");
|
|
54
|
+
}
|
|
28
55
|
function isHeadingNode(node) {
|
|
29
56
|
return node.type === "heading";
|
|
30
57
|
}
|
|
@@ -3,8 +3,12 @@ import { S } from "toolcraft-schema";
|
|
|
3
3
|
import { readMarkdown } from "../core/read-markdown.js";
|
|
4
4
|
import { readSection } from "../core/read-section.js";
|
|
5
5
|
const readParams = S.Object({
|
|
6
|
-
file: S.String({ description: "Path to the markdown file" }),
|
|
7
|
-
depth: S.Optional(S.Number({
|
|
6
|
+
file: S.String({ description: "Path to the markdown file", minLength: 1 }),
|
|
7
|
+
depth: S.Optional(S.Number({
|
|
8
|
+
description: "Limit TOC to headings at depth <= n",
|
|
9
|
+
jsonType: "integer",
|
|
10
|
+
minimum: 0
|
|
11
|
+
}))
|
|
8
12
|
});
|
|
9
13
|
const tocEntryResult = S.Object({
|
|
10
14
|
depth: S.Number(),
|
|
@@ -24,8 +28,11 @@ export const readTool = defineCommand({
|
|
|
24
28
|
handler: async ({ params }) => readMarkdown(params)
|
|
25
29
|
});
|
|
26
30
|
const readSectionParams = S.Object({
|
|
27
|
-
file: S.String({ description: "Path to the markdown file" }),
|
|
28
|
-
section: S.String({
|
|
31
|
+
file: S.String({ description: "Path to the markdown file", minLength: 1 }),
|
|
32
|
+
section: S.String({
|
|
33
|
+
description: "Numeric path or exact heading text to read",
|
|
34
|
+
minLength: 1
|
|
35
|
+
}),
|
|
29
36
|
includeChildren: S.Optional(S.Boolean({ description: "Include nested child sections" }))
|
|
30
37
|
});
|
|
31
38
|
export const readSectionTool = defineCommand({
|