feat: add SKILL documentation for Gitea issue attachments, tibi hook authoring, and SSR caching

This commit is contained in:
2026-02-25 21:18:12 +00:00
parent 3c3e70b474
commit 30501f5f4c
3 changed files with 151 additions and 0 deletions

View File

@@ -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})
```

View File

@@ -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.

View File

@@ -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.