diff --git a/workflows/docs-updater/.ambient/ambient.json b/workflows/docs-updater/.ambient/ambient.json new file mode 100644 index 00000000..71e71123 --- /dev/null +++ b/workflows/docs-updater/.ambient/ambient.json @@ -0,0 +1,6 @@ +{ + "name": "Docs Updater", + "description": "Analyze code changes and update documentation. Discovers affected doc files, generates conservative format-aware updates, and opens a docs PR. Works with any text-based doc format. Optional semantic indexing for large repos.", + "systemPrompt": "You are a documentation update assistant that analyzes code changes and keeps documentation in sync.\n\nCRITICAL: Before doing ANYTHING else, read and follow the controller skill at .claude/skills/controller/SKILL.md. The controller defines the workflow phases, the setup procedure, and how to recommend next steps. You MUST follow its instructions exactly — including using AskUserQuestion (not plain text) for all user decisions. Do NOT skip the controller or improvise your own flow.\n\nWORKSPACE NAVIGATION:\nStandard file locations (from workflow root):\n- Skills: .claude/skills/*/SKILL.md\n- Outputs: artifacts/docs-updater/\n\nTool selection rules:\n- Use Read for: Known paths, standard files, files you just created\n- Use Glob for: Discovery (finding multiple files by pattern)\n- Use Grep for: Content search\n\nNever glob for standard files:\n✅ DO: Read .ambient/ambient.json\n❌ DON'T: Glob **/ambient.json", + "startupPrompt": "Greet the user and introduce yourself as a documentation update assistant. Explain that you can analyze code changes (from a PR, branch diff, or local changes) to discover which documentation files need updating, generate conservative format-aware updates, and open a docs PR. Include /index in the list of capabilities — it builds semantic indexes for docs (whether in a separate repo or a folder alongside the code) so future runs are faster, use fewer tokens, and can catch conceptual matches that keyword grep would miss. Note that /index is only recommended when the docs have many folders — for smaller docs the default grep-based discovery works well. Ask the user how they'd like to provide the code changes — a PR URL, branch name, or local diff." +} diff --git a/workflows/docs-updater/.claude/commands/index.md b/workflows/docs-updater/.claude/commands/index.md new file mode 100644 index 00000000..1c0926d7 --- /dev/null +++ b/workflows/docs-updater/.claude/commands/index.md @@ -0,0 +1,69 @@ +# /index - Build semantic indexes for documentation + +## Purpose + +Build a semantic summary of each documentation folder so future discovery +runs can skip irrelevant folders instead of grepping all files. + +## Prerequisites + +- Access to the documentation location (repo or subfolder) +- Write permissions to create `.doc-index/` in the docs location + +## When to use + +- The docs location has many folders and you plan to run the workflow + multiple times +- You want faster discovery in future sessions + +## Important + +Indexes are only worth building if you commit and push them. Without +that, they're lost when the session ends and the next run starts from +scratch. + +## Process + +1. Ask the user where the documentation lives: + - Same repo, subfolder (ask which folder) + - Separate repo (ask for URL or local path) +2. Check if `.doc-index/manifest.json` already exists + - If yes: for each folder in the manifest, compute SHA256 hashes of + all doc files and compare against the hashes stored in the manifest. + Only rebuild folders where at least one file hash differs. Report + which folders are up-to-date and which need rebuilding + - If no: build indexes for all folders from scratch +3. Scan the docs location for folders containing documentation files + - Add any newly discovered folders (not present in the manifest) to + the rebuild list + - Remove entries for folders that no longer exist +4. For folders with subfolders, create sub-indexes per subfolder +5. Dispatch a subagent to build the indexes +6. Generate a semantic summary for each folder covering: what it + documents, what code changes would affect it, key technical concepts +7. Write indexes to `.doc-index/` and update `manifest.json`. The + manifest MUST store per-file SHA256 hashes (not just file counts) + so that future runs can detect exactly which folders changed: + + ```json + { + "version": "1.0", + "updated": "", + "folders": { + "": { + "built": "", + "doc_hashes": { + "/.md": "", + "/.md": "" + } + } + } + } + ``` + +8. Ask where to commit the indexes (current branch or main) + +## Output + +- `.doc-index/manifest.json` — metadata with per-file SHA256 hashes +- `.doc-index/.index.md` — semantic summary per folder diff --git a/workflows/docs-updater/.claude/skills/controller/SKILL.md b/workflows/docs-updater/.claude/skills/controller/SKILL.md new file mode 100644 index 00000000..db501d0f --- /dev/null +++ b/workflows/docs-updater/.claude/skills/controller/SKILL.md @@ -0,0 +1,132 @@ +--- +name: controller +description: Top-level workflow controller that manages phase transitions for documentation updates. +--- + +# Docs Updater Workflow Controller + +You are the workflow controller. Your job is to manage the docs-updater workflow +by executing phases and handling transitions between them. + +## Phases + +1. **Setup** (automatic, first interaction) + Establish the code change source (diff) and the documentation repo location. + +2. **Review** — the `discovery` skill then the `generation` skill in preview mode. + Discover which doc files need updates and show proposed changes. + +3. **Update** — the `generation` skill in apply mode. + Apply the accepted changes to documentation files. + +4. **Open PR** — the `pr` skill. + Push changes and create a draft pull request for the documentation updates. + +Phases can be skipped or reordered at the user's discretion. + +Note: The `/index` command is available separately for building semantic +indexes. It is not a phase managed by this controller — the user can run +it independently at any time. + +## Setup Procedure + +Before any phase can run, you must establish two things. Run this setup +automatically on the first user interaction. + +### Step 1: Determine the code change source + +Ask the user via `AskUserQuestion` how they want to provide the code changes: + +- **PR URL** — Use `gh pr diff ` to obtain the diff +- **Branch comparison** — Use `git diff ...` to obtain the diff +- **Local uncommitted changes** — Use `git diff` to obtain the diff + +Obtain the diff and keep it in context. If the diff is empty, tell the user +and stop. + +### Step 2: Determine the docs repo location + +Ask the user via `AskUserQuestion` where the documentation lives: + +- **Same repo, subfolder** — User provides the subfolder path (e.g., `docs/`) +- **Separate repo** — User provides a repo URL or local path; clone it if needed +- **Current directory** — Documentation is in the current working directory + +Navigate to the docs location and record the path. All subsequent skills +operate relative to this docs root. + +### Step 3: Confirm setup and ask for next step + +Summarize the setup to the user: +- Code change source and diff size (number of files changed) +- Docs location and number of doc files found + +Then **you MUST use `AskUserQuestion`** to present the next step options. +Do NOT use a plain text question — `AskUserQuestion` triggers platform +notifications so the user knows you need input. Plain text questions do +not create these signals and the user may not see them. + +## How to Execute a Phase + +1. **Announce** the phase to the user before doing anything else. +2. **Run** the skill for the current phase. +3. When the skill completes, present the results and use "Recommending Next + Steps" below to offer options. +4. **Use `AskUserQuestion` to get the user's decision.** Present the + recommended next step and alternatives as options. Do NOT continue until the + user responds. This is a hard gate — the `AskUserQuestion` tool triggers + platform notifications so the user knows you need their input. Plain-text + questions do not create these signals. + +## Recommending Next Steps + +After each phase completes, present the user with **options** — not just one +next step. + +### Typical Flow + +```text +setup → review → (user selects files) → update → pr +``` + +### What to Recommend + +**After Setup — present these options via `AskUserQuestion`:** +- Review (recommended) — discover affected doc files +- If the docs location has 10+ folders and no `.doc-index/` exists, + mention that the user can run `/index` first to build semantic indexes + for faster discovery in future runs + +**After Review:** +- Recommend Update to apply the proposed changes +- Offer to adjust the file selection first + +**After Update:** +- Recommend Open PR to submit the changes +- Offer to stop here if the user handles PRs manually + +**After PR:** +- The workflow is complete. Summarize what was done. + +## Passing Context to Skills + +When invoking skills, provide the following context: + +- **Discovery skill**: The diff content and the docs root path +- **Generation skill**: The diff content, the docs root path, and the list of + selected files from the discovery phase. Specify the mode: preview for + Review, apply for Update +- **PR skill**: Invoke after changes are written to disk. Use branch prefix + `docs/` and conventional commit format `docs(scope): description` + +## Rules + +- **Never auto-advance.** Always use `AskUserQuestion` and wait for the user's + response between phases. This is the single most important rule. If you + proceed to another phase without the user's explicit go-ahead, the workflow + is broken. +- **Recommendations come from this file, not from skills.** Skills report + findings; this controller decides what to recommend next. +- **Track which directory you are in.** The code and docs may live in + separate repos or in the same repo (e.g., a `docs/` subfolder). Always + know which location you are in before running commands. diff --git a/workflows/docs-updater/.claude/skills/discovery/SKILL.md b/workflows/docs-updater/.claude/skills/discovery/SKILL.md new file mode 100644 index 00000000..30f46513 --- /dev/null +++ b/workflows/docs-updater/.claude/skills/discovery/SKILL.md @@ -0,0 +1,157 @@ +--- +name: discovery +description: Find documentation files that need updates based on code changes. Default mode uses identifier matching and grep. Optional semantic indexing for large repos. +--- + +# Documentation Discovery + +Given a code diff and a docs repository, identify which documentation files +need updating based on the code changes. + +## Modes + +- **Discovery mode** (default): Find files that need updating using identifier + matching and grep — no setup required, works immediately +- **Index-assisted mode**: If semantic indexes exist (`.doc-index/`), use them + for faster discovery on large repos + +Index building is handled by the `/index` command, not this skill. + +## Discovery Process (Default — No Indexes) + +Follow these steps in order. Do not skip steps. + +### Step 1: Receive the diff and changed file list + +Use the diff provided by the controller. Record the list of changed files: + +```bash +git diff --name-only ... +``` + +If no changes can be identified, stop and report. + +### Step 2: Build the identifier checklist + +Go through **every** changed file in the diff. For each file, extract +identifiers from the modified lines (lines starting with `+` or `-`) and +from diff hunk headers (`@@` lines). + +Identifiers to extract: +- Function and method names +- Class and type names +- CLI flag names and configuration keys +- API endpoint paths +- Constants and variable names that appear in public interfaces + +Use the most specific form of each identifier. Full function names, CLI +flags, and config keys are good — they match only relevant docs. Avoid +short generic words that would match hundreds of unrelated files. + +Write them as a numbered checklist — one entry per changed file, with all +identifiers from that file. **Do not skip files.** Every changed file gets +an entry. This checklist is your contract — you will search docs for every +identifier on it. + +### Step 3: Discover documentation files + +Find all text-based documentation files in the docs location: + +```bash +find {docs_root} -type f \( -name "*.md" -o -name "*.adoc" -o -name "*.rst" \ + -o -name "*.txt" -o -name "*.html" -o -name "*.yaml" -o -name "*.yml" \) \ + ! -path "*/.git/*" ! -path "*/.doc-index/*" ! -path "*/node_modules/*" +``` + +If the location has no documentation files, produce zero findings and stop. + +### Step 4: Search docs for every identifier + +Write a shell script that greps for each identifier across the documentation +files. Run it in a single Bash call: + +```bash +for id in "identifier1" "identifier2" "identifier3"; do + matches=$(grep -rFl "$id" {docs_root} --include="*.md" --include="*.adoc" --include="*.rst" --include="*.txt" --include="*.html" --include="*.yaml" --include="*.yml" 2>/dev/null) + if [ -n "$matches" ]; then + echo "MATCH: $id -> $matches" + fi +done +``` + +Include **every** identifier from the checklist. No identifiers are skipped. + +From the output, collect all matched doc files into a candidate list. + +Exclude from the candidate list: +- Documentation files already modified in the same diff — those are being + actively updated +- Auto-generated files — look for markers like "DO NOT EDIT", "generated by", + or "auto-generated" in the first few lines of the file, or check if a + generator script in the repo produces these files + +### Step 5: Evaluate candidates (two passes) + +**Pass 1 — Quick scan.** For each candidate doc file from step 4, view +only the lines that matched (use `grep -n` to see them in context). Based +on the matching lines alone, decide whether the doc might be stale: + +```text +- path/to/doc.md → possibly stale (describes behavior that changed) +- path/to/other.md → not stale (mentions identifier in passing) +- path/to/another.md → not stale (changelog entry, historical) +``` + +Every candidate must have a verdict. Do not skip candidates. + +**Pass 2 — Deep read.** For each candidate marked "possibly stale" in +pass 1, read the relevant sections of the file alongside the corresponding +part of the diff. Confirm whether the doc is actually stale. + +When evaluating: +- **Only flag docs whose content is now incorrect.** A doc that mentions + an identifier is not stale if the described behavior is unchanged. +- **Do not flag changelog entries or release notes** that describe past + releases — historical entries are not stale. +- **Do not flag docs about a different component** that happens to share + an identifier name. + +### Step 6: Present results + +For each confirmed stale doc, present to the user: + +- File path +- What is stale and why (reference the specific code change) +- What should be updated + +Use `AskUserQuestion` to let the user confirm or modify the selection. +Present files as options the user can accept or reject individually. + +## Index-Assisted Discovery (Optional — For Large Repos) + +If `.doc-index/` exists in the docs location, use it for faster discovery. + +### How it works + +1. Read all `{docs_root}/.doc-index/*.index.md` files +2. Read the code diff +3. For each index, determine: would documentation in this area become + incorrect based on this diff? Use the "Code Changes That Would Require + Documentation Updates" and "Key Technical Concepts" sections to decide +4. Only folders that pass this check proceed — the rest are skipped entirely +5. For the relevant folders, use the "Files Summary" section of the index + to identify which specific files are likely affected — do NOT read all + files in the folder. Only read the files whose summary suggests they + document the changed behavior +6. Present results to the user (same as Step 6 of the default mode) + +### When to suggest indexing + +After completing discovery in default mode, if the docs location has 10+ +folders, suggest running the `/index` command to build indexes and +committing them to speed up future runs. + +## Output + +Return the list of selected file paths (confirmed by the user) to the +controller for the generation phase. diff --git a/workflows/docs-updater/.claude/skills/generation/SKILL.md b/workflows/docs-updater/.claude/skills/generation/SKILL.md new file mode 100644 index 00000000..b2118bdd --- /dev/null +++ b/workflows/docs-updater/.claude/skills/generation/SKILL.md @@ -0,0 +1,59 @@ +--- +name: generation +description: Generate documentation updates based on code changes. Supports preview and apply modes. +--- + +# Documentation Generation + +Given a code diff and a list of documentation files selected for updating, +generate minimal, format-correct updated content for each file. + +## Modes + +- **Preview mode** (during Review): Generate proposed changes, show diffs + to the user. Do not write files. +- **Apply mode** (during Update): Generate changes, write them to disk, + and show the diff of each change to the user so they can see exactly what + was modified. + +## Formatting + +Match the existing format of each file. Do not mix syntax formats within +a file. + +**Critical**: When generating updated file content, output RAW content only. +NEVER wrap the output in code fences (no `` ```markdown ``, no `` ``` ``). +The output must be the file content itself, ready to write directly to disk. + +## Decision Logic Per File + +For each file, apply this logic: + +1. **Does this file document the EXACT thing being changed in the diff?** + - If NO → skip this file (NO_UPDATE_NEEDED) + +2. **Does the diff change behavior, interfaces, or documented usage in a way that could make this file incorrect?** + - If NO → skip (NO_UPDATE_NEEDED) + +3. **Is that new thing already documented in this file?** + - If YES → skip (NO_UPDATE_NEEDED) + +4. **Otherwise** → add ONLY the specific change, nothing more + +Do not restructure or rewrite surrounding content. Change only what the +diff justifies. + +If the diff alone doesn't provide enough context to write an accurate +update, read the relevant source code to understand the behavior before +writing. If the behavior is still unclear, flag the update as needing +human review rather than guessing. + +## User Instructions + +The user may provide instructions when invoking Update: + +- **Global instructions** (e.g., "keep changes minimal") +- **Per-file instructions** (e.g., "config.rst: only update the CLI usage + example") + +Per-file instructions override global instructions for that specific file. diff --git a/workflows/docs-updater/.claude/skills/pr/SKILL.md b/workflows/docs-updater/.claude/skills/pr/SKILL.md new file mode 100644 index 00000000..8f3e9439 --- /dev/null +++ b/workflows/docs-updater/.claude/skills/pr/SKILL.md @@ -0,0 +1,694 @@ +--- +name: pr +description: Create a pull request from the current branch. Use this instead of running gh pr create directly — it detects GitHub App vs user auth, finds or creates forks, syncs workflow files, detects the upstream default branch, and falls back to compare URLs when API access is limited. +--- + +# Create Pull Request Skill + +You are preparing to submit changes as a pull request. This skill provides a +systematic, failure-resistant process for getting code from the working directory +into a PR. It handles the common obstacles: authentication, fork workflows, +remote configuration, and cross-repo PR creation. + +## IMPORTANT: Follow This Skill Exactly + +This skill exists because ad-hoc PR creation fails in predictable ways. +**Do not improvise.** Follow the numbered steps in order. Do not skip steps. +Do not invent alternative approaches when a step fails — use the documented +fallback ladder at the bottom of this file. + +## Your Role + +Get the changes submitted as a draft pull request. Handle the full +git workflow: branch, commit, push, and PR creation. When steps fail, follow +the documented recovery paths instead of guessing. + +## Critical Rules + +- **Never ask the user for git credentials.** Use `gh auth status` to check. +- **Never push directly to upstream.** Always use a fork remote. This applies + even if you are authenticated as an org bot or app — do not assume any + account has push access to upstream. Always go through a fork. +- **Never skip pre-flight checks.** They prevent every common failure. +- **Always create a draft PR.** Let the author mark it ready after review. +- **Always work in the project repo directory**, not the workflow directory. +- **Never attempt `gh repo fork` without asking the user first.** +- **Never fall back to patch files without exhausting all other options.** + +## Process + +### Placeholders Used in This Skill + +These are determined during pre-flight checks. Record each value as you go. + +| Placeholder | Source | Example | +| --- | --- | --- | +| `AUTH_TYPE` | Step 0: `gh auth status` + `gh api user` | `user-token` / `github-app` / `none` | +| `GH_USER` | Step 0: `gh api user` or `/installation/repositories` (for bots) | `jsmith` | +| `UPSTREAM_OWNER/REPO` | Step 2c: `gh repo view --json nameWithOwner` | `acme/myproject` | +| `DEFAULT_BRANCH` | Step 2c: detected from upstream repo | `main` / `dev` / `master` | +| `UPSTREAM_REMOTE` | Step 2b: the git remote name pointing to the upstream org | `origin` / `upstream` | +| `FORK_OWNER` | Step 3: owner portion of fork's `nameWithOwner`, or `GH_USER` if newly created | `jsmith` | +| `FORK_REMOTE` | Step 4: the git remote name pointing to the fork | `fork` / `origin` | +| `REPO` | The repository name (without owner) | `myproject` | +| `BRANCH_NAME` | Step 5: the branch you create | `feature/issue-42-auth-middleware` | + +### Step 0: Determine Auth Context + +Run this FIRST, before any other work. The auth type determines the entire +flow — if you skip this, every subsequent step will use the wrong strategy. + +```bash +gh auth status +``` + +Then determine your identity: + +```bash +# Works for normal user tokens: +gh api user --jq .login 2>/dev/null + +# If that fails (403), you're running as a GitHub App/bot. +# Get the real user from the app installation: +gh api /installation/repositories --jq '.repositories[0].owner.login' +``` + +The `/installation/repositories` endpoint works because GitHub Apps are +installed on user accounts — the repo owner is the actual user. + +Record `GH_USER` and `AUTH_TYPE`: + +- If `gh api user` succeeded: `AUTH_TYPE` = `user-token`, `GH_USER` = the login +- If `gh api user` failed but `/installation/repositories` worked: + `AUTH_TYPE` = `github-app`, `GH_USER` = the repo owner login +- If `gh auth status` itself failed: try recovering from an expired token + (see below). If recovery fails: `AUTH_TYPE` = `none` + +**Recovering from expired tokens:** Platform sessions often start with a +`GITHUB_TOKEN` env var that can expire mid-session. The `refresh_credentials` +MCP tool refreshes the backend but does NOT update the shell env var. If +`gh auth status` fails, check for a git credential helper: + +```bash +git config --global credential.helper 2>/dev/null +``` + +If a credential helper exists (e.g., `/tmp/git-credential-ambient`), query it +for a fresh token: + +```bash +FRESH_TOKEN=$(printf 'protocol=https\nhost=github.com\n\n' | git credential fill 2>/dev/null | grep '^password=' | cut -d= -f2) +``` + +If that returns a token, export it and re-check auth: + +```bash +export GITHUB_TOKEN="$FRESH_TOKEN" +gh auth status +``` + +**Important:** Each shell invocation gets a fresh environment, so you must +prepend the export to every subsequent `gh` command, or write the token to +`~/.config/gh/hosts.yml` so `gh` picks it up natively: + +```bash +gh auth login --with-token <<< "$FRESH_TOKEN" +``` + +The `gh auth login` approach is preferred — it persists for all subsequent +`gh` commands without per-command exports. After recovery, re-run the identity +checks above to set `AUTH_TYPE` and `GH_USER`. + +### Step 1: Locate the Project Repository + +The workflow runs from the workflow directory, but the code changes live +in the project repository. Before doing any git work: + +```bash +# Find the project repo — it's typically in /workspace/repos/ or an add_dirs path +ls /workspace/repos/ 2>/dev/null || ls /workspace/artifacts/ 2>/dev/null +``` + +`cd` into the project repo directory before proceeding. All subsequent git +commands run from there. + +If the user provides a path or the repo is obvious from session context +(prior commands, artifacts), use that directly. + +### Step 2: Pre-flight Checks + +Run ALL of these before doing anything else. Do not skip any. + +**2a. Check git configuration:** + +```bash +git config user.name +git config user.email +``` + +- If both are set: proceed. +- If missing and `gh` is authenticated: set them using `GH_USER` from Step 0: + +```bash +git config user.name "GH_USER" +git config user.email "GH_USER@users.noreply.github.com" +``` + +- If missing and `gh` is NOT authenticated: set reasonable defaults so commits + work. Use `"workflow-agent"` / `"workflow@agent.local"` as placeholders. + +**2b. Inventory existing remotes:** + +```bash +git remote -v +``` + +Note which remote points to the upstream repo and which (if any) points to +the user's fork. Common patterns: + +| Remote Name | URL Contains | Likely Role | +| --- | --- | --- | +| `origin` | upstream org | Upstream (read-only) | +| `origin` | user's name | Fork (read-write) | +| `fork` | user's name | Fork (read-write) | +| `upstream` | upstream org | Upstream (read-only) | + +**2c. Identify the upstream repo and its default branch:** + +If `gh` is authenticated: + +```bash +gh repo view --json nameWithOwner,defaultBranchRef --jq '{nameWithOwner, defaultBranch: .defaultBranchRef.name}' +``` + +This returns both the repo name and its default branch. Record +`UPSTREAM_OWNER/REPO` and `DEFAULT_BRANCH` from the output. + +If `gh` is NOT authenticated, extract from the upstream remote (identified in +Step 2b as `UPSTREAM_REMOTE`): + +```bash +# Repo name (use UPSTREAM_REMOTE, not necessarily origin — origin may be the fork) +git remote get-url UPSTREAM_REMOTE | sed -E 's#.*/([^/]+/[^/]+?)(\.git)?$#\1#' + +# Default branch +git remote show UPSTREAM_REMOTE 2>/dev/null | grep 'HEAD branch' | awk '{print $NF}' +``` + +Record the results as `UPSTREAM_OWNER/REPO` and `DEFAULT_BRANCH`. + +**Do not assume the default branch is `main`.** Many projects use `dev`, +`master`, `develop`, or other branch names. All subsequent steps that +reference the base branch MUST use `DEFAULT_BRANCH`. + +**2d. Check current branch and changes:** + +```bash +git status +git diff --stat +``` + +Confirm there are actual changes to commit. If there are no changes, stop +and tell the user. + +### Step 2e: Pre-flight Gate (REQUIRED) + +**Do not proceed to Step 3 until you have printed the following filled-in +table.** Every row must have a value or an explicit "unknown". If you cannot +fill in a row, that itself is important information that determines the flow. + +```text +Pre-flight summary: +| Placeholder | Value | +| -------------------- | ------------------ | +| AUTH_TYPE | ___ | +| GH_USER | ___ | +| UPSTREAM_OWNER/REPO | ___ | +| DEFAULT_BRANCH | ___ | +| UPSTREAM_REMOTE | ___ | +| EXISTING_REMOTES | ___ | +| HAS_CHANGES | yes / no | +| CURRENT_BRANCH | ___ | +``` + +### Expected Flow by Auth Type + +Now that you know `AUTH_TYPE`, here is what to expect for the rest of this +skill. Read the row that matches your `AUTH_TYPE` so you are prepared for +expected failures instead of surprised by them. + +**`user-token`:** Fork check → push to fork → `gh pr create` → done. +This is the happy path. + +**`github-app`:** Fork check → push to fork → `gh pr create` MAY fail +with "Resource not accessible by integration" (this is expected when the +bot is installed on the user's account, not the upstream org). If it fails, +provide the user a pre-filled compare URL (Step 8 fallback / Rung 2). Some +repos grant the app sufficient permissions, so always try `gh pr create` +first. + +**`none`:** You cannot push or create PRs. Stop and ask the user (see below), +then prepare the branch and PR description for them to submit manually. + +--- + +**If `AUTH_TYPE` is `none` — STOP and ask the user.** Present their +options clearly: + +> GitHub CLI authentication is not available in this environment, which means +> I can't push branches or create PRs directly. +> +> I can still prepare everything (branch, commit, PR description). To get it +> submitted, you have a few options: +> +> 1. **Set up `gh auth`** in this environment (`gh auth login`) and I'll +> handle the rest +> 2. **Tell me your fork URL** if you already have one — I may be able to +> push to it +> 3. **I'll prepare the branch and PR description**, and give you the exact +> commands to push and create the PR from your own machine +> +> Which would you prefer? + +**Wait for the user to respond.** Then proceed accordingly: + +- Option 1: User sets up auth → re-run Step 0, continue normally +- Option 2: User provides fork → set `FORK_OWNER` from it, skip to Step 4 +- Option 3: Continue through Steps 5–6 (branch, commit, PR description) but + skip Steps 7–8 (push, PR creation). At the end, provide the user with + the exact push and PR creation commands — but only ONE set of clear + instructions, not a wall of text + +**If `AUTH_TYPE` is `user-token` or `github-app`:** Continue to Step 3. +Do NOT skip Step 3 based on the account type — even org bots and GitHub +Apps need a fork. + +### Step 3: Ensure a Fork Exists + +You almost certainly do NOT have push access to the upstream repo. Use a fork. + +**Determining FORK_OWNER:** The fork owner is almost always `GH_USER` (the +authenticated GitHub username from Step 0). When the `gh repo list` command +below returns a fork, its `nameWithOwner` will be in `FORK_OWNER/REPO` format — +use the owner portion. If the user creates a new fork, `FORK_OWNER` = `GH_USER`. + +**Check if the user has a fork:** + +```bash +gh repo list GH_USER --fork --json nameWithOwner,parent --jq '.[] | select(.parent.owner.login == "UPSTREAM_OWNER" and .parent.name == "REPO") | .nameWithOwner' +``` + +Replace `GH_USER` with the value from Step 0. Replace `UPSTREAM_OWNER` and +`REPO` with the two parts of `UPSTREAM_OWNER/REPO` from Step 2c (e.g., for +`acme/myproject`, use `UPSTREAM_OWNER` = `acme` and `REPO` = `myproject`). + +**Note:** The GitHub API returns the parent as separate `.parent.owner.login` +and `.parent.name` fields — it does NOT have a `.parent.nameWithOwner` field. + +The output will be `FORK_OWNER/REPO` (e.g., `jsmith/myproject`). Record +the owner portion as `FORK_OWNER`. + +**If a fork exists:** use it — skip ahead to Step 4. + +**If NO fork exists — HARD STOP.** You cannot continue without a fork. +Do not try to push to upstream. Do not create a patch file. Do not try +API workarounds. Ask the user: + +> I don't see a fork of `UPSTREAM_OWNER/REPO` under your GitHub account +> (`GH_USER`). I need a fork to push the branch and create a PR. +> +> Would you like me to try creating one? If that doesn't work in this +> environment, you can create one yourself at: +> `https://github.com/UPSTREAM_OWNER/REPO/fork` +> +> Let me know when you're ready and I'll continue. + +**Then stop. Do not proceed until the user responds.** + +Once the user confirms, try creating the fork: + +```bash +gh repo fork UPSTREAM_OWNER/REPO --clone=false +``` + +- If this succeeds: continue to Step 4. +- If this fails (sandbox/permission issue): tell the user to create the fork + manually using the URL above. **Stop again and wait for the user to confirm + the fork exists before continuing.** + +Do not proceed to Step 4 until a fork actually exists and you have confirmed +it with: + +```bash +gh repo view GH_USER/REPO --json nameWithOwner --jq .nameWithOwner +``` + +### Step 4: Configure the Fork Remote + +Once a fork exists (or was found), ensure there's a git remote pointing to it. + +```bash +# Check if fork remote already exists +git remote -v | grep FORK_OWNER +``` + +If not present, add it: + +```bash +git remote add fork https://github.com/FORK_OWNER/REPO.git +``` + +**Set `FORK_REMOTE`** based on what you find: + +- If you just added a remote named `fork` → `FORK_REMOTE` = `fork` +- If `origin` already points to the fork (URL contains `FORK_OWNER`) → + `FORK_REMOTE` = `origin` +- If another existing remote points to the fork → `FORK_REMOTE` = that name + +Record `FORK_REMOTE` now. **Use it in all subsequent commands** (push, fetch, +ls-remote) instead of hardcoding `fork` or `origin`. + +### Step 4a: Check Fork Sync Status + +**Why this check exists:** When a user's fork is out of sync with upstream, +particularly when upstream has added workflow files (`.github/workflows/`) that +don't exist in the fork, pushing a feature branch can fail with a confusing +error like: + +```text +refusing to allow a GitHub App to create or update workflow `.github/workflows/foo.yml` without `workflows` permission +``` + +This happens because GitHub sees the push as "creating" workflow files (from +the fork's perspective), even though the feature branch simply includes files +that already exist in upstream. The GitHub App typically doesn't have `workflows` +permission by design. + +**Detection:** + +```bash +# Fetch both the fork and the upstream remote (identified in Step 2b) +git fetch FORK_REMOTE +git fetch UPSTREAM_REMOTE + +# Compare fork's DEFAULT_BRANCH against upstream's DEFAULT_BRANCH +# (don't rely on the local branch — it may be stale) +WORKFLOW_DIFF=$(git diff --name-only FORK_REMOTE/DEFAULT_BRANCH..UPSTREAM_REMOTE/DEFAULT_BRANCH -- .github/workflows/ 2>/dev/null) + +if [ -n "$WORKFLOW_DIFF" ]; then + echo "Fork is out of sync with upstream (workflow files differ):" + echo "$WORKFLOW_DIFF" +fi +``` + +`UPSTREAM_REMOTE` is whichever remote was identified as pointing to the +upstream org in Step 2b (typically `origin` when origin is the upstream repo, +or `upstream` if the user added it separately). + +**If workflow differences exist — attempt automated sync:** + +```bash +# Try to sync the fork's default branch with upstream +gh api --method POST repos/FORK_OWNER/REPO/merge-upstream -f branch=DEFAULT_BRANCH +``` + +- If this succeeds: fetch the fork again (`git fetch FORK_REMOTE`) and continue +- If this fails (usually due to workflow permission restrictions): guide the + user to sync manually + +**If automated sync fails — STOP and guide the user:** + +> Your fork is out of sync with upstream and contains workflow file differences. +> This prevents me from pushing because GitHub would interpret it as creating +> workflow files, which requires special permissions. +> +> Please sync your fork by either: +> +> 1. **Via GitHub web UI:** Visit https://github.com/FORK_OWNER/REPO and click +> "Sync fork" → "Update branch" +> +> 2. **Via command line** (may require `gh auth refresh -s workflow` first): +> ```bash +> gh repo sync FORK_OWNER/REPO --branch DEFAULT_BRANCH +> ``` +> +> Let me know when the sync is complete and I'll continue with the PR. + +**After user confirms sync — rebase and continue:** + +```bash +# Fetch the updated fork +git fetch FORK_REMOTE + +# Update local DEFAULT_BRANCH before creating the feature branch +git checkout DEFAULT_BRANCH +git rebase FORK_REMOTE/DEFAULT_BRANCH + +# Continue to Step 5 (create feature branch from updated DEFAULT_BRANCH) +``` + +### Step 5: Create a Branch + +```bash +git checkout -b BRANCH_NAME +``` + +Branch naming conventions — choose the prefix that matches the work type: + +- `bugfix/issue-NUMBER-SHORT_DESCRIPTION` — bug fixes +- `feature/issue-NUMBER-SHORT_DESCRIPTION` — new features +- `refactor/SHORT_DESCRIPTION` — refactoring +- `docs/SHORT_DESCRIPTION` — documentation changes + +Use kebab-case, keep it under 50 characters. Include the issue number when one +exists. + +If a branch already exists with the changes (from a prior phase), use +it instead of creating a new one. + +### Step 6: Stage and Commit + +Stage changes selectively (`git add path/to/files`, not `git add .`), review +with `git status`, then commit using conventional commit format: + +```bash +git commit -m "TYPE(SCOPE): SHORT_DESCRIPTION + +DETAILED_DESCRIPTION + +Fixes #ISSUE_NUMBER" +``` + +Where `TYPE` matches the work: `fix`, `feat`, `refactor`, `docs`, `test`, etc. + +Use prior artifacts (analysis, implementation notes) to write an +accurate commit message. Don't make up details. + +**Include the PR description in the commit body.** When a PR has a single +commit, GitHub auto-fills the PR description from the commit message. This +ensures the PR form is pre-populated even when `gh pr create` fails (a +common case for bot environments). If a `docs/pr-description.md` artifact +exists in the workflow's artifact directory, append its content after the +`Fixes #N` line. If it doesn't exist, +compose a brief PR body from session context (problem, approach, testing) +and include that instead. + +### Step 7: Push to Fork + +```bash +# Ensure git uses gh for authentication +gh auth setup-git + +# Push the branch (use FORK_REMOTE from Step 4) +git push -u FORK_REMOTE BRANCH_NAME +``` + +**If this fails:** + +- **Authentication / credential error**: Verify `gh auth status` succeeds and + that `gh auth setup-git` ran without errors. The user may need to + re-authenticate or the sandbox may be blocking network access. +- **Remote not found**: Verify the fork remote URL is correct. +- **Permission denied**: The fork remote may be pointing to upstream, not the + actual fork. Verify with `git remote get-url FORK_REMOTE`. + +### Step 8: Create the Draft PR + +**Important context on bot permissions:** If you are running as a GitHub App +bot (e.g., `ambient-code[bot]`), `gh pr create --repo UPSTREAM_OWNER/REPO` +may fail with `Resource not accessible by integration`. This happens when the +bot is installed on the **user's** account but not the upstream org. Some repos +grant the app sufficient permissions, so always try `gh pr create` first — +but if it fails with a 403, this is expected. Do not debug further; go +directly to the fallback below. + +**Try `gh pr create` first** (works for user tokens; may also work for bots +on some repos): + +```bash +gh pr create \ + --draft \ + --repo UPSTREAM_OWNER/REPO \ + --head FORK_OWNER:BRANCH_NAME \ + --base DEFAULT_BRANCH \ + --title "TYPE(SCOPE): SHORT_DESCRIPTION" \ + --body-file docs/pr-description.md +``` + +`--head` must be `FORK_OWNER:BRANCH_NAME` format (with the owner prefix) for +cross-fork PRs. If `--body-file` doesn't exist, use `--body` with content +composed from session artifacts. + +**If `gh pr create` fails (403, "Resource not accessible by integration", etc.):** + +This is a common and expected outcome when running as a GitHub App bot. +Do NOT retry, do NOT debug further, do NOT fall back to a patch file. Instead: + +1. **Write the PR description** to `docs/pr-description.md` in the + workflow's artifact directory (if not already written). + +2. **Give the user a pre-filled GitHub compare URL** with `title` and `body` + query parameters so the PR form opens fully populated: + + ```text + https://github.com/UPSTREAM_OWNER/REPO/compare/DEFAULT_BRANCH...FORK_OWNER:BRANCH_NAME?expand=1&title=URL_ENCODED_TITLE&body=URL_ENCODED_BODY + ``` + + URL-encode the title and body. If the encoded URL would exceed ~8KB + (browser limit), omit the `body` parameter — the commit message body + from Step 6 will still auto-fill the description for single-commit PRs. + +3. **Remind the user** to check "Create draft pull request" if they want + it as a draft. + +4. **Provide clone-and-checkout commands** so the user can test locally: + + ```text + ## Test the branch locally + + # Fresh clone: + git clone https://github.com/FORK_OWNER/REPO.git + cd REPO + git checkout BRANCH_NAME + + # Or if you already have the repo cloned: + git remote add fork https://github.com/FORK_OWNER/REPO.git # if not already added + git fetch fork BRANCH_NAME + git checkout -b BRANCH_NAME fork/BRANCH_NAME + ``` + +**If "branch not found"**: The push in Step 7 may have failed silently. +Verify with `git ls-remote FORK_REMOTE BRANCH_NAME`. + +### Step 9: Confirm and Report + +After the PR is created (or the URL is provided), summarize: + +- PR URL (or manual creation URL with pre-filled title and body) +- What was included in the PR +- What branch it targets +- Clone-and-checkout commands for local testing (if the PR was created via + compare URL fallback — the user may need to verify the fix on their machine) +- Any follow-up actions needed (mark ready for review, add reviewers, etc.) + +## Fallback Ladder + +When something goes wrong, work down this list. **Do not skip to lower +rungs** — always try the higher options first. + +### Rung 1: Fix and Retry (preferred) + +Most failures have a specific cause (wrong remote, auth scope, branch name). +Diagnose it using the Error Recovery table and retry. + +### Rung 2: Manual PR via GitHub Compare URL + +If `gh pr create` fails but the branch is pushed to the fork (this is a +**common and expected** outcome when running as a GitHub App bot): + +1. **Write the PR body** to `docs/pr-description.md` in the artifact directory +2. **Provide the compare URL with `title` and `body` query params** so the + PR form opens fully populated (see Step 8 failure path for format) +3. **Provide clone-and-checkout commands** for local testing +4. **Note**: between the commit message body (Step 6) and the URL params, + the user should see the PR description auto-filled with no manual copying + +### Rung 3: User creates fork, you push and PR + +If no fork exists and automated forking fails: + +1. Give the user the fork URL: `https://github.com/UPSTREAM_OWNER/REPO/fork` +2. **Wait for the user to confirm the fork exists** +3. Add the fork remote, push the branch, create the PR + +### Rung 4: Patch file (absolute last resort) + +Only if ALL of the above fail — for example, the user has no GitHub account, +or network access is completely blocked: + +1. Generate a patch: `git diff > changes.patch` +2. Write it to the workflow's artifact directory as `changes.patch` +3. Explain to the user how to apply it: `git apply changes.patch` +4. **Acknowledge this is a degraded experience** and explain what prevented + the normal flow + +## Output + +- The PR URL (printed to the user) +- Optionally writes `docs/pr-description.md` to the artifact directory if + it didn't already exist + +## Usage Examples + +**After completing the workflow:** + +```text +/pr +``` + +**With a specific issue reference:** + +```text +/pr Fixes #47 - include all documented tool types in OpenAPI spec +``` + +**When the fork is already set up:** + +```text +/pr --repo openresponses/openresponses +``` + +## Error Recovery Quick Reference + +| Symptom | Cause | Fix | +| --- | --- | --- | +| `gh auth status` fails | Not logged in | User must run `gh auth login` | +| `gh` commands return 401 "Bad credentials" | `GITHUB_TOKEN` expired mid-session | Query git credential helper for fresh token, then `gh auth login --with-token` (see Step 0 recovery) | +| `git push` "could not read Username" | git credential helper not configured | Run `gh auth setup-git` then retry push | +| `git push` permission denied | Pushing to upstream, not fork | Verify remote URL, switch to fork | +| `git push` "refusing to allow...without `workflows` permission" | Fork out of sync with upstream (missing workflow files) | Run Step 4a: sync fork, then rebase and retry push | +| `gh pr create` 403 / "Resource not accessible" | Bot installed on user, not upstream org | Give user the compare URL (Rung 2) — this is expected for most bot setups | +| `gh repo fork` fails | Sandbox blocks forking | User creates fork manually | +| Branch not found on remote | Push failed silently | Re-run `git push`, check network | +| No changes to commit | Changes already committed or not staged | Check `git status`, `git log` | +| Wrong base branch | `DEFAULT_BRANCH` wasn't detected or was overridden | Re-run `gh repo view --json defaultBranchRef` and update `DEFAULT_BRANCH` | + +## Notes + +- This skill assumes the implementation work (code changes, tests) is already + done before invoking `/pr`. +- If a `docs/pr-description.md` artifact already exists in the workflow's + artifact directory, this skill will use it. +- If no PR description artifact exists, this skill creates a minimal PR body + from session context (conversation history, prior artifacts). +- The fork workflow is the standard for open-source contributions. Even if the + user has write access to upstream, using a fork keeps the upstream clean. + +## When This Phase Is Done + +Report your results: + +- PR URL (or manual creation URL if automated creation wasn't possible) +- What was included +- Any follow-up actions needed (mark ready for review, add reviewers, etc.) + diff --git a/workflows/docs-updater/CLAUDE.md b/workflows/docs-updater/CLAUDE.md new file mode 100644 index 00000000..5e532817 --- /dev/null +++ b/workflows/docs-updater/CLAUDE.md @@ -0,0 +1,42 @@ +# Docs Updater Workflow + +Keep documentation in sync with code changes through these phases: + +1. **Review** — Discover which doc files need updates and preview changes +2. **Update** — Apply accepted changes to documentation files +3. **Open PR** — Submit a documentation pull request + +The `/index` command is available separately for building semantic indexes. + +All phases are implemented as skills. The controller skill manages phase +transitions and recommendations. Artifacts go in `artifacts/docs-updater/`. + +## Principles + +- **Targeted**: Only update docs that would become incorrect because of the code change +- **Format-aware**: Respect the existing format of each file without mixing syntax +- **Minimal changes**: Add only what the diff justifies. Do not restructure, rewrite, or "improve" +- **Show evidence**: When selecting a file, explain exactly what would become incorrect +- **When in doubt, do NOT update**: Prefer skipping a file over making an unnecessary change + +## Hard Limits + +- No creating new documentation files — only update existing ones +- No modifying code files — this workflow operates on docs only +- No writing content that is not directly justified by the diff +- No force-push or destructive git operations +- No logging tokens or secrets + +## Safety + +- Present file selections to the user before generating content +- Always use `AskUserQuestion` between phases — never auto-advance +- Flag uncertainty: if unsure whether a file needs updating, do NOT include it + +## Working With Code and Docs Locations + +- The code and docs may live in separate repos or in the same repo (e.g., a `docs/` subfolder) +- The code location is the source of the diff +- The docs location is the target of changes +- Keep track of which directory you are in at all times +- All git operations for the PR happen in the docs location diff --git a/workflows/docs-updater/README.md b/workflows/docs-updater/README.md new file mode 100644 index 00000000..50306a28 --- /dev/null +++ b/workflows/docs-updater/README.md @@ -0,0 +1,99 @@ +# Docs Updater Workflow + +Analyze code changes and keep documentation in sync. This workflow discovers which doc files are affected by a code diff, generates format-aware updates, and opens a docs PR. + +## How It Works + +1. You provide a code change (PR URL, branch diff, or local changes) and point to where your docs live (separate repo or subfolder in the same repo) +2. The workflow extracts identifiers from the changed code and searches your docs for matches +3. It evaluates each match to determine if the doc content is now stale +4. You review and select which files to update +5. It generates minimal, format-correct updates to those files +6. It opens a draft PR with the documentation updates + +## Workflow Phases + +| Phase | Purpose | +|-------|---------| +| **Setup** | Establish the diff source and docs location (automatic) | +| **Review** | Discover affected files and preview proposed changes | +| **Update** | Apply accepted changes to documentation files | +| **Open PR** | Push changes and create a draft pull request | + +The `/index` command is available separately for building semantic indexes +on large docs locations. + +### Typical Flow + +```text +setup → review → (select files) → update → open PR +``` + +## Getting Started + +### Scenario 1: You Have a PR URL + +1. Start the workflow +2. Provide the PR URL when asked +3. Point to where docs live (subfolder, separate repo, or current directory) +4. Choose "Review" to see which docs need updating + +### Scenario 2: You Have Local Changes + +1. Start the workflow from within the code repo +2. Choose "local uncommitted changes" when asked +3. Point to where docs live +4. Choose "Review" to see which docs need updating + +### Scenario 3: Large Docs Location, First Run + +1. Run `/index` to build semantic indexes before starting the workflow +2. Start the workflow and complete setup +3. Choose "Review" — discovery will use the indexes automatically + +## The Semantic Index System (Optional) + +For most repos, the default discovery (identifier matching + grep) works well. On large docs locations with many folders, you can optionally build semantic indexes via `/index` for faster discovery. + +Each folder gets an index that captures: + +- What the folder documents (overview) +- What each file covers (file summaries) +- What code changes would make these docs outdated +- Key technical concepts and terms + +With indexes, the workflow narrows to relevant folders first, then scans individual files — skipping folders that aren't affected by the diff. + +**Important**: Indexes are only valuable if you commit and push them so future runs can reuse them. Without committing, the indexes are lost when the session ends and you'd be better off using the default grep-based discovery. Indexes are hash-based — they only rebuild when doc files change. + +## Format Support + +The workflow handles any text-based documentation format, including Markdown, AsciiDoc, reStructuredText, plain text, HTML, and YAML. + +## Directory Structure + +```text +docs-updater/ +├── .ambient/ +│ └── ambient.json # Workflow configuration +├── .claude/ +│ ├── commands/ +│ │ └── index.md # /index command for building semantic indexes +│ └── skills/ +│ ├── controller/SKILL.md # Phase management and transitions +│ ├── discovery/SKILL.md # File discovery (grep or index-assisted) +│ ├── generation/SKILL.md # Content generation +│ └── pr/SKILL.md # PR creation +├── CLAUDE.md # Behavioral guidelines +└── README.md # This file +``` + +## Troubleshooting + +**No docs found**: Make sure the docs location path is correct and contains documentation files. + +**Empty diff**: Verify the code change source — the PR URL must be accessible, or local changes must be uncommitted. + +**Too many matches**: If discovery returns too many candidates, the two-pass evaluation (quick scan then deep read) should filter out false positives. If it's still noisy, consider building indexes via `/index`. + +**Format issues in generated content**: If the generated update mixes syntax (e.g., Markdown headers in an AsciiDoc file), report it — the generation skill should never mix formats.