anchorify

Skill version 0.3.0 · Requires anchorify CLI ≥ 0.3.0. Check yours with anchorify --version; upgrade with the procedure in Upgrading below.

Wrap the anchorify CLI to publish or update a file at a stable public URL on anchorify.io. Supports markdown, code, JSON, YAML, CSV, TSV, HTML, and slide decks (Marp + reveal.js).

Install or refresh

Preferred — the CLI installs the skill for you:

anchorify skill install

This writes ~/.claude/skills/anchorify/SKILL.md from the canonical doc, creating directories as needed. Re-run any time to update; pass --force to overwrite without prompting when the local file has been modified.

Manual fallback (if you don't yet have the CLI or want to inspect the file before saving):

mkdir -p ~/.claude/skills/anchorify
curl -fsSL https://anchorify.io/docs/anchorify-skill.md \
  > ~/.claude/skills/anchorify/SKILL.md

The local copy is a snapshot — re-run anchorify skill install when the version above (top of this page) is higher than the one in your local SKILL.md.

Migrating from the old publish-md or justforwarded skill? Move the folder: mv ~/.claude/skills/justforwarded ~/.claude/skills/anchorify (or from publish-md) and re-fetch from the URL above. The old /docs/publish-md-skill.md and /docs/justforwarded-skill.md URLs still 308-redirect to this page, so any existing installation keeps working.

Upgrading the CLI and skill

Trigger: when the user asks any of "upgrade anchorify", "update the CLI", "is there a newer version", "you're on an old version", "refresh the skill", "is my skill out of date" — OR when a publish / lint call fails with error: 4xx mentioning an unsupported field / content type that newer CLIs handle.

Procedure (run these on the user's behalf, in order):

  1. Read the installed CLI version:

    anchorify --version
    

    If the command isn't found, the CLI isn't installed — skip to step 3.

  2. Read the latest published version:

    npm view anchorify-cli version
    

    No auth required; this hits the public npm registry. The npm package is anchorify-cli (the bare anchorify name was already taken by an unrelated utility); the binary it installs is named anchorify.

  3. If installed < latest (or CLI is missing), upgrade and refresh the skill in one go:

    npm install -g anchorify-cli@latest && anchorify skill install --force
    

    --force overwrites the local SKILL.md without prompting — the user is explicitly asking for a refresh, so it's safe.

  4. Confirm the new versions:

    anchorify --version
    head -3 ~/.claude/skills/anchorify/SKILL.md
    
  5. Report the before → after versions to the user. If the upgrade crossed a major version, point them at the release notes / git log for what changed.

Skill-only refresh (the CLI is current but the local SKILL.md is stale): the third command alone is enough:

anchorify skill install --force

The skill version is shown both in this page's title and in the frontmatter (version: key at the top of the raw markdown). When the canonical page shows a higher version than the local copy, refresh.

Prerequisites

The user needs the anchorify CLI installed and authenticated.

If anchorify is missing, tell them to install it (one-time setup) and run anchorify login. The login flow asks for a host (https://anchorify.io) and a token they copy from https://anchorify.io/dashboard.

If a publish call returns error: 401, the user is not logged in — tell them to run anchorify login.

Config & environment

Two env vars change where the CLI reads/writes config and which host it hits. Both exist mainly for sandboxed testing — sub-agents and CI runs should set them to avoid accidentally publishing to production with a developer's logged-in token.

  • ANCHORIFY_CONFIG_DIR — overrides the config directory (default ~/.config/anchorify). Redirects both auth.json and published.json to the given path. Use this to fully sandbox a test:

    ANCHORIFY_CONFIG_DIR=/tmp/anchorify-sandbox anchorify login \
      --host http://localhost:3737 --token <local-token>
    ANCHORIFY_CONFIG_DIR=/tmp/anchorify-sandbox anchorify --list
    

    Nothing in the user's real ~/.config/anchorify/ is touched. The legacy JF_CONFIG_DIR is still honored as a fallback so older test scripts keep working.

  • ANCHORIFY_HOST + ANCHORIFY_TOKEN — when both are set, they override auth.json (and a one-line [anchorify] using ANCHORIFY_HOST/TOKEN from env (auth.json ignored) notice is printed to stderr if auth.json exists, so the override is visible). Setting only one of them falls back to auth.json — partial env doesn't trigger the override, since mixing an env host with a file token (or vice versa) would produce a confusing token-mismatch. The legacy REPO_SHARE_HOST + REPO_SHARE_TOKEN pair is still honored when the ANCHORIFY_* pair isn't set.

How URLs work

Per-user URLs are https://anchorify.io/<username>/<slug> — e.g. https://anchorify.io/alice/q1-report. The username prefix is fixed per token; the slug is what you pick.

The CLI maintains a local mapping at ~/.config/anchorify/published.json (absolute file path → {id, url, filename, last_published_at}). You do not have to remember slugs across sessions — read the mapping or run anchorify --list.

Publishing a new file (file path NOT in the mapping)

  1. List existing slugs so you don't pick a taken one:

    anchorify --list
    

    Output: one tab-separated line per published item — <url>\t<filename>\t<source-path>. Scope is the signed-in user.

  2. Lint the file before publishing so you catch authoring issues (slide-deck frontmatter that won't be detected, JSON parse errors, CSVs with the wrong delimiter, etc.):

    anchorify lint <absolute-path>
    
    • Exit 0 = clean (or only info/warn-severity notes).
    • Exit 1 = at least one error-severity issue; the file WILL look broken to recipients. Surface the warning to the user and either fix the file or confirm they want to publish anyway.
  3. Pick a slug. Rules:

    • Lowercase letters, digits, dashes only
    • 3–40 chars is the sweet spot (60 max)
    • No leading/trailing dash
    • Derived from the file's H1 title or filename, NOT generic ("notes", "doc")
    • Distinct from every existing slug in --list

    Examples: q1-report, cardinal-heating-audit, geology-week3.

  4. Publish:

    anchorify <absolute-path> --slug <slug>
    

    Output is a single line — the public URL. Return that URL to the user, nothing else around it. If the publish response carried any authoring warnings, the CLI prints them to stderr; surface those to the user too.

  5. If you get error: 409 {"error":"slug taken",...}, pick a different slug and retry.

Updating an existing publish

If the file's absolute path is in the local mapping (or the user says "update"), just run with no flags. The CLI looks up the file and updates the same URL:

anchorify <absolute-path>

URL is unchanged; content is replaced. Return the URL.

To update by id explicitly (e.g. "push v2 to hello-world"), pass --id <id>.

Other operations

Delete a share:

anchorify delete <absolute-path>
# or by slug:
anchorify delete --slug <slug>

The local mapping entry is removed when deleting via path. Deleted URLs return 410 Gone (recipients see the link existed but is gone).

Password-protect a share:

# At publish time:
anchorify <abs-path> --slug <slug> --password <pw>
# Clear an existing password:
anchorify <abs-path> --no-password

Recipients see a password gate; only the correct password renders content. View counts only increment on actual content render.

Visibility (public / unlisted / members):

Every share has one of three tiers. By default a new share is unlisted — it renders for anyone with the URL, but the page sets noindex so search engines and AI crawlers don't pick it up. (The pre-V3 value secret is the legacy alias for unlisted and is still accepted everywhere.) Other tiers:

  • public — listed on the owner's profile and indexable.
  • members — only signed-in org admins + project viewers can read; anon → 404.

Set the tier at publish time:

# Create as public:
anchorify <abs-path> --slug <slug> --public
# Force unlisted (default; useful as an explicit flip on update):
anchorify <abs-path> --unlisted
# Members-only (signed-in org/project viewers only):
anchorify <abs-path> --members

Updates without a visibility flag preserve the existing tier. The flags are mutually exclusive.

Flip an existing share's visibility without re-publishing:

anchorify visibility <slug-or-id> public
anchorify visibility <slug-or-id> unlisted
anchorify visibility <slug-or-id> members

Resolves <slug-or-id> against --list (matches by id, slug, or URL suffix). If an unlisted share has a password and you flip it to public, the server refuses to silently clear the password — re-run with --force to clear the password and make it public:

anchorify visibility <slug-or-id> public --force

Force a brand-new URL even if the file is in the mapping:

anchorify <abs-path> --new

Content type detection

Anchorify renders shares differently depending on their content type — markdown via marked, code with highlight.js, CSV/TSV as a sortable table, JSON/YAML pretty-printed and highlighted, HTML sanitized and rendered.

The CLI does not classify the file itself. It just sends the filename + content; the server picks a renderer:

  1. Auto-detection from file extension (default). The server maps common extensions:
    • .md, .markdown → markdown
    • .json → json
    • .yaml, .yml → yaml
    • .csv → csv
    • .tsv → tsv
    • .html, .htm → html
    • .js, .ts, .py, .go, .rs, .rb, .sh, .sql, etc. → code (with language inferred for syntax highlighting)
    • Anything else → markdown
  2. Explicit --type override for ambiguous extensions. Pass one of: markdown, code, json, yaml, csv, tsv, html.
# Auto-detect from extension — no flag needed:
anchorify data.csv --slug q1-numbers          # renders as a table
anchorify settings.yaml --slug prod-config    # yaml-highlighted
anchorify snippet.py --slug pyrun             # python-highlighted

# Override when the extension lies (e.g. a .txt file that is actually code):
anchorify weird.txt --slug snippet --type code
anchorify notes.txt --slug brief --type markdown

--type is validated client-side against the allowed set, so you get an immediate error for typos rather than a server round-trip. Updates to an existing share that don't pass --type keep whatever type the share was created with (no silent reclassification).

Fix an existing share's render type without re-publishing:

anchorify type <slug-or-id> markdown
anchorify type <slug-or-id> code
anchorify type <slug-or-id> json

Resolves <slug-or-id> against --list (matches by id, slug, or URL suffix). Use this to recover from the paste-flow gotcha where leaving Filename blank in the web "+ New share" form silently classifies the share as markdown — Python / JSON / CSV pastes will render wrong until the type is flipped.

Collaboration subcommands

The CLI surfaces five collaboration features. Each prints tab-separated rows on success so a parent agent or jq-pipe can parse the output.

notifications — list your in-app notifications

anchorify notifications              # all, newest 50
anchorify notifications --unread     # unread only
anchorify notifications --limit 10   # cap at 10

Output: <id>\t<kind>\t<created_at_iso>\t<title>\t<link>.

Use this when the user asks "what's new", "any new comments on my shares", or "did anyone request access". See /docs/notifications for the full set of notification kinds.

link-perm — set the anyone-with-the-link permission

anchorify link-perm <slug-or-id> none
anchorify link-perm <slug-or-id> can_view
anchorify link-perm <slug-or-id> can_comment
anchorify link-perm <slug-or-id> can_suggest

Sets the additive grant attached to the URL. none (default) = read-only. can_comment = anonymous URL-holders can post comments + reactions. can_suggest = URL-holders can submit suggested changes. See /docs/link-permissions for the full matrix.

versions — list and restore a share's version history

# List versions, newest first:
anchorify versions <slug-or-id>
# Output: <version-id>\t<created_at_iso>\t<source>\t<author>\t<status>

# Restore the share to an older version:
anchorify versions restore <slug-or-id> <version-id>

source is one of publish / rollback / suggestion / agent_edit. status is approved / suggested / rejected. The restore appends a new rollback row to the history (so even the rollback is versioned) and prints ok on success.

audit — list audit events for an org

anchorify audit                              # your home org, 50 events
anchorify audit --org <slug>                 # explicit org
anchorify audit --limit 200
anchorify audit --action share.delete        # filter by action

Output: <created_at_iso>\t<action>\t<actor>\t<target_type>:<target_id>.

The --action filter matches the dotted action keys recorded by the service — see /docs/audit for the catalog.

suggestions — list suggestions submitted on your shares or by you

# Open suggestions on a specific share you own (owner-only):
anchorify suggestions <slug-or-id>

# Every suggestion YOU have submitted, across every share:
anchorify suggestions mine

The mine form output: <version-id>\t<share-url>\t<status>\t<created_at_iso>.

Approve/reject from the web UI — the CLI lists only.

report-bug — file a bug from the CLI or an agent

# Bare summary — opens with empty body. Anonymous is OK (rate-limited
# per IP); when logged in, the report carries your user id + org.
anchorify report-bug "CLI hung on publish"

# Inline body + agent breadcrumbs.
anchorify report-bug "Editor save 500'd" \
  --body "Saving from /editor returned 500; retry succeeded." \
  --context '{"slug":"q1-report","last_http_status":500}'

# Body from a file (useful for long repro steps):
anchorify report-bug "Render crash on big csv" \
  --file ~/jf-bug-repro.md \
  --surface dashboard

Flags:

  • --body <text> — inline long-form body. Mutually exclusive with --file.
  • --file <path> — read body from a file. Helpful for paste-from-editor.
  • --context '<json>' — free-form JSON breadcrumbs (slug, last HTTP status, recent error message). Must parse to an object.
  • --surface cli|api|agent|dashboard|vscode|raycast|obsidian — which surface filed the report. Defaults to cli.

On success prints the report id on stdout. Every error response from /api/v1/* also includes a report_with hint with the exact CLI shape an agent can chain inline.

Notes & failure modes

  • File path must be absolute — resolve ~ and relative paths first.
  • The CLI prints only the URL on success. On failure, error: <code> <body> to stderr.
  • 401 = run anchorify login (auth missing or token revoked)
  • 409 = slug collision; pick a different slug and retry
  • 400 = invalid slug format (re-read the slug rules above)
  • 404 on delete = slug doesn't belong to the signed-in user
  • The returned URL is publicly readable unless a password is set. Warn the user before publishing if the file might contain credentials, client names, or anything they wouldn't want a stranger to see.
  • The local mapping at ~/.config/anchorify/published.json is plain JSON; read/inspect it directly when debugging.