daybook/skills/daybook-posting/SKILL.md
peter 4b5ec09ae2 Convert uploaded .xlsx files to csv artifacts server-side
Uploading a .xlsx file no longer becomes a generic downloadable binary -
it's read server-side with openpyxl and converted to a csv artifact,
rendering in the same interactive grid as a native CSV post. Detected by
content type or .xlsx extension (some clients send generic/incorrect
content types for multipart uploads). Only the active sheet converts;
formula cells use whatever cached result the spreadsheet software last
saved (openpyxl doesn't evaluate formulas itself). Corrupt/unreadable
files return 400.

Since the existing posting script already forwards any non-text file via
multipart with no special-casing, `daybook_post.py balance_sheet.xlsx`
already works with zero script changes - verified live.

Two real bugs found and fixed while building this, both now covered by
regression tests:
- {**a_querydict, ...} silently wrapped every value (including title) in
  a list - Python's ** spread on a dict subclass bypasses QueryDict's
  __getitem__ override and exposes its internal one-list-per-key storage
  directly. Fixed by building the delegated dict explicitly via .get().
- Pre-setting None for absent optional fields (e.g. source_prompt) defeated
  create_text_artifact's own data.get(field, default) calls, since .get()
  only falls through to its default when the key is *missing*, not when
  the value is None - caused a NOT NULL constraint failure. Fixed by only
  setting keys that were actually provided.
- csv.writer's default "\r\n" terminator was getting doubled by
  Path.write_text()'s own newline translation on Windows, producing a
  blank line between every row. Fixed with lineterminator="\n".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 16:29:39 +01:00

146 lines
6.5 KiB
Markdown

---
name: daybook-posting
description: Use when you have produced a finished deliverable such as a report, summary, brief, diagram, generated HTML, PDF, deck, or status update that a human should review later outside this chat. Publishes the artifact to Daybook and returns a URL.
---
# Daybook Posting
Daybook is a private artifact journal for completed agent outputs. It stores, indexes, and displays artifacts for human review. It does not execute commands, run workflows, or continue conversations.
Always use the helper script. Do not hand-build curl, JSON, PowerShell multipart, or inline shell content.
From the project root:
```bash
python skills/daybook-posting/scripts/daybook_post.py --check
python skills/daybook-posting/scripts/daybook_post.py --kind markdown --title "Build Report" --content-file report.md
cat report.md | python skills/daybook-posting/scripts/daybook_post.py --kind markdown --title "Build Report"
python skills/daybook-posting/scripts/daybook_post.py --file diagram.svg --title "Architecture Diagram"
```
The project also keeps a compatibility wrapper at `scripts/daybook_post.py`, but the canonical helper is bundled inside this skill.
The artifact URL is printed as the final stdout line.
## Configuration
Set these as real process/shell environment variables (`export` on
Linux/macOS, `$env:` or `set` on Windows, or your agent harness's own env
config) — the helper reads `os.environ` directly. There is no `.env` file
support; a `.env` file in the working directory is not read automatically.
```text
DAYBOOK_URL=http://127.0.0.1:8001
DAYBOOK_TOKEN=your-personal-api-token
DAYBOOK_SOURCE=agent
```
`DAYBOOK_TOKEN` is your personal per-user API token, from Settings → Account in the app (not a token shared by everyone). Artifacts posted with your token land only in your feed.
## Kind Cheat Sheet
| Kind | Use for | Content shape |
|---|---|---|
| `markdown` | Reports, briefs, notes | Markdown text. Fenced code blocks get syntax highlighting. |
| `html_fragment` | Themed HTML reports/cards/tables | Body-only HTML. No `html/head/body`. |
| `html_full` | Complete custom pages/frameworks | Full HTML document with doctype/html/head/body. See "CDN Snippets" below. |
| `deck` | Simple slide-like artifact | Top-level `<section>...</section>` blocks. |
| `json` | Structured data, metrics, run summaries | Valid JSON text. Renders as a collapsible tree. |
| `csv` | Tabular data | CSV text, first row as header. Renders as a sortable/searchable table. |
For images, PDFs, SVGs, and other files, use `--file`; the helper infers multipart and content type.
## CDN Snippets For `html_full`
`html_full` is Daybook's escape hatch for custom pages — nothing is
injected into it, so a poster wanting Tailwind or a charting library has to
supply its own `<link>`/`<script>` tags. Known-good ones:
```html
<!-- Tailwind (Play CDN - compiles in-browser on every load; fine for a
one-off artifact, not something you'd want on a high-traffic page) -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
```
**Offline caveat:** Daybook can run fully self-hosted/air-gapped. Any
artifact that links a CDN script won't render fully (or at all, for
Tailwind's in-browser compiler) without internet access. If the deployment
target is offline, don't reach for these — use `markdown`/`html_fragment`
instead, which are styled entirely server-side with no external requests.
**Self-hosted alternative for tables/JSON — no CDN needed:** Daybook already
vendors Grid.js and renderjson locally (used to render `csv`/`json`
artifacts), and they're reachable as plain static files from any artifact,
including `html_full`:
```html
<link rel="stylesheet" href="/static/vendor/gridjs/mermaid.min.css">
<script src="/static/vendor/gridjs/gridjs.umd.js"></script>
<!-- or, for a collapsible JSON tree: -->
<script src="/static/vendor/renderjson/renderjson.js"></script>
```
Prefer these over an external CDN for table/JSON rendering inside a custom
`html_full` page — same libraries, but self-hosted, so they work offline
and don't depend on a third party staying up. This only covers
tables/JSON; there's no vendored equivalent for Tailwind/Chart.js, so the
CDN snippets above are still the only option for those.
Only use this for `html_full` specifically — never add CDN references to
`markdown`/`html_fragment`/`deck`. Those kinds exist so Daybook, not the
poster, owns styling; injecting a default CDN habit into them would
undermine that.
## Images Inside Content
To embed an image *within* a markdown or HTML artifact (rather than posting it as its own
artifact), upload it first and reference the returned URL:
```bash
# 1. Upload -> prints the URL as the last line
python skills/daybook-posting/scripts/daybook_post.py --upload-image chart.png
# 2. Reference that URL in your markdown, then post as usual
# ![chart](http://127.0.0.1:8001/assets/ab12cd34ef56)
python skills/daybook-posting/scripts/daybook_post.py report.md
```
The image is linked to the artifact that references it and is deleted along with it.
Unreferenced uploads are cleaned up after 24 hours.
For CSV and JSON, use `--kind csv`/`--kind json` with `--content-file` — or
just post the file positionally; auto-mode detects `.csv`/`.json` and picks
the right kind automatically:
```bash
python skills/daybook-posting/scripts/daybook_post.py results.csv
python skills/daybook-posting/scripts/daybook_post.py --kind json --title "Run Summary" --content-file summary.json
```
`.xlsx` spreadsheets work too, with no special flag needed — just `--file`
(or post it positionally, same as any other file):
```bash
python skills/daybook-posting/scripts/daybook_post.py balance_sheet.xlsx
```
The server converts it to a `csv` artifact automatically (active sheet
only; see `docs/api.md` for the full list of conversion limitations —
notably, formulas need to have been calculated by real spreadsheet software
that saved the file, and rich formatting like bold/colors/merged cells
isn't preserved).
For other code that should render today, use `--file path --as-markdown` to
post it as a Markdown fenced code block. Without `--as-markdown`, it uploads
as a downloadable binary file.
## Rules
- Run `--check` first if unsure Daybook is reachable.
- Use a stable `DAYBOOK_SOURCE` or pass `--source` (the agent-profile, script, or person name that produced this artifact).
- Use `--tags a,b,c` for filtering.
- Use `--meta run_id=... --meta model=...` for machine-readable context.
- Never inline multiline content into the shell command; use a file or stdin.