# Generate Context (All Agents)

Generate this repo's context from the code on disk and push it to the
workspace — `flydocs/context/project.md` and `flydocs/context/service.json`.

Triggers: "generate context", "my context is empty", "the portal has no
context for this repo", "regenerate project.md"

Use this when the portal cannot produce the context itself: a GitHub
Enterprise, self-hosted, or clone-URL repo has no App install to crawl from, so
the server has nothing to generate and `flydocs update` pulls down an empty
`project.md`. You are already in the repo; you can read what the crawler
cannot. You write the two artifacts, a script pushes them.

---

## Step 0: Refuse outside a FlyDocs-configured repo

Read `.flydocs/config.json` in the repo you are working in (in a multi-repo
workspace that is the child repo, not the workspace root).

- **Missing** → stop. Print: `No .flydocs/config.json here — run 'flydocs init'
in this repo first.` Do not create one, and do not generate anything.
- **`tier` is not `cloud`** → generate the two files (Steps 2-4) but skip the
  push in Step 5. There is no workspace to push to; the local files are the
  whole point on the local tier.

## Step 1: Pull first

```bash
flydocs run context.pull
```

Fallback: `python3 .claude/skills/flydocs-workflow/scripts/context.py pull`.

This comes first, and what it does is narrower than it looks. The relay does
not serve marked-up sections: `config/generate` strips every
`<!-- flydocs: … -->` marker from what it stores and appends the workspace's
rules as plain markdown — `## Workspace Rules`, `## Repo Rules`, and a
`## Status Workflow` block. So the pull refreshes the **rules text on disk**;
it does not bring section markers, and the file you are about to edit is flat
markdown: your narrative first, the appended rules after it.

Expect the pull to fail on exactly the repo this command exists for:

```
Server returned no project.md content. Ensure context is configured in the dashboard.
```

with exit code 1. That is the empty context, which is the problem you are here
to fix. Note it and continue to Step 2 — do not stop, and do not try to
"configure it in the dashboard"; the portal cannot crawl this repo.

## Step 2: Read the repo

Derive everything from files, not from memory of similar projects. Manifests
live wherever the repo puts them — root, `apps/*`, `packages/*`, `services/*`,
`src/*-service/` — so find them rather than assuming a layout:

- **Manifests**: `package.json`, `pyproject.toml`, `requirements.txt`,
  `Cargo.toml`, `go.mod`, `pom.xml`, `build.gradle`, `Gemfile`,
  `composer.json`, `*.csproj`, `Package.swift`, `pubspec.yaml`.
- **Entry points**: the `main`/`bin`/`scripts` fields of those manifests, then
  `src/index.*`, `src/main.*`, `cmd/*/main.go`, `app/`, `server.*`.
- **API surface**: route directories (`src/app/api/`, `routes/`,
  `controllers/`), GraphQL schemas, `.proto` files, published package names,
  event topics, webhook handlers.
- **Dependencies that matter**: other repos and services this one calls, not
  the whole lockfile. The README, `.env.example`, client modules and base URLs
  are where these show up.
- **Prose**: `README.md`, `CONTRIBUTING.md`, `docs/`, existing ADRs under
  `flydocs/knowledge/decisions/` — for purpose and standards, in the project's
  own words.

Two rules: write what is there, and say nothing you could not point at a file
for. An honest three-line stack beats an invented architecture.

## Step 3: Write `flydocs/context/project.md`

Rewrite **only your own narrative** — the part at the top, before anything
the relay appended. Leave everything after it exactly as the pull left it.

What follows your narrative can be two things, and you do not have to tell
them apart. The relay strips the marker lines out of the stored document but
keeps what was between them, so a repo whose stored context already has rules
gets them twice in the served file: once unlabelled, right after the
narrative, and once under `## Workspace Rules` / `## Repo Rules` /
`## Status Workflow`. The push reads the stored context and subtracts both
copies. Your job is to leave them alone.

If the pull produced no file at all, write just the narrative — no rules
headings, no markers:

