Content Shape (built-in)
WarmHub provides a single built-in Content shape for conventional repo markdown. Three well-known instance names are recognized:
| Name | Stored / Synthesized | Purpose |
|---|---|---|
Readme | Stored | Human-facing README |
Agents | Stored | AI-agent guidance (AGENTS.md) |
LlmsTxt | Synthesized | Sitemap per llmstxt.org |
The Content shape has a single field: content: string.
Fetch matrix
Section titled “Fetch matrix”Every surface supports the same three names with parallel verbs:
| Surface | Content/Readme | Content/Agents | Content/LlmsTxt |
|---|---|---|---|
| CLI | wh repo content {get,set,generate} --kind readme | wh repo content {get,set,generate} --kind agents | wh repo content get --kind llms-txt |
| SDK | client.repo.getReadme/setReadme/generateReadme | client.repo.getAgents/setAgents/generateAgents | client.repo.getLlmsTxt |
| MCP | warmhub_repo_content_{get,set,generate} (kind: readme) | warmhub_repo_content_{get,set,generate} (kind: agents) | warmhub_repo_content_get (kind: llms-txt) |
| Raw HTTP | GET /{org}/{repo}/readme.md | /{org}/{repo}/agents.md | /{org}/{repo}/llms.txt |
Empty-stub semantics
Section titled “Empty-stub semantics”Every read endpoint returns 200 with content — possibly empty. Until the first write, the response is a synthesized empty stub rather than an absent or null body:
{ "synthesized": true, "shape": "Content", "name": "Readme", "data": { "content": "" }, "active": true}After the first commit, subsequent reads return the stored content thing.
SDK note.
client.repo.getReadme()andclient.repo.getAgents()can returnnullby contract. In practice the backend returns the synthesized empty stub described above instead of null, but TypeScript callers should still null-check defensively to match the type contract.
Discovery via repo.describe
Section titled “Discovery via repo.describe”The repo describe aggregate (warmhub_repo_describe MCP tool) returns an additionalInformation array pointing at the three well-known wrefs:
"additionalInformation": [ { "name": "Readme", "wref": "Content/Readme", "synthesized": false }, { "name": "Agents", "wref": "Content/Agents", "synthesized": false }, { "name": "LlmsTxt", "wref": "Content/LlmsTxt", "synthesized": true }]Use this field to discover where conventional content lives in any repo without hardcoding wrefs.
Synthesized Content/LlmsTxt
Section titled “Synthesized Content/LlmsTxt”Content/LlmsTxt is rendered server-side per request from live repo data. It is read-only — writes are rejected (error code: READ_ONLY_BUILTIN_CONTENT).
The rendered body follows the llmstxt.org convention:
> repo description
## Shapes
- [ShapeName](Shape/ShapeName): description
## Things by shape (sample)
- [Shape/name](Shape/name)
## Outbound references — same org## Outbound references — cross-org## Inbound references — same org## Inbound references — cross-orgCross-org refs the caller cannot read are omitted entirely. Unauthenticated callers receive the basic body (H1, description, shapes) without the ref sections.
The refs field in the SDK response carries the structured reference data for authenticated callers:
const result = await client.repo.getLlmsTxt('org', 'repo')// result.data.content — rendered markdown body// result.refs?.outbound.sameOrg — same-org outbound refs (authenticated only)// result.refs?.outbound.crossOrg — cross-org outbound refs// result.refs?.inbound.sameOrg — same-org inbound refs// result.refs?.inbound.crossOrg — cross-org inbound refsCLI examples
Section titled “CLI examples”# Fetch Readmewh repo content get org/repo --kind readme
# Set Readme from a filewh repo content set org/repo --kind readme --file readme.md
# Set Readme inlinewh repo content set org/repo --kind readme --content '# My Repo'
# AI-generate a Readme draft (does not commit)wh repo content generate org/repo --kind readme
# AI-generate and commit in one stepwh repo content generate org/repo --kind readme --save
# Fetch AGENTS.md guidancewh repo content get org/repo --kind agents
# Set AGENTS.mdecho '# Agent Guide' | wh repo content set org/repo --kind agents
# Fetch synthesized llms.txtwh repo content get org/repo --kind llms-txtSDK examples
Section titled “SDK examples”import { WarmHubClient } from '@warmhub/sdk-ts'
const client = new WarmHubClient({ auth: { getToken: async () => process.env.WH_TOKEN } })
// Readconst readme = await client.repo.getReadme('acme', 'world')console.log(readme.data?.content)
const agents = await client.repo.getAgents('acme', 'world')const llmsTxt = await client.repo.getLlmsTxt('acme', 'world')
// Writeawait client.repo.setReadme('acme', 'world', '# World\n\nThis repo tracks game world state.')await client.repo.setAgents('acme', 'world', '# Agent Guide\n\nRead shapes before writing.')
// AI-generate (returns draft, does not commit)const { content } = await client.repo.generateReadme('acme', 'world')await client.repo.setReadme('acme', 'world', content)MCP examples
Section titled “MCP examples”// Fetch Readme{ "name": "warmhub_repo_content_get", "arguments": { "orgName": "acme", "repoName": "world", "kind": "readme" } }
// Set AGENTS.md{ "name": "warmhub_repo_content_set", "arguments": { "orgName": "acme", "repoName": "world", "kind": "agents", "content": "# Agent Guide" } }
// Fetch synthesized llms.txt{ "name": "warmhub_repo_content_get", "arguments": { "orgName": "acme", "repoName": "world", "kind": "llms-txt" } }Preflight gates
Section titled “Preflight gates”The commit pipeline enforces a closed set at write time:
- Writing
Content/LlmsTxtis rejected — it is synthesized and cannot be stored (error code:READ_ONLY_BUILTIN_CONTENT). - Writing
Content/<anything-else>(outside the three well-known names) is rejected (error code:UNKNOWN_CONTENT_NAME). - Writing
Content/ReadmeorContent/Agentsis allowed to any authenticated user withthings:writescope. Content/ReadmeandContent/Agentsvalues are limited to 64 KiB (65,536UTF-8 bytes).
Hit a problem or have a question? Get in touch.