diff --git a/.agents/skills/gitea-issue-attachments/SKILL.md b/.agents/skills/gitea-issue-attachments/SKILL.md new file mode 100644 index 0000000..a9f6da5 --- /dev/null +++ b/.agents/skills/gitea-issue-attachments/SKILL.md @@ -0,0 +1,25 @@ +--- +name: gitea-issue-attachments +description: Upload files (screenshots, logs, etc.) to Gitea issues as attachments via the REST API. Use when attaching any file to a Gitea issue or comment. +--- + +# Gitea Issue Attachments + +Attach files to Gitea issues via the REST API: + +1. Get the Gitea API token from the running MCP docker process: + ```bash + GITEA_PID=$(ps aux | grep 'gitea-mcp-server' | grep -v grep | awk '{print $2}') + GITEA_TOKEN=$(cat /proc/$GITEA_PID/environ | tr '\0' '\n' | grep GITEA_ACCESS_TOKEN | cut -d= -f2) + ``` +2. Upload the file as an issue attachment: + ```bash + curl -s -X POST "https://gitbase.de/api/v1/repos/{owner}/{repo}/issues/{index}/assets" \ + -H "Authorization: token $GITEA_TOKEN" \ + -F "attachment=@path/to/file" + ``` + This returns JSON with a `uuid` field. +3. Reference the attachment in the issue or comment body: + ```markdown + ![Description](attachments/{uuid}) + ``` diff --git a/.agents/skills/tibi-hook-authoring/SKILL.md b/.agents/skills/tibi-hook-authoring/SKILL.md new file mode 100644 index 0000000..3cbedb9 --- /dev/null +++ b/.agents/skills/tibi-hook-authoring/SKILL.md @@ -0,0 +1,71 @@ +--- +name: tibi-hook-authoring +description: Write and debug server-side hooks for tibi-server (goja Go JS runtime). Covers IIFE structure, HookResponse/HookException types, context.filter Go-object quirk, single-item vs list retrieval, and MongoDB filter patterns. Use when creating or modifying files in api/hooks/. +--- + +# tibi-hook-authoring + +## Hook file structure + +Wrap every hook in an IIFE: + +```js +;(function () { + /** @type {HookResponse} */ + const response = { status: 200 } + + // ... hook logic ... + + return response +})() +``` + +Always return a `HookResponse` or throw a `HookException`. + +## Type safety + +- Use inline JSDoc type casting: `/** @type {TypeName} */ (value)`. +- Reference typed collection entries from `types/global.d.ts`. +- Avoid `@ts-ignore`; use proper casting instead. +- Use `const` and `let` instead of `var` — the goja runtime supports them. + +## context.filter — Go object quirk + +`context.filter` is a Go object, not a regular JS object. Even when empty, it is **truthy**. + +Always check with `Object.keys()`: + +```js +const requestedFilter = + context.filter && + typeof context.filter === "object" && + !Array.isArray(context.filter) && + Object.keys(context.filter).length > 0 + ? context.filter + : null +``` + +**Never** use `context.filter || null` — it is always truthy and produces an empty filter inside `$and`, which crashes the Go server. + +## Single-item vs. list retrieval + +For `GET /:collection/:id`, the Go server sets `_id` automatically from the URL parameter. + +GET read hooks should **not** set their own `_id` filter for `req.param("id")`. Only add authorization filters (e.g. `{ userId: userId }`). + +## HookResponse fields (GET hooks) + +| Field | Purpose | +| ------------- | ------------------------------------------------------------------- | +| `filter` | MongoDB filter (list retrieval, or restrict single-item) | +| `selector` | MongoDB projection (`{ field: 0 }` exclude, `{ field: 1 }` include) | +| `offset` | Pagination offset | +| `limit` | Pagination limit | +| `sort` | Sort specification | +| `pipelineMod` | Function to manipulate the aggregation pipeline | + +## context.data for write hooks + +- `context.data` can be an array for bulk operations — always guard with `!Array.isArray(context.data)`. +- For POST hooks, `context.data.id` may contain the new entry ID. +- For PUT/PATCH, `req.param("id")` gives the entry ID. diff --git a/.agents/skills/tibi-ssr-caching/SKILL.md b/.agents/skills/tibi-ssr-caching/SKILL.md new file mode 100644 index 0000000..ff7a917 --- /dev/null +++ b/.agents/skills/tibi-ssr-caching/SKILL.md @@ -0,0 +1,55 @@ +--- +name: tibi-ssr-caching +description: Implement and debug server-side rendering with goja (Go JS runtime) and dependency-based HTML cache invalidation for tibi-server. Use when working on SSR hooks, cache clearing, or the server-side Svelte rendering pipeline. +--- + +# tibi-ssr-caching + +## SSR request flow + +1. `ssr/get_read.js` receives a page request and calls `lib/ssr-server.js`. +2. `ssr-server.js` loads `lib/app.server.js` (the Svelte SSR bundle) and renders the page. +3. During rendering, API calls are tracked as **dependencies** (collection + entry ID). +4. The rendered HTML + dependencies are stored in the `ssr` collection. +5. On the client, `lib/ssr.js` hydrates using `window.__SSR_CACHE__` injected by the server. + +## Building the SSR bundle + +```bash +yarn build:server +``` + +- Output: `api/hooks/lib/app.server.js` +- Uses `babel.config.server.json` to transform async/await to generators (goja doesn't support async). +- Add `--banner:js='// @ts-nocheck'` to suppress type errors in the generated bundle. + +## Dependency-based cache invalidation + +When content changes, `clear_cache.js` only invalidates SSR entries that depend on the changed collection/entry: + +```js +// Each SSR cache entry stores its dependencies: +{ + url: "/some-page", + html: "...", + dependencies: [ + { collection: "content", id: "abc123" }, + { collection: "medialib", id: "def456" } + ] +} +``` + +The hook queries the `ssr` collection for entries whose `dependencies` array matches the changed collection (and optionally entry ID), then deletes only those cached pages. + +## SSR route validation + +Route validation in `config.js` controls which paths get SSR treatment. Return: + +- A positive number to enable SSR for that route +- `-1` to disable SSR (current default in the starter template) + +## Common pitfalls + +- **goja has no async/await**: The babel server config transforms these, but avoid top-level await. +- **No browser globals**: `window`, `document`, `localStorage` etc. don't exist in goja. Guard with `typeof window !== "undefined"`. +- **SSR cache can go stale**: Always ensure `clear_cache.js` covers any new collection that affects rendered output.