```markdown
# Project Context

## What This Is

[2-3 sentences: what this repo is, who uses it, what it is for.]

## Stack

- **Language**: …
- **Framework**: …
- **Data**: …
- **Build/Test**: …
- **Hosting**: …

## Structure

[Where things live: entry points, shared types, package boundaries. A short
list a new agent could navigate from.]

## Interfaces

[What this repo exposes (APIs, packages, events) and what it consumes from
other services.]

## Active Priorities

[What the team is working on now, if the repo says so. Omit the section
rather than guess.]
```

Do not add `<!-- flydocs: … -->` markers yourself. The push wraps the
narrative in the marked-up shape the portal stores, and puts the rules the
server already holds back into their own sections.

## Step 4: Write `flydocs/context/service.json`

Required — the push refuses a descriptor missing any of `version`, `name`,
`repoSlug`, `purpose`, `stack`. `repoSlug` must match the slug the workspace
registered (`repoSlug` in `.flydocs/config.json`).

```json
{
  "version": 2,
  "name": "Human-readable service name",
  "repoSlug": "owner/repo",
  "purpose": "One sentence: what this service does.",
  "stack": ["typescript", "next", "convex"],
  "apis": [
    {
      "type": "rest",
      "path": "/api/v1",
      "description": "What this surface does",
      "methods": ["GET", "POST"]
    }
  ],
  "dependencies": [
    {
      "service": "owner/other-repo",
      "interface": "REST /api/relay/*",
      "description": "Why this dependency exists"
    }
  ],
  "structure": {
    "entryPoints": ["src/index.ts"],
    "sharedTypes": ["src/lib/types.ts"],
    "buildSystem": "tsup",
    "packages": [
      { "name": "@acme/sdk", "path": "packages/sdk", "purpose": "…" }
    ]
  }
}
```

`packages` is for monorepos only. Full field reference:
`.claude/skills/flydocs-workflow/reference/service-descriptor-schema.md`.

Do not write `generatedBy`, `generatedAt` or `provenance` yourself — the push
stamps them from git.

## Step 5: Push

```bash
flydocs run context.push
```

Fallback: `python3 .claude/skills/flydocs-workflow/scripts/context.py push`.

Both artifacts go by default; `--project-md` or `--service-json` narrows it.
What the push does for you:

- reads the stored context first, then cuts both copies of the rules out of
  your narrative — the headed tail and the unlabelled bodies — naming in its
  warnings whatever it cut;
- puts the stored rules back into their own sections and wraps your narrative
  in the marked-up shape the portal stores;
- validates the descriptor, refuses one whose `repoSlug` is another repo's,
  and stamps it with `generatedBy: "agent"` and `provenance` (branch, commit,
  dirty, time).

If that read fails, the push **refuses** and names the reason: without it the
rules cannot be subtracted or preserved. `--no-preserve-rules` pushes anyway,
storing empty rules sections — correct only for a repo you know has none.

Named with `--project-md`, a file with no narrative of its own is an error.
Without the flag it is a warning and the descriptor still goes.

`--dry-run` makes no request at all, so its preview is your narrative before
the stored rules are subtracted; `--dry-run --with-read` performs the read and
nothing else, and previews exactly what the push would send.

It needs an admin API key — a member key is refused with `ADMIN_REQUIRED`, and
the fix is a workspace admin running it, not a retry.

**Order: generate, push, then `flydocs update`.** Never generate then update.
An update pulls the server's copy over your files, so a generation that was not
pushed is simply gone.

## Step 6: Report

Print, filling in the real values:

```
Context generated and pushed for [repoSlug] (context version N).
  flydocs/context/project.md   — narrative, [N] bytes
  flydocs/context/service.json — [N] apis, [N] dependencies

In the portal: Version Control → this repo's card → Context.
```

Name the location, not a URL: the config carries no workspace slug to build
one from. The repo's card on that page shows the pushed narrative and the
descriptor, and that is where a teammate confirms it landed.

$ARGUMENTS
