Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Thank you for helping improve the Hive Commons documentation site
([docs.hivecommons.dev](https://docs.hivecommons.dev)).

- Hive, hotshot, pluk, rationguard, promptargs, and Spektacular page content is
- Hive, hotshot, pluk, rationguard, promptargs, dibs, and Spektacular page content is
synced from the project repositories at build time — edit it there (see
README "How content is sourced").
- Site code, navigation, and layout changes belong in this repository.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@ Official documentation site for [Hive Commons](https://hivecommons.dev), served
- **[pluk](https://github.com/hivecommons/pluk)** — pub-sub event streaming for AI agent tmux sessions
- **[rationguard](https://github.com/hivecommons/rationguard)** — detect and rebut rationalization patterns in AI agent output
- **[promptargs](https://github.com/hivecommons/promptargs)** — template expansion for AI prompts
- **[dibs](https://github.com/hivecommons/dibs)** — claim ideas so contributors get credit when agents implement them
- **[Spektacular](https://github.com/hivecommons/spektacular)** — spec-driven development for AI coding agents (canonical site: [spektacular.dev](https://spektacular.dev))

## How content is sourced

Hive, hotshot, pluk, rationguard, promptargs, and Spektacular docs are
Hive, hotshot, pluk, rationguard, promptargs, dibs, and Spektacular docs are
**single-sourced from their repositories** and pulled at build time:

- `scripts/sync-hive-docs.ts` pulls Hive docs from `hivecommons/hive` (`src/docs/`, branch `v5`;
override with `HIVE_DOCS_OWNER` / `HIVE_DOCS_REPO` / `HIVE_DOCS_REF`).
- `scripts/sync-sibling-docs.ts` pulls the hotshot, pluk, rationguard, promptargs, and Spektacular docs from their repos.
- `scripts/sync-sibling-docs.ts` pulls the hotshot, pluk, rationguard, promptargs, dibs, and Spektacular docs from their repos.
Spektacular also pulls its tutorials from `hivecommons/spektacular-website` (MDX, converted to Markdown by
`scripts/mdx-to-markdown.ts`; override the ref with `SPEKTACULAR_WEBSITE_DOCS_REF`).

Expand Down
199 changes: 199 additions & 0 deletions docs/content/dibs/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
> **Synced from dibs.** This page is pulled from [hivecommons/dibs@main](https://github.com/hivecommons/dibs/blob/main/README.md) during the docs build. Edit the canonical source in the dibs repository.

# Dibs — a marketplace of ideas

> **Your idea, your credit, their code.**
>
> The missing contributor layer for the AI-agent era.

Dibs is a two-sided marketplace that connects **ideators** — people who call
dibs on ideas: great insight, but no time, tooling, or inclination to write
code — with **hive-managed repositories** whose AI agent swarms are hungry
for well-formed work.

1. **Ideators post ideas** — public or private — in plain language / markdown.
2. **Repos opt in** to receive ideas, declaring topics and appetite.
3. **An LLM matches** ideas to repos that want them; both sides must say yes.
4. On **mutual acceptance**, Dibs hands the ideator a **prefilled GitHub
issue** — they file it themselves, under their own GitHub account, so the
issue is natively attributed to them (label `ideated`). The hive's AI
agents implement it. Dibs is just the matchmaker: it never files the
issue for you.

## Ideators are first-class contributors

Open source has always had a quiet contributor class with no outlet:
researchers, operators, designers, power users — people who know exactly what
a project needs but will never open a pull request. Open source has also
always known ideas are contributions — the all-contributors spec has a 💡
"ideas" emoji, and the academic CRediT taxonomy credits "Conceptualization"
as a first-class role. Dibs makes that credit real, machine-readable, and
public.

**Creators get credit. Agents do the work. Projects take the bow.**

Every implemented idea traces back to its ideator: the GitHub issue credits
the idea's author, the resulting work carries that provenance, and the public
**credit wall** (`/api/credits`, "Credit wall" in the UI) lists every ideator
whose idea shipped. An idea is a contribution.

## Architecture

Single Go binary (`cmd/dibs`) serving both the JSON API and an embedded
static UI, deployed at its own subdomain: **dibs.hivecommons.dev**. The base
path is configurable (`DIBS_BASE_PATH`), so path-prefixed reverse-proxy
deployments also work.

| Package | Purpose |
|---|---|
| `pkg/server` | HTTP server, base-path routing, embedded UI |
| `pkg/auth` | Auth bridge — validates the hive hub session cookie against the hub |
| `pkg/store` | Idea model + JSON file store (atomic writes, no external DB), offer/settlement state machine |
| `pkg/api` | Idea CRUD + matching/offer/feed/decide/notification API (author-scoped, private ideas never leak) |
| `pkg/registry` | Hive-managed repo profiles + "accepting ideas" opt-in |
| `pkg/match` | LLM idea↔repo scoring via litellm gateway, cached TLDRs, "✨ Embellish" draft refinement, deterministic keyword fallback |
| `pkg/settle` | Settlement — prefilled GitHub new-issue URL the ideator files themselves (`ideated` label, 🐝 Dibs footer, URL-length budget) + issue-URL confirmation; legacy token mode. Ideas can target ANY GitHub repo: non-hive targets skip acceptance and their issues carry a "request a hive" growth CTA |
| `pkg/notify` | In-app notification feed (bell): matches, offers, decisions, issues |

### Configuration

| Env var | Default | Meaning |
|---|---|---|
| `DIBS_BASE_PATH` | `/` | Base path every route/asset is served under (set e.g. `/ideas` for path-prefixed proxying) |
| `DIBS_ADDR` | `:8080` | Listen address |
| `HUB_URL` | `https://hive.hivecommons.dev` | Hive hub for session validation & repo sync |
| `DATA_DIR` | `/data` | JSON store directory |
| `REPOS_SEED_FILE` | (unset) | Static JSON seed of repo profiles for dev/demo |
| `DIBS_LLM_BASE_URL` | (unset) | OpenAI-compatible gateway (hive litellm), e.g. `http://litellm:4000/v1`. Unset → deterministic keyword matcher |
| `DIBS_LLM_API_KEY` | (unset) | Bearer token for the LLM gateway |
| `DIBS_LLM_MODEL` | `gpt-4o-mini` | Model name routed by the gateway |
| `DIBS_GITHUB_TOKEN` | (unset) | **Legacy mode only.** When set, Dibs opens the credited issue server-side on accept. Unset (default), the ideator files a prefilled GitHub issue themselves — native attribution |

Legacy `IDEATE_*` names (the pre-rename prefix) are still honored as
fallbacks for every `DIBS_*` variable.

### Roadmap

- **Wave 1 (done):** scaffold, hub auth bridge, idea CRUD, repo registry.
- **Wave 2 (done):** LLM matching + TLDRs, swipe UX ("offer" / "pass"), issue settlement (credited GitHub issues), notifications.
- **Wave 3 (done):** deployment (GHCR image + [`deploy/` manifests](https://github.com/hivecommons/dibs/blob/main/deploy/README.md) for dibs.hivecommons.dev), public landing page, public credit wall, ideator profile stats.
- **Wave 4 (done):** matchmaker settlement — the ideator files a prefilled GitHub issue themselves (native attribution, `ideated` label) and confirms the issue URL; LLM-assisted idea refinement on posting and repo-tailored expansion on submission; token-based settlement demoted to legacy.
- **Later:** cookie-domain/hub-OAuth session sharing, non-hive receptor repos, idea economics.

## Deployment

Every push to `main` publishes `ghcr.io/hivecommons/dibs` (`latest` +
commit sha) via `.github/workflows/docker.yml`; the image is distroless and
runs as non-root. Plain Kubernetes manifests for **dibs.hivecommons.dev**
(Deployment, Service, cert-manager TLS Ingress, PVC, ConfigMap/Secret) live
in [`deploy/`](https://github.com/hivecommons/dibs/blob/main/deploy/README.md), including the required DNS record and
hub-cookie prerequisites.

## Public surface (no sign-in required)

| Route | Purpose |
|---|---|
| `GET /` | Landing page (pitch + sign-in) — signed-in users get the full app |
| `GET /api/credits` | Credit wall data: settled ideas only (handle, title, TLDR, repo, issue URL) |
| `GET /healthz` | Health + embedded git version |

Everything else requires a hive hub session; signed-in ideators additionally
get `GET /api/me/stats` (ideas posted / offered / accepted / settled).

## Submit ideas from your agent

Dibs exposes a stateless Streamable HTTP MCP endpoint at
`https://dibs.hivecommons.dev/mcp`, so Claude Code, Copilot CLI, Cursor, and
other MCP clients can submit ideas without leaving the agent.

Available tools:

| Tool | Auth | Purpose |
|---|---|---|
| `submit_idea(title, description, tags?)` | Required | Creates a private draft idea owned by you and returns its id and API URL |
| `list_my_ideas()` | Required | Lists your ideas with lifecycle phase/status |
| `get_idea(id)` | Optional for public ideas; required for your private ideas | Shows detail, matches, offers, and settlement state |
| `list_repos()` | Not required | Lists registered repo venues |

### Token

Use `Authorization: Bearer <token>`. Dibs first asks the Hive Hub
`/api/saas/whoami` endpoint to verify the bearer token server-to-server; the
hub already accepts GitHub bearer tokens for registered hub users. If that is
not practical, copy the raw `hive_hub_user` cookie value from your browser
after signing in at `https://hive.hivecommons.dev`; Dibs falls back to verifying
that value through the same hub `whoami` cookie flow used by the web UI.

In Chrome/Edge: open DevTools → Application → Cookies →
`https://hive.hivecommons.dev` (or `https://dibs.hivecommons.dev`) → copy the
`hive_hub_user` value.

### Claude Code

```sh
claude mcp add --transport http dibs https://dibs.hivecommons.dev/mcp \
--header "Authorization: Bearer <your-token-or-hive_hub_user-cookie-value>"
```

### Claude Desktop and ChatGPT connectors

Both accept a remote MCP server over Streamable HTTP, so the same endpoint
works without a local process.

Claude Desktop: Settings -> Connectors -> Add custom connector, with the URL
`https://dibs.hivecommons.dev/mcp`. Where the connector UI allows a custom
header, set `Authorization: Bearer <token>`; the read-only tools work without
one, and `submit_idea` needs it.

ChatGPT: Settings -> Connectors -> Add custom connector (available on plans
where custom MCP connectors are enabled), using the same URL and header.

## Recall ideas you never acted on

Dibs ships an MCP prompt, `recall_ideas`, for the ideas that never made it to
a form: the ones mentioned in passing and forgotten. Dibs cannot find those
itself - only the assistant holding your history can - so the prompt asks it
to.

It instructs the assistant to search its memory and your past conversations
for product ideas, feature wishes, requirements and specs you described but
never acted on, report each with enough context to act on, and then ask which
ones to list. It submits only what you pick.

| Argument | Purpose |
|---|---|
| `scope` | Optional focus - a project, a repo, a period like "the last year". Omit to search everything available. |
| `limit` | Maximum candidates to report. Defaults to 10, capped at 50. |

In Claude Code and Claude Desktop the prompt appears as a slash command once
the connector is added. Clients that do not surface prompts in their UI can
still call `prompts/get` directly.

### Generic MCP JSON

```json
{
"mcpServers": {
"dibs": {
"type": "http",
"url": "https://dibs.hivecommons.dev/mcp",
"headers": {
"Authorization": "Bearer <your-token-or-hive_hub_user-cookie-value>"
}
}
}
}
```

## Development

```sh
go build ./...
go test -race -count=1 ./...
go run ./cmd/dibs # serves on :8080 at /
./dibs --version # prints the embedded commit
```

## License

Apache-2.0 — see [LICENSE](https://github.com/hivecommons/dibs/blob/main/LICENSE).
23 changes: 21 additions & 2 deletions public/config/shared.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
"isDefault": true
}
},
"dibs": {
"latest": {
"label": "main (Latest)",
"branch": "main",
"isDefault": true
}
},
"spektacular": {
"latest": {
"label": "main (Latest)",
Expand Down Expand Up @@ -82,6 +89,11 @@
"basePath": "promptargs",
"currentVersion": "main"
},
"dibs": {
"name": "dibs",
"basePath": "dibs",
"currentVersion": "main"
},
"spektacular": {
"name": "Spektacular",
"basePath": "spektacular",
Expand Down Expand Up @@ -118,6 +130,12 @@
"description": "Template expansion for AI prompts",
"secondary": true
},
{
"title": "dibs",
"href": "/docs/dibs/overview/introduction",
"description": "Claim ideas so contributors get credit when agents implement them",
"secondary": true
},
{
"title": "Spektacular",
"href": "/docs/spektacular/overview/introduction",
Expand All @@ -131,7 +149,8 @@
"pluk": "https://github.com/hivecommons/pluk/edit/main",
"rationguard": "https://github.com/hivecommons/rationguard/edit/main",
"promptargs": "https://github.com/hivecommons/promptargs/edit/main",
"spektacular": "https://github.com/hivecommons/spektacular/edit/main"
"spektacular": "https://github.com/hivecommons/spektacular/edit/main",
"dibs": "https://github.com/hivecommons/dibs/edit/main"
},
"updatedAt": "2026-09-24T01:19:21.087Z"
"updatedAt": "2026-09-24T11:55:47.068Z"
}
1 change: 1 addition & 0 deletions scripts/check-internal-links-helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ describe("PROJECT_FOR_NAV", () => {
expect(PROJECT_FOR_NAV.PLUK).toBe("docs/pluk");
expect(PROJECT_FOR_NAV.RATIONGUARD).toBe("docs/rationguard");
expect(PROJECT_FOR_NAV.PROMPTARGS).toBe("docs/promptargs");
expect(PROJECT_FOR_NAV.DIBS).toBe("docs/dibs");
expect(PROJECT_FOR_NAV.SPEKTACULAR).toBe("docs/spektacular");
});
});
Expand Down
1 change: 1 addition & 0 deletions scripts/check-internal-links-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export const PROJECT_FOR_NAV: Record<string, string> = {
PLUK: "docs/pluk",
RATIONGUARD: "docs/rationguard",
PROMPTARGS: "docs/promptargs",
DIBS: "docs/dibs",
SPEKTACULAR: "docs/spektacular",
// Shared sections (Community, …) live under docs/content/<section>/ and
// are served at /docs/<section>/<slug>.
Expand Down
2 changes: 1 addition & 1 deletion scripts/sync-sibling-docs.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { PROJECTS, rewriteLinks, scrubLegacyBranding, syncedHeader, type ProjectSync } from "./sync-sibling-docs";

const SIBLING_PROJECTS = ["hotshot", "pluk", "rationguard", "promptargs", "spektacular"];
const SIBLING_PROJECTS = ["hotshot", "pluk", "rationguard", "promptargs", "dibs", "spektacular"];

function project(id: string): ProjectSync {
const p = PROJECTS.find((x) => x.project === id);
Expand Down
9 changes: 8 additions & 1 deletion scripts/sync-sibling-docs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Sync hotshot, pluk, rationguard, promptargs, and Spektacular docs from their GitHub repos into docs/content/.
* Sync hotshot, pluk, rationguard, promptargs, dibs, and Spektacular docs from their GitHub repos into docs/content/.
*
* Same single-sourcing model as scripts/sync-hive-docs.ts: the repos own the
* markdown; this script pulls it at build time (prebuild) so the site never
Expand Down Expand Up @@ -97,6 +97,13 @@ export const PROJECTS: ProjectSync[] = [
branch: process.env.PROMPTARGS_DOCS_REF || "main",
files: [{ source: "README.md", target: "readme.md", required: true }],
},
{
project: "dibs",
owner: SIBLING_OWNER,
repo: "dibs",
branch: process.env.DIBS_DOCS_REF || "main",
files: [{ source: "README.md", target: "readme.md", required: true }],
},
{
// Canonical site is https://spektacular.dev. The repo README and
// docs/knowledge-base.md come from the spektacular repo; the tutorials are
Expand Down
1 change: 1 addition & 0 deletions src/__tests__/check-internal-links-helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ describe("PROJECT_FOR_NAV", () => {
PLUK: "docs/pluk",
RATIONGUARD: "docs/rationguard",
PROMPTARGS: "docs/promptargs",
DIBS: "docs/dibs",
SPEKTACULAR: "docs/spektacular",
GENERAL: "docs",
});
Expand Down
1 change: 1 addition & 0 deletions src/__tests__/versions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('getProjectFromPath', () => {
['/docs/pluk', 'pluk'],
['/docs/rationguard/install', 'rationguard'],
['/docs/promptargs', 'promptargs'],
['/docs/dibs', 'dibs'],
['/docs/spektacular/api', 'spektacular'],
] as const)('maps %s to project %s', (pathname, id) => {
expect(getProjectFromPath(pathname).id).toBe(id)
Expand Down
1 change: 1 addition & 0 deletions src/app/docs/[...slug]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function getProjectFromSlug(slug: string[]): ProjectId {
if (slug[0] === 'pluk') return 'pluk'
if (slug[0] === 'rationguard') return 'rationguard'
if (slug[0] === 'promptargs') return 'promptargs'
if (slug[0] === 'dibs') return 'dibs'
if (slug[0] === 'spektacular') return 'spektacular'
}
return 'hive'
Expand Down
4 changes: 2 additions & 2 deletions src/app/docs/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import path from 'path'
import { notFound } from 'next/navigation'

const HIVE_DOCS_PATH = process.env.HIVE_DOCS_PATH
const STATIC_PROJECTS: ProjectId[] = ['hive', 'hotshot', 'pluk', 'rationguard', 'promptargs', 'spektacular']
const STATIC_PROJECTS: ProjectId[] = ['hive', 'hotshot', 'pluk', 'rationguard', 'promptargs', 'dibs', 'spektacular']

export const dynamic = 'force-static'
export const dynamicParams = false
Expand Down Expand Up @@ -173,7 +173,7 @@ export function syncedSourceUrl(content: string): string | undefined {
}

function getProjectFromSlug(slug: string[]): { projectId: ProjectId | undefined; docSlug: string[] } {
const knownProjects: string[] = ['hive', 'hotshot', 'pluk', 'rationguard', 'promptargs', 'spektacular']
const knownProjects: string[] = ['hive', 'hotshot', 'pluk', 'rationguard', 'promptargs', 'dibs', 'spektacular']

if (slug.length > 0 && knownProjects.includes(slug[0])) {
return {
Expand Down
Loading
Loading