# Publishing to sites.tushar.ai sites.tushar.ai lists things Tushar's agents make. It is built from the private GitHub repo `triptu/sites`, branch `main`. Every change to `main` is deployed within about a minute. An artifact is one folder: ``` artifacts//artifact.json always artifacts//index.html unless it is a link artifact artifacts//… images, data and scripts the page uses, by relative path ``` It is served at `https://sites.tushar.ai//`. ## 1. Pick a slug Lowercase kebab-case, short and descriptive, e.g. `effect-schema-explained`. It must not be taken. List the existing ones, including half-uploaded folders: - shell: `gh api repos/triptu/sites/contents/artifacts --jq '.[].name'` - GitHub MCP: `get_file_contents` with owner `triptu`, repo `sites`, path `artifacts` A few names are reserved for the site's own pages and files; `bun run check` and CI report these. ## 2. Write `index.html` The artifact is standalone, so give it whatever look suits the content. - One page that works when opened as a local file, with no build step. Put CSS and JS inline or next to the page with relative paths. - Make it readable on a phone, and usable in both light and dark. - The site's optional style kit provides its type, colours, light and dark themes, a theme toggle and chart colours: https://sites.tushar.ai/kit/kit.md (humans: https://sites.tushar.ai/kit/). - Don't include secrets, tokens or private data. The site is public. - Keep the folder under 10 MB. Host videos and large media elsewhere. On deploy the site adds one small script to every artifact for the ⌘K command bar (search, go home, theme); it is invisible until ⌘K is pressed. It also adds a share image and link-preview tags unless the page sets its own `og:image`, `og:title` or `og:description`, and a `` to the site's RSS feed unless the page names its own feed. ## 3. Write `artifact.json` ```json { "title": "Effect Schema, explained with diagrams", "description": "Decode, encode, transforms and branded types, one picture each.", "labels": ["learning"], "date": "2026-10-11T17:52Z", "agent": "claude-code", "model": "claude-opus-5-5" } ``` | field | rule | | ------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `title` | 1 to 80 characters. Plain and specific. | | `description` | 1 to 160 characters. One sentence saying what is inside. | | `labels` | One or more of the labels below, with no repeats. | | `date` | Now in UTC, as `YYYY-MM-DDTHH:MMZ` (`date -u +%Y-%m-%dT%H:%MZ`). The time orders artifacts from the same day. `YYYY-MM-DD` alone is also valid. | | `agent` | Who made it, up to 40 characters: `claude-code`, `codex`, `chatgpt`, … | | `model` | Optional, up to 80 characters. The exact model ID you run as, e.g. `claude-opus-5-5`. Leave it out if you don't know it; don't guess. The site doesn't show it. | | `updated` | Optional, in the same format as `date`. Set it when changing an existing artifact. | | `url` | Optional `https://…`. Makes a link artifact: there is no `index.html`, and the list links straight to the URL. Use it for videos hosted elsewhere. | Labels: `research` for reports and comparisons, `learning` for explainers and guides, `plan` for plans and designs, `tool` for something interactive, `video` for a video or a page built around videos. ## 4. Publish Use the first option available to you. Commit `artifact.json` together with the page or last: a folder without it is ignored, so a half-written artifact never appears. ### A. Shell with `git`, `gh` and `bun`: push to `main` ```sh dir=$(mktemp -d) && gh repo clone triptu/sites "$dir" -- --depth 1 && cd "$dir" # add artifacts//… bun install --frozen-lockfile && bun run check # commit as Tushar's GitHub account, or Vercel refuses to deploy git config user.email "$(gh api user --jq '"\(.id)+\(.login)@users.noreply.github.com"')" git add artifacts/ && git commit -m "add " && git push # if the push is rejected: git pull --rebase --autostash && git push ``` ### B. GitHub MCP server: open a PR 1. `create_branch`: owner `triptu`, repo `sites`, branch `publish/`, from_branch `main`. 2. `push_files`: same owner, repo and branch, message `add `, `files` = every file as `{ path: "artifacts//…", content: "" }`. This makes one commit. 3. `create_pull_request`: head `publish/`, base `main`, title `add `, body = the artifact's description. `content` is plain text, so only text files work (HTML, CSS, JS, JSON, SVG). Inline images as SVG or `data:` URIs, or host them elsewhere. ### C. `gh` without cloning: open a PR ```sh slug=; branch="publish/$slug" base=$(gh api repos/triptu/sites/git/ref/heads/main --jq .object.sha) gh api repos/triptu/sites/git/refs -f ref="refs/heads/$branch" -f sha="$base" put() { # put : body via stdin, since big files don't fit in an argument jq -n --arg m "add $slug" --arg b "$branch" --rawfile c <(base64 < "$1" | tr -d '\n') \ '{message: $m, branch: $b, content: $c}' | gh api "repos/triptu/sites/contents/$2" -X PUT --input - } put index.html "artifacts/$slug/index.html" put artifact.json "artifacts/$slug/artifact.json" # last gh pr create --repo triptu/sites --head "$branch" --base main --title "add $slug" --body "" ``` ### What happens to the PR CI validates the changed artifacts and builds the site. If the PR only touches `artifacts/` and every check passes, it is merged and deployed automatically. If a check fails, read the failing step (`gh pr checks `, or the PR's status over MCP), fix it on the same branch, and CI runs again. A PR that changes anything outside `artifacts/` waits for Tushar. Commits are made as the account behind your token. Vercel only deploys Tushar's commits, so a bot token won't work. ## 5. Confirm After about a minute, `https://sites.tushar.ai/index.json` should include your slug. If it doesn't, `https://sites.tushar.ai/_errors.json` says what is wrong. Then give Tushar the URL. ## Rules - Touch only `artifacts//`. Don't edit other artifacts or the site's code. - Change an existing artifact only when Tushar asks, and set `updated`. Over the API or MCP, updating a file needs its `sha`, from a GET of the same path on the branch. - An invalid artifact is left off the list. It doesn't break the site or other artifacts.