typeclaw 0.35.0 → 0.36.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/package.json +2 -1
- package/src/agent/plugin-tools.ts +18 -8
- package/src/agent/session-origin.ts +4 -3
- package/src/agent/system-prompt.ts +1 -1
- package/src/bundled-plugins/doc-render/index.ts +20 -0
- package/src/bundled-plugins/doc-render/render.ts +140 -0
- package/src/bundled-plugins/doc-render/skills/typeclaw-render-pdf/SKILL.md +314 -0
- package/src/bundled-plugins/security/index.ts +15 -0
- package/src/bundled-plugins/security/permissions.ts +1 -0
- package/src/bundled-plugins/security/policies/plugin-addition.ts +240 -0
- package/src/channels/adapters/line.ts +12 -2
- package/src/cli/channel.ts +190 -7
- package/src/cli/inspect-select.ts +121 -0
- package/src/cli/inspect.ts +15 -7
- package/src/config/channels-mutation.ts +250 -0
- package/src/container/start.ts +24 -1
- package/src/init/reconcile-plugin-deps.ts +173 -0
- package/src/inspect/index.ts +5 -2
- package/src/inspect/live.ts +128 -13
- package/src/inspect/loop.ts +26 -9
- package/src/inspect/render.ts +28 -1
- package/src/inspect/transcript-view.ts +52 -11
- package/src/plugin/index.ts +2 -2
- package/src/plugin/loader.ts +61 -7
- package/src/plugin/manager.ts +18 -4
- package/src/run/bundled-plugins.ts +2 -0
- package/src/sandbox/availability.ts +87 -19
- package/src/sandbox/index.ts +4 -0
- package/src/secrets/storage.ts +27 -0
- package/src/server/index.ts +5 -1
- package/src/shared/protocol.ts +22 -11
- package/src/skills/typeclaw-markdown-pdf/SKILL.md +0 -400
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: typeclaw-markdown-pdf
|
|
3
|
-
description: "The ONLY supported way to turn Markdown into a polished, professional PDF (and optionally attach it to a channel). Load this whenever you need to deliver a document as a PDF rather than raw markdown — reports, summaries, briefs, meeting notes, docs, render report, export document, anything a human would want to download, print, or forward, including a researcher's report file shipped as a Slack/Discord attachment. Triggers: 'make a PDF', 'export to PDF', 'markdown to PDF', 'PDF report', 'render report', 'export document', 'the report', 'attach the report', 'send me a PDF', 'as a PDF', 'turn this into a document', a researcher/subagent result you want to ship as a file, 'PDF로', 'PDF로 만들어', 'PDF로 변환', 'PDF 첨부', '리포트', '보고서'. Handles CJK/Korean/Japanese/Chinese: CJK fonts are opt-in, so before rendering it checks whether a CJK font is present and, if not, asks the user to enable `docker.file.cjkFonts` and regenerate rather than shipping a tofu PDF — it never auto-downloads a font. Also load before saying you cannot produce PDFs — you can: this skill installs a tiny Typst toolchain into workspace/ on first use, then renders. NEVER build a PDF with jsPDF, pdfkit, a canvas text dump, or a headless-browser raw-text print — those produce unrendered markdown and broken CJK; this skill is the only correct path. Covers the one-time setup, the styled wrapper, the render command, and how to attach the PDF to Slack/Discord/Telegram/KakaoTalk. For operating on EXISTING PDFs (merge, split, extract text, fill forms), this is not the skill — use pypdf/qpdf instead."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# typeclaw-markdown-pdf
|
|
7
|
-
|
|
8
|
-
You can produce professional PDFs from Markdown. This skill installs a small,
|
|
9
|
-
self-contained [Typst](https://typst.app) toolchain into your `workspace/` the
|
|
10
|
-
**first time** you need a PDF, then reuses it. No Pandoc, no LaTeX, no headless
|
|
11
|
-
browser — just an npm-installed Typst compiler plus the
|
|
12
|
-
[`cmarker`](https://typst.app/universe/package/cmarker/) package that reads your
|
|
13
|
-
Markdown.
|
|
14
|
-
|
|
15
|
-
The flow is: **(1)** run the one-time setup (`bun add` the Typst compiler +
|
|
16
|
-
vendor `cmarker` into `workspace/.tools/`), **(2)** write a styled `.typ` wrapper
|
|
17
|
-
that reads your Markdown, **(3)** run the render script. If a channel asked for
|
|
18
|
-
the PDF, attach the result with `channel_send`.
|
|
19
|
-
|
|
20
|
-
You do **not** need to learn Typst markup. `cmarker` renders your CommonMark
|
|
21
|
-
(headings, lists, tables, code, blockquotes, footnotes, links, images). The
|
|
22
|
-
wrapper only sets _styling_ — fonts, margins, headings, page numbers — so the
|
|
23
|
-
output looks deliberate, not like a default-template export.
|
|
24
|
-
|
|
25
|
-
> **This is the only supported way to make a PDF from Markdown in TypeClaw.**
|
|
26
|
-
> Do **not** reach for `jsPDF`, `pdfkit`, a `<canvas>` text dump, or a
|
|
27
|
-
> headless-browser "print raw text" path. Those skip Markdown rendering (you get
|
|
28
|
-
> literal `##` and `**` in the output) and ship no CJK font, so Korean/Japanese/
|
|
29
|
-
> Chinese come out as mojibake. The Typst path below renders the Markdown properly;
|
|
30
|
-
> for CJK it relies on the opt-in `cjkFonts` font and gates on its presence (see
|
|
31
|
-
> "## Handling CJK content") rather than shipping tofu. If you catch yourself about
|
|
32
|
-
> to `bun add` a PDF library, stop and use this skill instead.
|
|
33
|
-
|
|
34
|
-
## When to use this
|
|
35
|
-
|
|
36
|
-
- A research report, brief, or summary the user wants as a downloadable file.
|
|
37
|
-
- A subagent (e.g. the `researcher`) handed you a `research-<slug>.md` to ship as a PDF.
|
|
38
|
-
- Any channel message asking for "a PDF" / "the report attached" / "PDF로 보내줘".
|
|
39
|
-
|
|
40
|
-
When plain markdown in chat is fine, **don't** make a PDF. This is for when a
|
|
41
|
-
_file_ is the deliverable.
|
|
42
|
-
|
|
43
|
-
## Step 0 — one-time setup (install the toolchain)
|
|
44
|
-
|
|
45
|
-
Run this `bash` block once per container life. It is **idempotent** — if the
|
|
46
|
-
tools are already present it does nothing and exits fast. It `bun add`s the
|
|
47
|
-
version-pinned Typst compiler (npm pulls only this platform's prebuilt binary —
|
|
48
|
-
Linux x64/arm64, glibc or musl) and vendors the SHA256-verified `cmarker` package
|
|
49
|
-
into `workspace/.tools/` so `@preview/cmarker` resolves offline.
|
|
50
|
-
|
|
51
|
-
```sh
|
|
52
|
-
set -eu
|
|
53
|
-
cd workspace
|
|
54
|
-
mkdir -p .tools
|
|
55
|
-
cd .tools
|
|
56
|
-
|
|
57
|
-
# Pinned to the exact versions validated for this skill. COMPILER_VERSION is the
|
|
58
|
-
# npm package version of the Typst compiler; it embeds Typst 0.14.2. Bumping
|
|
59
|
-
# either is a deliberate edit — keep the embedded-Typst note below in sync.
|
|
60
|
-
COMPILER_VERSION="0.7.0" # @myriaddreamin/typst-ts-node-compiler (embeds Typst 0.14.2)
|
|
61
|
-
CMARKER_VERSION="0.1.8"
|
|
62
|
-
PKGDIR="typst-packages/preview/cmarker/$CMARKER_VERSION"
|
|
63
|
-
|
|
64
|
-
if [ -f "node_modules/@myriaddreamin/typst-ts-node-compiler/package.json" ] && [ -f "$PKGDIR/lib.typ" ]; then
|
|
65
|
-
echo "markdown-pdf toolchain already installed"
|
|
66
|
-
else
|
|
67
|
-
# The Typst compiler, version-pinned. `bun add` resolves the right prebuilt
|
|
68
|
-
# NAPI binary for this platform via optionalDependencies — no Rust toolchain,
|
|
69
|
-
# no manual download. The exact pin keeps the toolchain reproducible: a future
|
|
70
|
-
# npm release can't silently change the embedded Typst version or the API that
|
|
71
|
-
# Step 3 depends on.
|
|
72
|
-
[ -f package.json ] || echo '{"name":"typeclaw-markdown-pdf-tools","private":true}' > package.json
|
|
73
|
-
bun add "@myriaddreamin/typst-ts-node-compiler@$COMPILER_VERSION"
|
|
74
|
-
|
|
75
|
-
# cmarker (Markdown -> Typst), vendored so compilation needs no network.
|
|
76
|
-
mkdir -p "$PKGDIR"
|
|
77
|
-
curl -fsSL -o cmarker.tar.gz \
|
|
78
|
-
"https://packages.typst.org/preview/cmarker-$CMARKER_VERSION.tar.gz"
|
|
79
|
-
echo "157cc40db2716f12c7eabb95df1f60714a4d95ebfb1c6087cf4aec224e49392a cmarker.tar.gz" | sha256sum -c -
|
|
80
|
-
tar -xzf cmarker.tar.gz -C "$PKGDIR"
|
|
81
|
-
rm cmarker.tar.gz
|
|
82
|
-
echo "markdown-pdf toolchain installed"
|
|
83
|
-
fi
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Notes:
|
|
87
|
-
|
|
88
|
-
- It writes only under `workspace/`, the directory your `bash`/`write` tools can
|
|
89
|
-
write to. `workspace/.tools/` is gitignored scratch — it does not get committed.
|
|
90
|
-
- It needs network the first time (to `bun add` the compiler + fetch the package).
|
|
91
|
-
After that the tools persist for the life of the container.
|
|
92
|
-
- **Everything is version-pinned and reproducible.** The validated toolchain is
|
|
93
|
-
`@myriaddreamin/typst-ts-node-compiler@0.7.0` (which embeds Typst **0.14.2**) and
|
|
94
|
-
`cmarker@0.1.8` (SHA256-verified). The `bun add` uses the exact `@0.7.0` pin, so
|
|
95
|
-
a future npm release can't change the embedded Typst version or the API Step 3
|
|
96
|
-
uses. To upgrade, bump both `COMPILER_VERSION` and the embedded-Typst note
|
|
97
|
-
together after re-validating.
|
|
98
|
-
|
|
99
|
-
## Step 1 — have the markdown ready
|
|
100
|
-
|
|
101
|
-
Use an existing markdown file (yours or a subagent's), or `write` your content to
|
|
102
|
-
`workspace/<slug>.md`. Standard CommonMark plus tables and footnotes all work.
|
|
103
|
-
|
|
104
|
-
## Step 2 — write the styled wrapper
|
|
105
|
-
|
|
106
|
-
`write` this to `workspace/<slug>.typ`, changing only the `read("...")` filename
|
|
107
|
-
to match your markdown. The defaults are a clean, professional house style; adjust
|
|
108
|
-
fonts/margins only if the user asks.
|
|
109
|
-
|
|
110
|
-
```typst
|
|
111
|
-
#set document(title: "Report")
|
|
112
|
-
#set page(
|
|
113
|
-
paper: "a4",
|
|
114
|
-
margin: (x: 2.5cm, y: 2.75cm),
|
|
115
|
-
numbering: "1",
|
|
116
|
-
footer: context align(center, text(size: 9pt, fill: luma(120))[
|
|
117
|
-
#counter(page).display("1 / 1", both: true)
|
|
118
|
-
]),
|
|
119
|
-
)
|
|
120
|
-
#set text(font: ("Libertinus Serif", "New Computer Modern", "Noto Serif CJK KR"), size: 11pt, lang: "en")
|
|
121
|
-
#set par(justify: true, leading: 0.68em, spacing: 1.1em)
|
|
122
|
-
|
|
123
|
-
#show heading: set text(weight: "semibold")
|
|
124
|
-
#show heading.where(level: 1): it => block(width: 100%, above: 1.4em, below: 0.9em)[
|
|
125
|
-
#text(size: 1.5em, it.body)
|
|
126
|
-
#v(-0.4em)
|
|
127
|
-
#line(length: 100%, stroke: 0.5pt + luma(200))
|
|
128
|
-
]
|
|
129
|
-
#show link: it => text(fill: rgb("#1a56db"), underline(it))
|
|
130
|
-
#show quote.where(block: true): it => block(
|
|
131
|
-
inset: (left: 1em), stroke: (left: 2pt + luma(200)),
|
|
132
|
-
text(style: "italic", fill: luma(80), it.body),
|
|
133
|
-
)
|
|
134
|
-
#show raw.where(block: true): it => block(
|
|
135
|
-
fill: luma(245), inset: 8pt, radius: 4pt, width: 100%, text(size: 9pt, it),
|
|
136
|
-
)
|
|
137
|
-
#show table: set table(stroke: 0.5pt + luma(200))
|
|
138
|
-
|
|
139
|
-
#import "@preview/cmarker:0.1.8"
|
|
140
|
-
#cmarker.render(read("report.md"), h1-level: 1, blockquote: quote.with(block: true))
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Notes:
|
|
144
|
-
|
|
145
|
-
- `read("report.md")` is **relative to the workspace** (the compiler's `workspace`
|
|
146
|
-
is set to `workspace/` — see Step 3). Keep the `.typ` and `.md` in `workspace/`.
|
|
147
|
-
- Fonts `Libertinus Serif` / `New Computer Modern` are bundled with Typst (no font
|
|
148
|
-
install) and carry the Latin text. `"Noto Serif CJK KR"` is appended as the
|
|
149
|
-
fallback so Korean/CJK glyphs resolve per-glyph — Typst falls through to it
|
|
150
|
-
wherever the Latin fonts have no glyph, leaving Latin runs untouched. It comes
|
|
151
|
-
from `fonts-noto-cjk`, which Step 3's renderer loads from `/usr/share/fonts` via
|
|
152
|
-
`fontPaths`. **The package is only present when the container's `cjkFonts` toggle
|
|
153
|
-
resolves to `true`** (default `"auto"` installs it only on a CJK host locale), so
|
|
154
|
-
on a non-CJK host CJK text renders as tofu — see "## Handling CJK content" below
|
|
155
|
-
for the pre-render check that catches this and asks the user before shipping a
|
|
156
|
-
broken PDF. If your CJK font lives elsewhere, add its dir to the `fontPaths` list.
|
|
157
|
-
|
|
158
|
-
## Handling CJK content
|
|
159
|
-
|
|
160
|
-
CJK fonts are **opt-in** (the `docker.file.cjkFonts` toggle). When they are off,
|
|
161
|
-
Typst still renders — it just substitutes `.notdef` tofu boxes for every
|
|
162
|
-
Korean/Japanese/Chinese glyph, so the render "succeeds" and you can ship a broken
|
|
163
|
-
PDF without noticing. **Do not** download, vendor, or `curl` a font into the
|
|
164
|
-
workspace to work around this, and **do not** silently deliver a tofu PDF. Instead,
|
|
165
|
-
run this gate **before** Step 3 whenever the markdown might contain CJK:
|
|
166
|
-
|
|
167
|
-
```sh
|
|
168
|
-
# Run from workspace/. MD is the markdown you are about to render.
|
|
169
|
-
MD="report.md"
|
|
170
|
-
|
|
171
|
-
# Hangul, Kana, CJK ideographs + the common extensions. grep -P on Debian; perl
|
|
172
|
-
# slurp as the fallback (BusyBox/macOS grep lack -P).
|
|
173
|
-
CJK_RE='[\x{1100}-\x{11FF}\x{3130}-\x{318F}\x{AC00}-\x{D7A3}\x{3040}-\x{30FF}\x{31F0}-\x{31FF}\x{3400}-\x{4DBF}\x{4E00}-\x{9FFF}\x{F900}-\x{FAFF}\x{20000}-\x{2A6DF}\x{2A700}-\x{2B73F}\x{2B740}-\x{2B81F}\x{2B820}-\x{2CEAF}\x{2CEB0}-\x{2EBEF}\x{30000}-\x{3134F}]'
|
|
174
|
-
if command -v grep >/dev/null && echo | grep -qP '' 2>/dev/null; then
|
|
175
|
-
LC_ALL=C.UTF-8 grep -qP "$CJK_RE" -- "$MD" && HAS_CJK=1 || HAS_CJK=0
|
|
176
|
-
else
|
|
177
|
-
perl -CSDA -0777 -ne "exit(/$CJK_RE/ ? 0 : 1)" "$MD" && HAS_CJK=1 || HAS_CJK=0
|
|
178
|
-
fi
|
|
179
|
-
|
|
180
|
-
# A CJK font Typst can load. dpkg is the authoritative signal for the opt-in
|
|
181
|
-
# fonts-noto-cjk package; the file scan covers a preinstalled or mounted font.
|
|
182
|
-
# fontconfig/fc-list is NOT consulted — Typst reads fontPaths directly, not fc.
|
|
183
|
-
has_cjk_font() {
|
|
184
|
-
dpkg-query -W -f='${Status}' fonts-noto-cjk 2>/dev/null | grep -q 'install ok installed' && return 0
|
|
185
|
-
find /usr/share/fonts /usr/local/share/fonts -type f \( -iname '*.otf' -o -iname '*.ttf' -o -iname '*.ttc' \) 2>/dev/null |
|
|
186
|
-
grep -Eiq '(Noto(Sans|Serif)CJK|Noto (Sans|Serif) CJK|SourceHan|Source Han|WenQuanYi|Nanum|Unifont|DroidSansFallback|AR PL)'
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if [ "$HAS_CJK" = 1 ] && ! has_cjk_font; then
|
|
190
|
-
echo "CJK_FONT_MISSING"
|
|
191
|
-
fi
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
If the gate prints `CJK_FONT_MISSING`, **stop — do not render or attach a PDF.**
|
|
195
|
-
Tell the user, honestly, that this is a restart-required boot setting, e.g.:
|
|
196
|
-
|
|
197
|
-
> This report has Korean/Japanese/Chinese text, but the container has no CJK font
|
|
198
|
-
> — they're opt-in, so the PDF would come out as tofu boxes. Want me to set
|
|
199
|
-
> `docker.file.cjkFonts: true` in `typeclaw.json`? It's a boot setting, so after I
|
|
200
|
-
> edit it you'll need to run `typeclaw restart` from the host project directory,
|
|
201
|
-
> and then I'll regenerate the PDF.
|
|
202
|
-
|
|
203
|
-
Only after the user agrees: edit `typeclaw.json` to set `docker.file.cjkFonts:
|
|
204
|
-
true` (use the `typeclaw-config` skill), ask them to `typeclaw restart`, and
|
|
205
|
-
regenerate the PDF **after** the restarted container reports `has_cjk_font` true.
|
|
206
|
-
If the markdown has no CJK, or a CJK font is present, skip straight to Step 3.
|
|
207
|
-
|
|
208
|
-
## Step 3 — render
|
|
209
|
-
|
|
210
|
-
`write` this tiny renderer to `workspace/.tools/render.ts`, then run it. It loads
|
|
211
|
-
the npm-installed compiler, points the package cache at the vendored `cmarker`, and
|
|
212
|
-
writes the PDF. Pass the wrapper and output paths as arguments.
|
|
213
|
-
|
|
214
|
-
```ts
|
|
215
|
-
// workspace/.tools/render.ts
|
|
216
|
-
import { NodeCompiler } from '@myriaddreamin/typst-ts-node-compiler'
|
|
217
|
-
import { existsSync, writeFileSync } from 'node:fs'
|
|
218
|
-
|
|
219
|
-
const [, , mainFile, outFile] = process.argv
|
|
220
|
-
if (!mainFile || !outFile) throw new Error('usage: render.ts <main.typ> <out.pdf>')
|
|
221
|
-
|
|
222
|
-
// Load system fonts so CJK glyphs resolve. The compiler does NOT auto-discover
|
|
223
|
-
// system font dirs the way the Typst CLI does — without explicit fontPaths,
|
|
224
|
-
// "Noto Serif CJK KR" (from fonts-noto-cjk under /usr/share/fonts) is invisible
|
|
225
|
-
// and Korean/Japanese/Chinese text renders as .notdef tofu boxes. Filtered with
|
|
226
|
-
// existsSync so a missing dir (e.g. on a dev/host run) is skipped, not fatal.
|
|
227
|
-
const fontPaths = ['/usr/share/fonts', '/usr/local/share/fonts', '/Library/Fonts', '/System/Library/Fonts'].filter(
|
|
228
|
-
existsSync,
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
const compiler = NodeCompiler.create({
|
|
232
|
-
workspace: '.', // run from workspace/, so read("report.md") resolves
|
|
233
|
-
...(fontPaths.length > 0 ? { fontArgs: [{ fontPaths }] } : {}),
|
|
234
|
-
})
|
|
235
|
-
const pdf = compiler.pdf({ mainFilePath: mainFile })
|
|
236
|
-
writeFileSync(outFile, Buffer.from(pdf))
|
|
237
|
-
console.log(`wrote ${outFile} (${pdf.length} bytes)`)
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
Run it from `workspace/`, with the package cache pointed at the vendored packages:
|
|
241
|
-
|
|
242
|
-
```sh
|
|
243
|
-
cd workspace
|
|
244
|
-
TYPST_PACKAGE_CACHE_PATH="$PWD/.tools/typst-packages" \
|
|
245
|
-
TYPST_PACKAGE_PATH="$PWD/.tools/typst-packages" \
|
|
246
|
-
bun .tools/render.ts report.typ report.pdf
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
Verify: the command prints `wrote report.pdf (...)` and `workspace/report.pdf`
|
|
250
|
-
exists. On a compile error the compiler throws with the offending Typst line —
|
|
251
|
-
usually raw HTML or a markdown extension `cmarker` doesn't support; simplify that
|
|
252
|
-
part and re-run.
|
|
253
|
-
|
|
254
|
-
## Rich elements (optional)
|
|
255
|
-
|
|
256
|
-
When plain markdown isn't enough — you want a cover banner, callout boxes,
|
|
257
|
-
multi-column sections, captioned figures — you don't switch to HTML (Typst
|
|
258
|
-
doesn't render HTML). Instead, drop **raw Typst** into the markdown via
|
|
259
|
-
`<!--raw-typst ... -->` comments. `cmarker` evaluates them as Typst (the
|
|
260
|
-
`raw-typst: true` option is already the default and is set in the wrapper above).
|
|
261
|
-
The rest of the document stays plain markdown.
|
|
262
|
-
|
|
263
|
-
Each snippet below is self-contained — paste it into your `.md` where you want the
|
|
264
|
-
element. They use Typst built-ins only (no extra packages).
|
|
265
|
-
|
|
266
|
-
**Cover banner** (top of a report):
|
|
267
|
-
|
|
268
|
-
```markdown
|
|
269
|
-
<!--raw-typst
|
|
270
|
-
#block(width: 100%, fill: rgb("#0f172a"), inset: 18pt, radius: 6pt)[
|
|
271
|
-
#text(fill: white, size: 1.6em, weight: "bold")[Quarterly Business Review]
|
|
272
|
-
#v(2pt)
|
|
273
|
-
#text(fill: rgb("#94a3b8"), size: 0.95em)[Acme Robotics · Q2 2026 · Confidential]
|
|
274
|
-
]
|
|
275
|
-
#v(1em)
|
|
276
|
-
-->
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
**Callout boxes** (info / warning — change the two colors for other variants):
|
|
280
|
-
|
|
281
|
-
```markdown
|
|
282
|
-
<!--raw-typst
|
|
283
|
-
#block(fill: rgb("#eff6ff"), stroke: (left: 3pt + rgb("#3b82f6")), inset: 12pt, radius: 4pt, width: 100%)[
|
|
284
|
-
#text(weight: "bold")[Note.] Revenue grew 31% YoY.
|
|
285
|
-
]
|
|
286
|
-
#v(0.6em)
|
|
287
|
-
#block(fill: rgb("#fef2f2"), stroke: (left: 3pt + rgb("#ef4444")), inset: 12pt, radius: 4pt, width: 100%)[
|
|
288
|
-
#text(weight: "bold")[Risk.] A single supplier covers 40% of NPUs.
|
|
289
|
-
]
|
|
290
|
-
-->
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
**Two-column section** (use `#colbreak()` to split):
|
|
294
|
-
|
|
295
|
-
```markdown
|
|
296
|
-
<!--raw-typst
|
|
297
|
-
#columns(2, gutter: 1.4em)[
|
|
298
|
-
#text(weight: "bold")[Strengths]
|
|
299
|
-
- Net retention 124%
|
|
300
|
-
- Margin +240bps
|
|
301
|
-
#colbreak()
|
|
302
|
-
#text(weight: "bold")[Risks]
|
|
303
|
-
- Supplier concentration
|
|
304
|
-
- Partial FX hedging
|
|
305
|
-
]
|
|
306
|
-
-->
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
**Figure with caption** (swap the `rect(...)` for `image("chart.png")` to embed an
|
|
310
|
-
image written to `workspace/`):
|
|
311
|
-
|
|
312
|
-
```markdown
|
|
313
|
-
<!--raw-typst
|
|
314
|
-
#figure(
|
|
315
|
-
rect(width: 60%, height: 48pt, fill: luma(245), stroke: 0.5pt + luma(180)),
|
|
316
|
-
caption: [Revenue trend, Q1–Q2 2026.],
|
|
317
|
-
)
|
|
318
|
-
-->
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
**Definition grid** (label column + description column):
|
|
322
|
-
|
|
323
|
-
```markdown
|
|
324
|
-
<!--raw-typst
|
|
325
|
-
#grid(columns: (auto, 1fr), row-gutter: 6pt, column-gutter: 12pt,
|
|
326
|
-
text(weight: "bold")[NPU], [Neural processing unit — on-device inference accelerator.],
|
|
327
|
-
text(weight: "bold")[Net retention], [Revenue from existing customers vs. a year ago.],
|
|
328
|
-
)
|
|
329
|
-
-->
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
Keep it tasteful — a banner, a couple of callouts, and one good figure read as
|
|
333
|
-
deliberate; a wall of colored boxes reads as noise.
|
|
334
|
-
|
|
335
|
-
## Rendering an _existing_ web page or HTML to PDF
|
|
336
|
-
|
|
337
|
-
This skill renders **markdown you author**. If instead you need to capture an
|
|
338
|
-
**existing web page or a live URL** as a PDF — something Typst cannot do — use the
|
|
339
|
-
already-installed `agent-browser` (Chrome): `agent-browser --allow-file-access open
|
|
340
|
-
file:///agent/workspace/page.html` (or a URL), then `agent-browser pdf
|
|
341
|
-
/agent/workspace/out.pdf`. Note its output is fixed US-Letter with default margins
|
|
342
|
-
(no page-size flags), and launching the browser needs a trusted/owner session — so
|
|
343
|
-
it's the right tool for _archiving web content_, not for authoring styled reports.
|
|
344
|
-
For authored documents, stay on the Typst path above.
|
|
345
|
-
|
|
346
|
-
## Step 4 — deliver
|
|
347
|
-
|
|
348
|
-
- **Channel asked for the PDF** — attach it:
|
|
349
|
-
|
|
350
|
-
```
|
|
351
|
-
channel_send(text: "Here's the report.", attachments: [{ path: "/agent/workspace/report.pdf", filename: "Edge-AI-Brief.pdf" }])
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
Use a human-friendly `filename` and an absolute `/agent/workspace/...` path. Slack,
|
|
355
|
-
Discord, Telegram, and KakaoTalk upload the file; the GitHub adapter has no
|
|
356
|
-
attachment support, so there post a link or paste the markdown.
|
|
357
|
-
|
|
358
|
-
- **Replying in a thread** — use `channel_reply` with the same `attachments` shape.
|
|
359
|
-
|
|
360
|
-
- **No channel** (TUI session) — just report the path: `workspace/report.pdf`.
|
|
361
|
-
|
|
362
|
-
## If you got the markdown from a subagent
|
|
363
|
-
|
|
364
|
-
The `researcher` subagent writes its report to `workspace/research-<slug>.md` and
|
|
365
|
-
returns a `<report>` block naming the file. Point the wrapper's `read(...)` at that
|
|
366
|
-
file, render, and attach. You do the PDF step — the researcher's `bash` is
|
|
367
|
-
read-only and it only emits markdown by design.
|
|
368
|
-
|
|
369
|
-
## Customizing this skill
|
|
370
|
-
|
|
371
|
-
This is a bundled default. Want a different house style, a different converter, or
|
|
372
|
-
a cover page with a logo? Copy this file to `.agents/skills/<your-name>/SKILL.md`
|
|
373
|
-
(use a **different** `name`; bundled skills win name collisions) and edit the setup
|
|
374
|
-
or the wrapper there. Because the whole pipeline — install + render — lives in the
|
|
375
|
-
skill, you can change either half without touching the container image.
|
|
376
|
-
|
|
377
|
-
## Known limitations
|
|
378
|
-
|
|
379
|
-
`cmarker` covers CommonMark well, but a few markdown features don't render as you
|
|
380
|
-
might expect:
|
|
381
|
-
|
|
382
|
-
- **Task-list checkboxes** (`- [ ]` / `- [x]`) render as literal `[ ]` text, not
|
|
383
|
-
checkboxes. Use a plain bullet list or a status column in a table instead.
|
|
384
|
-
- **Bold/italic directly adjacent to CJK + parenthetical Latin** (e.g.
|
|
385
|
-
`**로컬 우선(local-first)**`) may not be recognized as emphasis — CommonMark's
|
|
386
|
-
flanking rules treat that boundary as non-emphasis. Put a space inside, or bold a
|
|
387
|
-
pure run of text.
|
|
388
|
-
- **Raw HTML** in the markdown is mostly ignored. Express structure in markdown
|
|
389
|
-
(tables, lists) rather than HTML.
|
|
390
|
-
|
|
391
|
-
## Don'ts
|
|
392
|
-
|
|
393
|
-
- **Don't** hand-write Typst markup for the body. Let `cmarker` render the
|
|
394
|
-
markdown; only style via `#set` / `#show` rules in the wrapper.
|
|
395
|
-
- **Don't** write the `.typ`, `.md`, `.pdf`, or `.tools/` outside `workspace/` —
|
|
396
|
-
the sandbox blocks it.
|
|
397
|
-
- **Don't** re-run Step 0's install if the tools already exist — the guard at the
|
|
398
|
-
top skips it. Re-installing every time is wasteful.
|
|
399
|
-
- **Don't** attach a PDF to a GitHub channel — that adapter rejects attachments.
|
|
400
|
-
Link or inline instead.
|