Skip to content
Guide contents

Project Setup

← Guide index

Introducing docs-kit to a fresh repository, from nothing to your first current document. About 15 minutes.

If you already have design docs, read Adopting in an existing project first.

Prerequisites

  • Installation is done (docs-kit --version works)
  • The target is a git repository with at least one commit

The path

Step 1 — build the scaffolding with docs-kit init

Run it at the repository root.

docs-kit init
created: docs.config.yaml
created: docs/adr/
created: docs/spec/
created: docs/arch/
created: docs/plan/
created: docs/INDEX.md
created: CLAUDE.md
created: .github/workflows/docs-kit.yml
created: .claude/commands/docs-verify.md
created: .claude/commands/docs-catchup.md

What gets generated:

PathContentsIf it already exists
docs.config.yamlConfig fileSkipped
docs/{adr,spec,arch,plan}/The four layer directoriesSkipped
docs/INDEX.mdEmpty index (generated output)Skipped
CLAUDE.mdManaged block with the AI protocolOnly the block is replaced. Appended if absent
.github/workflows/docs-kit.ymlCISkipped (--force to overwrite)
.claude/commands/docs-verify.mdSlash command definitionSame
.claude/commands/docs-catchup.mdSameSame

init is idempotent. Running it repeatedly never damages existing files. For CLAUDE.md it replaces only the range between <!-- docs-kit:begin v1 --> and <!-- docs-kit:end -->, leaving everything else untouched. Rerun it after upgrading to refresh the managed block.

Running outside a git repository fails with exit 3.

Step 2 — tune docs.config.yaml

The generated defaults:

schemaVersion: 1
root: docs
codeRoots: [src]
ignore: []

Only two of these need attention: codeRoots and ignore. Leave the rest alone.

codeRoots — the top-level directories where code lives. A scope pointing outside them raises lint warning W003 (typo detection). Match reality.

codeRoots: [cmd, internal, pkg]   # Go
codeRoots: [src, app]             # TypeScript
codeRoots: [lib, app]             # Ruby / Rails

ignore — files whose changes should not stale a document. Always include test code. Leaving this empty means every one-line test edit stales your design docs, and the workflow collapses.

ignore:
  - "**/*_test.go"          # Go
  - "**/*.test.ts"          # TypeScript
  - "**/*.spec.ts"
  - "**/__snapshots__/**"
  - "**/testdata/**"
  - "**/fixtures/**"

ignore is applied after the scope match. It is the place for “inside scope, but cannot affect the spec.”

These four keys are the entire configuration. Layer structure, status values, and the INDEX format are all non-configurable — so that the AI reads the same way in every repository.

Step 3 — write the first ADR

Make the first document an ADR. Pick something already decided in the project (language, framework, data store) and write it up.

docs-kit new adr use-postgres --title "Use PostgreSQL for persistence"
created ADR-0001
docs/adr/0001-use-postgres.md

The ADR number is assigned automatically as max + 1. The last line is the created path, so open it directly.

The template has three sections — Situation / Decision / Consequences (headings are Japanese in the shipped template).

---
id: ADR-0001
title: Use PostgreSQL for persistence
status: draft
---

# ADR-0001: Use PostgreSQL for persistence

## Context

(why this decision became necessary)

## Decision

(what was decided)

## Consequences

(what changes as a result; the trade-offs accepted)

Writing tips:

  • Put the constraints in “Situation.” What the problem was, what the options were
  • State “Decision” in one sentence. Leaving hedges in makes it undecidable for later readers (and for AI)
  • Record the rejected options and why in “Consequences.” This is an ADR’s greatest value. Without it you redo the same argument in six months

When finished, change status from draft to current by hand.

status: current

Documents without a scope may be set to current by hand. They are outside the freshness check, so verify would mean nothing. Most ADRs are like this.

Step 4 — write the first spec and set its scope

Next, write “how it works now.” Pick one already-implemented feature.

docs-kit new spec editing-model --title "Editing model"
created SPEC-editing-model
docs/spec/editing-model.md

Replace the scope in the front matter with the code range this document is responsible for.

