ntion 0.1.2 → 0.2.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 +8 -0
- package/dist/notion/markdown.js +133 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,6 +35,14 @@ A single "get all my tasks" workflow tells the whole story:
|
|
|
35
35
|
2. **No schema bloat** — MCP's database fetch includes ~2 KB of SQLite DDL, ~800 B of XML boilerplate, and ~1.4 KB of base64 `collectionPropertyOption://` URLs that are never used for reads. ntion returns only actionable data.
|
|
36
36
|
3. **Markdown-first** — Page content defaults to markdown, matching what agents actually consume. No manual format negotiation needed.
|
|
37
37
|
|
|
38
|
+
## Agent skill
|
|
39
|
+
|
|
40
|
+
ntion ships with an [agent skill](https://docs.anthropic.com/en/docs/claude-code/skills) that teaches AI agents how to use the CLI. Install it with:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx skills add https://github.com/mbroton/notion-cli --skill ntion-cli
|
|
44
|
+
```
|
|
45
|
+
|
|
38
46
|
## Install
|
|
39
47
|
|
|
40
48
|
```bash
|
package/dist/notion/markdown.js
CHANGED
|
@@ -9,13 +9,142 @@ function chunkText(content) {
|
|
|
9
9
|
}
|
|
10
10
|
return chunks;
|
|
11
11
|
}
|
|
12
|
+
const DEFAULT_ANNOTATIONS = {
|
|
13
|
+
bold: false,
|
|
14
|
+
italic: false,
|
|
15
|
+
strikethrough: false,
|
|
16
|
+
code: false,
|
|
17
|
+
};
|
|
18
|
+
function findClosingStar(text, start) {
|
|
19
|
+
for (let i = start; i < text.length; i++) {
|
|
20
|
+
if (text[i] === "*" && text[i + 1] !== "*" && text[i - 1] !== "*") {
|
|
21
|
+
return i;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return -1;
|
|
25
|
+
}
|
|
26
|
+
function parseInlineSegments(text, inherited, linkUrl) {
|
|
27
|
+
const segments = [];
|
|
28
|
+
let i = 0;
|
|
29
|
+
let plain = "";
|
|
30
|
+
const flush = () => {
|
|
31
|
+
if (plain) {
|
|
32
|
+
segments.push({ content: plain, annotations: { ...inherited }, link: linkUrl });
|
|
33
|
+
plain = "";
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
while (i < text.length) {
|
|
37
|
+
// Inline code (no nesting inside)
|
|
38
|
+
if (text[i] === "`") {
|
|
39
|
+
const close = text.indexOf("`", i + 1);
|
|
40
|
+
if (close !== -1) {
|
|
41
|
+
flush();
|
|
42
|
+
segments.push({
|
|
43
|
+
content: text.slice(i + 1, close),
|
|
44
|
+
annotations: { ...inherited, code: true },
|
|
45
|
+
link: linkUrl,
|
|
46
|
+
});
|
|
47
|
+
i = close + 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Link [text](url)
|
|
52
|
+
if (text[i] === "[") {
|
|
53
|
+
const bracketClose = text.indexOf("]", i + 1);
|
|
54
|
+
if (bracketClose !== -1 && text[bracketClose + 1] === "(") {
|
|
55
|
+
const parenClose = text.indexOf(")", bracketClose + 2);
|
|
56
|
+
if (parenClose !== -1) {
|
|
57
|
+
flush();
|
|
58
|
+
const linkText = text.slice(i + 1, bracketClose);
|
|
59
|
+
const url = text.slice(bracketClose + 2, parenClose);
|
|
60
|
+
segments.push(...parseInlineSegments(linkText, inherited, url));
|
|
61
|
+
i = parenClose + 1;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// *** bold+italic
|
|
67
|
+
if (text[i] === "*" && text[i + 1] === "*" && text[i + 2] === "*") {
|
|
68
|
+
const close = text.indexOf("***", i + 3);
|
|
69
|
+
if (close !== -1) {
|
|
70
|
+
flush();
|
|
71
|
+
segments.push(...parseInlineSegments(text.slice(i + 3, close), { ...inherited, bold: true, italic: true }, linkUrl));
|
|
72
|
+
i = close + 3;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// ** bold
|
|
77
|
+
if (text[i] === "*" && text[i + 1] === "*") {
|
|
78
|
+
const close = text.indexOf("**", i + 2);
|
|
79
|
+
if (close !== -1) {
|
|
80
|
+
flush();
|
|
81
|
+
segments.push(...parseInlineSegments(text.slice(i + 2, close), { ...inherited, bold: true }, linkUrl));
|
|
82
|
+
i = close + 2;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// * italic
|
|
87
|
+
if (text[i] === "*") {
|
|
88
|
+
const close = findClosingStar(text, i + 1);
|
|
89
|
+
if (close !== -1) {
|
|
90
|
+
flush();
|
|
91
|
+
segments.push(...parseInlineSegments(text.slice(i + 1, close), { ...inherited, italic: true }, linkUrl));
|
|
92
|
+
i = close + 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// ~~ strikethrough
|
|
97
|
+
if (text[i] === "~" && text[i + 1] === "~") {
|
|
98
|
+
const close = text.indexOf("~~", i + 2);
|
|
99
|
+
if (close !== -1) {
|
|
100
|
+
flush();
|
|
101
|
+
segments.push(...parseInlineSegments(text.slice(i + 2, close), { ...inherited, strikethrough: true }, linkUrl));
|
|
102
|
+
i = close + 2;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
plain += text[i];
|
|
107
|
+
i++;
|
|
108
|
+
}
|
|
109
|
+
flush();
|
|
110
|
+
return segments;
|
|
111
|
+
}
|
|
112
|
+
function segmentToRichText(segment) {
|
|
113
|
+
return chunkText(segment.content).map((chunk) => {
|
|
114
|
+
const textObj = { content: chunk };
|
|
115
|
+
if (segment.link) {
|
|
116
|
+
textObj.link = { url: segment.link };
|
|
117
|
+
}
|
|
118
|
+
const obj = { type: "text", text: textObj };
|
|
119
|
+
const { bold, italic, strikethrough, code } = segment.annotations;
|
|
120
|
+
if (bold || italic || strikethrough || code) {
|
|
121
|
+
const annotations = {};
|
|
122
|
+
if (bold)
|
|
123
|
+
annotations.bold = true;
|
|
124
|
+
if (italic)
|
|
125
|
+
annotations.italic = true;
|
|
126
|
+
if (strikethrough)
|
|
127
|
+
annotations.strikethrough = true;
|
|
128
|
+
if (code)
|
|
129
|
+
annotations.code = true;
|
|
130
|
+
obj.annotations = annotations;
|
|
131
|
+
}
|
|
132
|
+
return obj;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
12
135
|
function toRichText(content) {
|
|
136
|
+
const normalized = content.length > 0 ? content : " ";
|
|
137
|
+
const segments = parseInlineSegments(normalized, DEFAULT_ANNOTATIONS);
|
|
138
|
+
if (segments.length === 0) {
|
|
139
|
+
return [{ type: "text", text: { content: " " } }];
|
|
140
|
+
}
|
|
141
|
+
return segments.flatMap(segmentToRichText);
|
|
142
|
+
}
|
|
143
|
+
function toPlainRichText(content) {
|
|
13
144
|
const normalized = content.length > 0 ? content : " ";
|
|
14
145
|
return chunkText(normalized).map((chunk) => ({
|
|
15
146
|
type: "text",
|
|
16
|
-
text: {
|
|
17
|
-
content: chunk,
|
|
18
|
-
},
|
|
147
|
+
text: { content: chunk },
|
|
19
148
|
}));
|
|
20
149
|
}
|
|
21
150
|
function paragraphBlock(text) {
|
|
@@ -131,7 +260,7 @@ function codeBlock(text, language) {
|
|
|
131
260
|
object: "block",
|
|
132
261
|
type: "code",
|
|
133
262
|
code: {
|
|
134
|
-
rich_text:
|
|
263
|
+
rich_text: toPlainRichText(text),
|
|
135
264
|
language,
|
|
136
265
|
},
|
|
137
266
|
};
|