---
id: SPEC-editing-model
title: Editing model
status: draft
scope:
  - src/core/editing/**
depends_on: [ADR-0001]
---

Choose the scope by asking “if this file changes, do I need to reread this document?” If yes, include it.

  • Too broad (src/**) → everything stales it, and eventually nobody looks
  • Too narrow (one file) → a change next door goes unnoticed
  • A good target is one document per module

Body tips:

  • Say what this document is in the first paragraph. The 200-character summary returned by which and MCP is derived from the first non-heading, non-empty paragraph. A good one lets the AI correctly decide whether to open it
  • Do not write history — link to the ADR. A spec carries only “what is true now”

Step 5 — reconcile with the code, then verify

Read the spec you wrote against the actual code. This is the critical step: docs-kit guarantees only “when this was last done.” The work itself is done by a human or an AI.

Once you have confirmed it matches:

docs-kit verify SPEC-editing-model
SPEC-editing-model: verified_at → 8f3a1c2 (promoted to current)

verified_at now holds HEAD’s SHA and status has moved from draft to current.

---
id: SPEC-editing-model
title: Editing model
status: current
scope:
  - src/core/editing/**
depends_on: [ADR-0001]
verified_at: 8f3a1c2e0b7d4a91c3f6e2d8b5a704c1e9f3d602
---

verify rewrites only the relevant front matter lines. It does not re-serialize the YAML, so comments, x- fields, and the body survive byte for byte.

If there are uncommitted changes inside scope, you get a warning. verify works against HEAD, so those changes are not covered. Commit first.

Step 6 — generate the index and commit

docs-kit lint
lint: OK (2 documents)
docs-kit index
generated: docs/INDEX.md (2 documents)
docs-kit stale
ADR-0001            skipped  (no scope)
SPEC-editing-model  ok       (verified at 8f3a1c2)

ok 1 / stale 0 / broken 0 / skipped 1

With all three green, commit.

git add docs docs.config.yaml CLAUDE.md .github .claude && git commit -m "docs: introduce docs-kit with the first ADR and spec"

docs/INDEX.md is generated output. Never hand-edit it. If it drifts, regenerate with docs-kit index. CI’s index --check keeps it in sync (the gofmt approach).

Step 7 — turn on CI

init already generated .github/workflows/docs-kit.yml. Push and it runs.

One thing to check inside it:

- uses: actions/checkout@v4
  with:
    fetch-depth: 0        # ← without this, every document comes back broken

fetch-depth: 0 is required. stale walks git history, so a shallow clone cannot resolve the verified_at SHAs and everything reports unknown-sha / broken.

DOCS_KIT_VERSION is stamped with the version of the binary that ran init. For a local build that will be something like dev, so replace it with a real tag.

env:
  DOCS_KIT_VERSION: v1.0.0

Step 8 — connect Claude Code

claude mcp add docs-kit -- docs-kit mcp

The AI now has four tools (docs_which / docs_stale / docs_verify / docs_new), and the managed block that init put in CLAUDE.md pins its working procedure.

The two slash commands written into .claude/commands/ also become available.

CommandPurpose
/docs-verifyThe closing routine (reflect spec changes → verify → check lint/stale)
/docs-catchupResolve stale / broken documents against the implementation

Step 9 — run the loop once to check

Confirm the setup actually works by going around the loop once.

1. Does reverse lookup work?

docs-kit which src/core/editing/model.ts
primary:
  SPEC-editing-model  current  8f3a1c2  docs/spec/editing-model.md
related:
  ADR-0001            current  -        docs/adr/0001-use-postgres.md

Correct means the spec appears under primary, and the ADR linked via depends_on appears under related.

2. Does the freshness check work?

Change any file inside scope, commit it, then run stale.

docs-kit stale
ADR-0001            skipped  (no scope)
SPEC-editing-model  stale    code-changed: 1 files changed since 8f3a1c2
  - src/core/editing/model.ts

ok 0 / stale 1 / broken 0 / skipped 1

Getting stale means it works. Update the document to match the implementation and run docs-kit verify SPEC-editing-model to return it to ok.

That completes the setup. From here it is just the loop in Workflow.

Common stumbles

SymptomCauseFix
E008You set status: current but there is no verified_atDo not set current by hand — go through docs-kit verify <id>
E004id and filename do not correspondAlign them: SPEC-foospec/foo.md, ADR-0003adr/0003-*.md
Everything is always stalescope is too broad, or tests are not in ignoreNarrow the scope. Add test globs to ignore
W002 warningThe scope matches no existing file (reported only once the document is no longer draft)Fix the path typo. Check codeRoots too
W003 warningThe scope points outside codeRootsCorrect codeRoots to match reality
verify errors outNo scope / plan layer / supersededAdd a scope. The plan layer cannot be verified by design
Only CI reports everything brokenfetch-depth: 0 is missingAdd it to the checkout step
index --check failsYou forgot to commit INDEX.mdRun docs-kit index and commit the diff

Next