#!/usr/bin/env bash## Analyze code changes and suggest documentation updates.## Usage:# script/docs-suggest --pr 49177 # Analyze a PR (immediate mode)# script/docs-suggest --commit abc123 # Analyze a commit# script/docs-suggest --diff file.patch # Analyze a diff file# script/docs-suggest --staged # Analyze staged changes## Modes:# --batch Append suggestions to batch file for later PR (default for main)# --immediate Output suggestions directly (default for cherry-picks)## Options:# --dry-run Show assembled context without calling LLM# --output FILE Write suggestions to file instead of stdout (immediate mode)# --verbose Show detailed progress# --model NAME Override default model# --preview Add preview callout to suggested docs (auto-detected)## Batch mode:# Suggestions are appended to docs/.suggestions/pending.md# Use script/docs-suggest-publish to create a PR from batched suggestions## Examples:# # Analyze a PR to main (batches by default)# script/docs-suggest --pr 49100## # Analyze a cherry-pick PR (immediate by default)# script/docs-suggest --pr 49152## # Force immediate output for testing# script/docs-suggest --pr 49100 --immediate## # Dry run to see context# script/docs-suggest --pr 49100 --dry-run set -euo pipefail # DefaultsMODE=""TARGET=""DRY_RUN=falseVERBOSE=falseOUTPUT=""MODEL="${DROID_MODEL:-claude-sonnet-4-5-20250929}"OUTPUT_MODE="" # batch or immediate, auto-detected if not setADD_PREVIEW_CALLOUT="" # auto-detected if not set # Colors for outputRED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[0;33m'BLUE='\033[0;34m'NC='\033[0m' # No Color log() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${BLUE}[docs-suggest]${NC} $*" >&2 fi} error() { echo -e "${RED}Error:${NC} $*" >&2 exit 1} warn() { echo -e "${YELLOW}Warning:${NC} $*" >&2} # Parse argumentswhile [[ $# -gt 0 ]]; do case $1 in --pr) MODE="pr" TARGET="$2" shift 2 ;; --commit) MODE="commit" TARGET="$2" shift 2 ;; --diff) MODE="diff" TARGET="$2" shift 2 ;; --staged) MODE="staged" shift ;; --batch) OUTPUT_MODE="batch" shift ;; --immediate) OUTPUT_MODE="immediate" shift ;; --preview) ADD_PREVIEW_CALLOUT="true" shift ;; --no-preview) ADD_PREVIEW_CALLOUT="false" shift ;; --dry-run) DRY_RUN=true shift ;; --verbose) VERBOSE=true shift ;; --output) OUTPUT="$2" shift 2 ;; --model) MODEL="$2" shift 2 ;; -h|--help) head -42 "$0" | tail -40 exit 0 ;; *) error "Unknown option: $1" ;; esacdone # Validate modeif [[ -z "$MODE" ]]; then error "Must specify one of: --pr, --commit, --diff, or --staged"fi # Get repo rootREPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"cd "$REPO_ROOT" # Batch file locationBATCH_DIR="$REPO_ROOT/docs/.suggestions"BATCH_FILE="$BATCH_DIR/pending.md" # Temp directory for context assemblyTMPDIR=$(mktemp -d)trap 'rm -rf "$TMPDIR"' EXIT log "Mode: $MODE, Target: ${TARGET:-staged changes}"log "Temp dir: $TMPDIR" # ============================================================================# Step 1: Get the diff and detect context# ============================================================================ get_diff() { case $MODE in pr) if ! command -v gh &> /dev/null; then error "gh CLI required for --pr mode. Install: https://cli.github.com" fi log "Fetching PR #$TARGET info..." # Get PR metadata for auto-detection PR_JSON=$(gh pr view "$TARGET" --json baseRefName,title,number) PR_BASE=$(echo "$PR_JSON" | grep -o '"baseRefName":"[^"]*"' | cut -d'"' -f4) PR_TITLE=$(echo "$PR_JSON" | grep -o '"title":"[^"]*"' | cut -d'"' -f4) PR_NUMBER=$(echo "$PR_JSON" | grep -o '"number":[0-9]*' | cut -d':' -f2) log "PR #$PR_NUMBER: $PR_TITLE (base: $PR_BASE)" # Auto-detect output mode based on target branch if [[ -z "$OUTPUT_MODE" ]]; then if [[ "$PR_BASE" == "main" ]]; then OUTPUT_MODE="batch" log "Auto-detected: batch mode (PR targets main)" else OUTPUT_MODE="immediate" log "Auto-detected: immediate mode (PR targets $PR_BASE)" fi fi # Auto-detect preview callout if [[ -z "$ADD_PREVIEW_CALLOUT" ]]; then if [[ "$PR_BASE" == "main" ]]; then ADD_PREVIEW_CALLOUT="true" log "Auto-detected: will add preview callout (new feature going to main)" else # Cherry-pick to release branch - check if it's preview or stable ADD_PREVIEW_CALLOUT="false" log "Auto-detected: no preview callout (cherry-pick)" fi fi # Store metadata for batch mode echo "$PR_NUMBER" > "$TMPDIR/pr_number" echo "$PR_TITLE" > "$TMPDIR/pr_title" echo "$PR_BASE" > "$TMPDIR/pr_base" log "Fetching PR #$TARGET diff..." gh pr diff "$TARGET" > "$TMPDIR/changes.diff" gh pr diff "$TARGET" --name-only > "$TMPDIR/changed_files.txt" ;; commit) log "Getting commit $TARGET diff..." git show "$TARGET" --format="" > "$TMPDIR/changes.diff" git show "$TARGET" --format="" --name-only > "$TMPDIR/changed_files.txt" # Default to immediate for commits OUTPUT_MODE="${OUTPUT_MODE:-immediate}" ADD_PREVIEW_CALLOUT="${ADD_PREVIEW_CALLOUT:-false}" ;; diff) if [[ ! -f "$TARGET" ]]; then error "Diff file not found: $TARGET" fi log "Using provided diff file..." cp "$TARGET" "$TMPDIR/changes.diff" grep -E '^\+\+\+ b/' "$TARGET" | sed 's|^+++ b/||' > "$TMPDIR/changed_files.txt" || true OUTPUT_MODE="${OUTPUT_MODE:-immediate}" ADD_PREVIEW_CALLOUT="${ADD_PREVIEW_CALLOUT:-false}" ;; staged) log "Getting staged changes..." git diff --cached > "$TMPDIR/changes.diff" git diff --cached --name-only > "$TMPDIR/changed_files.txt" OUTPUT_MODE="${OUTPUT_MODE:-immediate}" ADD_PREVIEW_CALLOUT="${ADD_PREVIEW_CALLOUT:-false}" ;; esac if [[ ! -s "$TMPDIR/changes.diff" ]]; then error "No changes found" fi log "Found $(wc -l < "$TMPDIR/changed_files.txt" | tr -d ' ') changed files" log "Output mode: $OUTPUT_MODE, Preview callout: $ADD_PREVIEW_CALLOUT"} # ============================================================================# Step 2: Filter to relevant changes# ============================================================================ filter_changes() { log "Filtering to documentation-relevant changes..." # Keep only source code changes (not tests, not CI, not docs themselves) grep -E '^crates/.*\.rs$' "$TMPDIR/changed_files.txt" | \ grep -v '_test\.rs$' | \ grep -v '/tests/' | \ grep -v '/test_' > "$TMPDIR/source_files.txt" || true # Also track if settings/keybindings changed grep -E '(settings|keymap|actions)' "$TMPDIR/changed_files.txt" > "$TMPDIR/config_files.txt" || true local source_count=$(wc -l < "$TMPDIR/source_files.txt" | tr -d ' ') local config_count=$(wc -l < "$TMPDIR/config_files.txt" | tr -d ' ') log "Relevant files: $source_count source, $config_count config" if [[ "$source_count" -eq 0 && "$config_count" -eq 0 ]]; then echo "No documentation-relevant changes detected (only tests, CI, or docs modified)." exit 0 fi} # ============================================================================# Step 3: Assemble context# ============================================================================ assemble_context() { log "Assembling context..." # Start the prompt cat > "$TMPDIR/prompt.md" << 'PROMPT_HEADER'# Documentation Suggestion Request You are analyzing code changes to determine if documentation updates are needed. Before analysis, read and follow these rule files:- `.rules`- `docs/.rules` ## Your Task 1. Analyze the diff below for user-facing changes2. Determine if any documentation updates are warranted3. If yes, provide specific, actionable suggestions4. If no, explain why no updates are needed ## Guidelines PROMPT_HEADER # Add conventions log "Adding documentation conventions..." cat >> "$TMPDIR/prompt.md" << 'CONVENTIONS'### Documentation Conventions - **Voice**: Second person ("you"), present tense, direct and concise- **No hedging**: Avoid "simply", "just", "easily"- **Settings pattern**: Show Settings Editor UI first, then JSON as alternative- **Keybindings**: Use `{#kb action::Name}` syntax, not hardcoded keys- **Terminology**: "folder" not "directory", "project" not "workspace", "Settings Editor" not "settings UI"- **SEO keyword targeting**: For each docs page you suggest updating, choose one primary keyword/intent phrase using the page's user intent- **SEO metadata**: Every updated/new docs page should include frontmatter with `title` and `description` (single-line `key: value` entries)- **Metadata quality**: Titles should clearly state page intent (~50-60 chars), descriptions should summarize the reader outcome (~140-160 chars)- **Keyword usage**: Use the primary keyword naturally in frontmatter and in page body at least once; never keyword-stuff- **SEO structure**: Keep exactly one H1 and preserve logical H1→H2→H3 hierarchy- **Internal links minimum**: Non-reference pages should include at least 3 useful internal docs links; reference pages can include fewer when extra links would be noise- **Marketing links**: For main feature pages, include a relevant `zed.dev` marketing link alongside docs links ### Brand Voice Rubric (Required) For suggested doc text, apply the brand rubric scoring exactly and only pass textthat scores 4+ on every criterion: | Criterion || -------------------- || Technical Grounding || Natural Syntax || Quiet Confidence || Developer Respect || Information Priority || Specificity || Voice Consistency || Earned Claims | Pass threshold: all criteria 4+ (minimum 32/40 total). Also reject suggestions containing obvious taboo phrasing (hype, emotionalmanipulation, or marketing-style superlatives). For every docs file you suggest changing, treat the entire file as in scope forbrand review (not only the edited section). Include any additional full-filevoice fixes needed to reach passing rubric scores. ### What Requires Documentation - New user-facing features or commands- Changed keybindings or default behaviors- New or modified settings- Deprecated or removed functionality ### What Does NOT Require Documentation - Internal refactoring without behavioral changes- Performance optimizations (unless user-visible)- Bug fixes that restore documented behavior- Test changes, CI changesCONVENTIONS # Add preview callout instructions if needed if [[ "$ADD_PREVIEW_CALLOUT" == "true" ]]; then # Get current preview version for modification callouts local preview_version preview_version=$(gh release list --limit 5 2>/dev/null | grep -E '\-pre\s' | head -1 | grep -oE 'v[0-9]+\.[0-9]+' | head -1 || echo "v0.XXX") preview_version="${preview_version#v}" # Remove leading 'v' cat >> "$TMPDIR/prompt.md" << PREVIEW_INSTRUCTIONS ### Preview Release Callouts This change is going into Zed Preview first. Use the appropriate callout based on the type of change: #### For NEW/ADDITIVE features (new commands, new settings, new UI elements): \`\`\`markdown> **Preview:** This feature is available in Zed Preview. It will be included in the next Stable release.\`\`\` #### For BEHAVIOR MODIFICATIONS (changed defaults, altered behavior of existing features): \`\`\`markdown> **Changed in Preview (v${preview_version}).** See [release notes](/releases#${preview_version}).\`\`\` **Guidelines:**- Use the "Preview" callout for entirely new features or sections- Use the "Changed in Preview" callout when modifying documentation of existing behavior- Place callouts immediately after the section heading, before any content- Both callout types will be stripped when the feature ships to StablePREVIEW_INSTRUCTIONS fi echo "" >> "$TMPDIR/prompt.md" echo "## Changed Files" >> "$TMPDIR/prompt.md" echo "" >> "$TMPDIR/prompt.md" echo '```' >> "$TMPDIR/prompt.md" cat "$TMPDIR/changed_files.txt" >> "$TMPDIR/prompt.md" echo '```' >> "$TMPDIR/prompt.md" echo "" >> "$TMPDIR/prompt.md" # Add the diff (truncated if huge) echo "## Code Diff" >> "$TMPDIR/prompt.md" echo "" >> "$TMPDIR/prompt.md" local diff_lines=$(wc -l < "$TMPDIR/changes.diff" | tr -d ' ') if [[ "$diff_lines" -gt 2000 ]]; then warn "Diff is large ($diff_lines lines), truncating to 2000 lines" echo '```diff' >> "$TMPDIR/prompt.md" head -2000 "$TMPDIR/changes.diff" >> "$TMPDIR/prompt.md" echo "" >> "$TMPDIR/prompt.md" echo "[... truncated, $((diff_lines - 2000)) more lines ...]" >> "$TMPDIR/prompt.md" echo '```' >> "$TMPDIR/prompt.md" else echo '```diff' >> "$TMPDIR/prompt.md" cat "$TMPDIR/changes.diff" >> "$TMPDIR/prompt.md" echo '```' >> "$TMPDIR/prompt.md" fi # Add output format instructions cat >> "$TMPDIR/prompt.md" << 'OUTPUT_FORMAT' ## Output Format Respond with ONE of these formats: ### If documentation updates ARE needed: ```markdown## Documentation Suggestions ### Summary[1-2 sentence summary of what changed and why docs need updating] ### Suggested Changes #### 1. [docs/src/path/to/file.md]- **Section**: [existing section to update, or "New section"]- **Change**: [Add/Update/Remove]- **Target keyword**: [single keyword/intent phrase for this page]- **Frontmatter**: ```yaml --- title: ... description: ... --- ```- **Links**: [List at least 3 internal docs links for non-reference pages; if this is a main feature page, include one relevant `zed.dev` marketing link]- **Suggestion**: [Specific text or description of what to add/change]- **Full-file brand pass**: [Required: yes. Note any additional voice edits elsewhere in the same file needed to pass rubric across the entire file.]- **Brand voice scorecard**: | Criterion | Score | Notes | | -------------------- | ----- | ----- | | Technical Grounding | /5 | | | Natural Syntax | /5 | | | Quiet Confidence | /5 | | | Developer Respect | /5 | | | Information Priority | /5 | | | Specificity | /5 | | | Voice Consistency | /5 | | | Earned Claims | /5 | | | **TOTAL** | /40 | | Pass threshold: all criteria 4+. #### 2. [docs/src/another/file.md]... ### Notes for Reviewer[Any context or uncertainty worth flagging]``` ### If NO documentation updates are needed: ```markdown## No Documentation Updates Needed **Reason**: [Brief explanation - e.g., "Internal refactoring only", "Test changes", "Bug fix restoring existing behavior"] **Changes reviewed**:- [Brief summary of what the code changes do]- [Why they don't affect user-facing documentation]``` Be conservative. Only suggest documentation changes when there's a clear user-facing impact.OUTPUT_FORMAT log "Context assembled: $(wc -l < "$TMPDIR/prompt.md" | tr -d ' ') lines"} Lines 1–500 of 620. Load more
#!/usr/bin/env bash # # Analyze code changes and suggest documentation updates. # # Usage: # script/docs-suggest --pr 49177 # Analyze a PR (immediate mode) # script/docs-suggest --commit abc123 # Analyze a commit # script/docs-suggest --diff file.patch # Analyze a diff file # script/docs-suggest --staged # Analyze staged changes # # Modes: # --batch Append suggestions to batch file for later PR (default for main) # --immediate Output suggestions directly (default for cherry-picks) # # Options: # --dry-run Show assembled context without calling LLM # --output FILE Write suggestions to file instead of stdout (immediate mode) # --verbose Show detailed progress # --model NAME Override default model # --preview Add preview callout to suggested docs (auto-detected) # # Batch mode: # Suggestions are appended to docs/.suggestions/pending.md # Use script/docs-suggest-publish to create a PR from batched suggestions # # Examples: # # Analyze a PR to main (batches by default) # script/docs-suggest --pr 49100 # # # Analyze a cherry-pick PR (immediate by default) # script/docs-suggest --pr 49152 # # # Force immediate output for testing # script/docs-suggest --pr 49100 --immediate # # # Dry run to see context # script/docs-suggest --pr 49100 --dry-run set -euo pipefail # Defaults MODE="" TARGET="" DRY_RUN=false VERBOSE=false OUTPUT="" MODEL="${DROID_MODEL:-claude-sonnet-4-5-20250929}" OUTPUT_MODE="" # batch or immediate, auto-detected if not set ADD_PREVIEW_CALLOUT="" # auto-detected if not set # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color log() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${BLUE}[docs-suggest]${NC} $*" >&2 fi } error() { echo -e "${RED}Error:${NC} $*" >&2 exit 1 } warn() { echo -e "${YELLOW}Warning:${NC} $*" >&2 } # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --pr) MODE="pr" TARGET="$2" shift 2 ;; --commit) MODE="commit" TARGET="$2" shift 2 ;; --diff) MODE="diff" TARGET="$2" shift 2 ;; --staged) MODE="staged" shift ;; --batch) OUTPUT_MODE="batch" shift ;; --immediate) OUTPUT_MODE="immediate" shift ;; --preview) ADD_PREVIEW_CALLOUT="true" shift ;; --no-preview) ADD_PREVIEW_CALLOUT="false" shift ;; --dry-run) DRY_RUN=true shift ;; --verbose) VERBOSE=true shift ;; --output) OUTPUT="$2" shift 2 ;; --model) MODEL="$2" shift 2 ;; -h|--help) head -42 "$0" | tail -40 exit 0 ;; *) error "Unknown option: $1" ;; esac done # Validate mode if [[ -z "$MODE" ]]; then error "Must specify one of: --pr, --commit, --diff, or --staged" fi # Get repo root REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$REPO_ROOT" # Batch file location BATCH_DIR="$REPO_ROOT/docs/.suggestions" BATCH_FILE="$BATCH_DIR/pending.md" # Temp directory for context assembly TMPDIR=$(mktemp -d) trap 'rm -rf "$TMPDIR"' EXIT log "Mode: $MODE, Target: ${TARGET:-staged changes}" log "Temp dir: $TMPDIR" # ============================================================================ # Step 1: Get the diff and detect context # ============================================================================ get_diff() { case $MODE in pr) if ! command -v gh &> /dev/null; then error "gh CLI required for --pr mode. Install: https://cli.github.com" fi log "Fetching PR #$TARGET info..." # Get PR metadata for auto-detection PR_JSON=$(gh pr view "$TARGET" --json baseRefName,title,number) PR_BASE=$(echo "$PR_JSON" | grep -o '"baseRefName":"[^"]*"' | cut -d'"' -f4) PR_TITLE=$(echo "$PR_JSON" | grep -o '"title":"[^"]*"' | cut -d'"' -f4) PR_NUMBER=$(echo "$PR_JSON" | grep -o '"number":[0-9]*' | cut -d':' -f2) log "PR #$PR_NUMBER: $PR_TITLE (base: $PR_BASE)" # Auto-detect output mode based on target branch if [[ -z "$OUTPUT_MODE" ]]; then if [[ "$PR_BASE" == "main" ]]; then OUTPUT_MODE="batch" log "Auto-detected: batch mode (PR targets main)" else OUTPUT_MODE="immediate" log "Auto-detected: immediate mode (PR targets $PR_BASE)" fi fi # Auto-detect preview callout if [[ -z "$ADD_PREVIEW_CALLOUT" ]]; then if [[ "$PR_BASE" == "main" ]]; then ADD_PREVIEW_CALLOUT="true" log "Auto-detected: will add preview callout (new feature going to main)" else # Cherry-pick to release branch - check if it's preview or stable ADD_PREVIEW_CALLOUT="false" log "Auto-detected: no preview callout (cherry-pick)" fi fi # Store metadata for batch mode echo "$PR_NUMBER" > "$TMPDIR/pr_number" echo "$PR_TITLE" > "$TMPDIR/pr_title" echo "$PR_BASE" > "$TMPDIR/pr_base" log "Fetching PR #$TARGET diff..." gh pr diff "$TARGET" > "$TMPDIR/changes.diff" gh pr diff "$TARGET" --name-only > "$TMPDIR/changed_files.txt" ;; commit) log "Getting commit $TARGET diff..." git show "$TARGET" --format="" > "$TMPDIR/changes.diff" git show "$TARGET" --format="" --name-only > "$TMPDIR/changed_files.txt" # Default to immediate for commits OUTPUT_MODE="${OUTPUT_MODE:-immediate}" ADD_PREVIEW_CALLOUT="${ADD_PREVIEW_CALLOUT:-false}" ;; diff) if [[ ! -f "$TARGET" ]]; then error "Diff file not found: $TARGET" fi log "Using provided diff file..." cp "$TARGET" "$TMPDIR/changes.diff" grep -E '^\+\+\+ b/' "$TARGET" | sed 's|^+++ b/||' > "$TMPDIR/changed_files.txt" || true OUTPUT_MODE="${OUTPUT_MODE:-immediate}" ADD_PREVIEW_CALLOUT="${ADD_PREVIEW_CALLOUT:-false}" ;; staged) log "Getting staged changes..." git diff --cached > "$TMPDIR/changes.diff" git diff --cached --name-only > "$TMPDIR/changed_files.txt" OUTPUT_MODE="${OUTPUT_MODE:-immediate}" ADD_PREVIEW_CALLOUT="${ADD_PREVIEW_CALLOUT:-false}" ;; esac if [[ ! -s "$TMPDIR/changes.diff" ]]; then error "No changes found" fi log "Found $(wc -l < "$TMPDIR/changed_files.txt" | tr -d ' ') changed files" log "Output mode: $OUTPUT_MODE, Preview callout: $ADD_PREVIEW_CALLOUT" } # ============================================================================ # Step 2: Filter to relevant changes # ============================================================================ filter_changes() { log "Filtering to documentation-relevant changes..." # Keep only source code changes (not tests, not CI, not docs themselves) grep -E '^crates/.*\.rs$' "$TMPDIR/changed_files.txt" | \ grep -v '_test\.rs$' | \ grep -v '/tests/' | \ grep -v '/test_' > "$TMPDIR/source_files.txt" || true # Also track if settings/keybindings changed grep -E '(settings|keymap|actions)' "$TMPDIR/changed_files.txt" > "$TMPDIR/config_files.txt" || true local source_count=$(wc -l < "$TMPDIR/source_files.txt" | tr -d ' ') local config_count=$(wc -l < "$TMPDIR/config_files.txt" | tr -d ' ') log "Relevant files: $source_count source, $config_count config" if [[ "$source_count" -eq 0 && "$config_count" -eq 0 ]]; then echo "No documentation-relevant changes detected (only tests, CI, or docs modified)." exit 0 fi } # ============================================================================ # Step 3: Assemble context # ============================================================================ assemble_context() { log "Assembling context..." # Start the prompt cat > "$TMPDIR/prompt.md" << 'PROMPT_HEADER' # Documentation Suggestion Request You are analyzing code changes to determine if documentation updates are needed. Before analysis, read and follow these rule files: - `.rules` - `docs/.rules` ## Your Task 1. Analyze the diff below for user-facing changes 2. Determine if any documentation updates are warranted 3. If yes, provide specific, actionable suggestions 4. If no, explain why no updates are needed ## Guidelines PROMPT_HEADER # Add conventions log "Adding documentation conventions..." cat >> "$TMPDIR/prompt.md" << 'CONVENTIONS' ### Documentation Conventions - **Voice**: Second person ("you"), present tense, direct and concise - **No hedging**: Avoid "simply", "just", "easily" - **Settings pattern**: Show Settings Editor UI first, then JSON as alternative - **Keybindings**: Use `{#kb action::Name}` syntax, not hardcoded keys - **Terminology**: "folder" not "directory", "project" not "workspace", "Settings Editor" not "settings UI" - **SEO keyword targeting**: For each docs page you suggest updating, choose one primary keyword/intent phrase using the page's user intent - **SEO metadata**: Every updated/new docs page should include frontmatter with `title` and `description` (single-line `key: value` entries) - **Metadata quality**: Titles should clearly state page intent (~50-60 chars), descriptions should summarize the reader outcome (~140-160 chars) - **Keyword usage**: Use the primary keyword naturally in frontmatter and in page body at least once; never keyword-stuff - **SEO structure**: Keep exactly one H1 and preserve logical H1→H2→H3 hierarchy - **Internal links minimum**: Non-reference pages should include at least 3 useful internal docs links; reference pages can include fewer when extra links would be noise - **Marketing links**: For main feature pages, include a relevant `zed.dev` marketing link alongside docs links ### Brand Voice Rubric (Required) For suggested doc text, apply the brand rubric scoring exactly and only pass text that scores 4+ on every criterion: | Criterion | | -------------------- | | Technical Grounding | | Natural Syntax | | Quiet Confidence | | Developer Respect | | Information Priority | | Specificity | | Voice Consistency | | Earned Claims | Pass threshold: all criteria 4+ (minimum 32/40 total). Also reject suggestions containing obvious taboo phrasing (hype, emotional manipulation, or marketing-style superlatives). For every docs file you suggest changing, treat the entire file as in scope for brand review (not only the edited section). Include any additional full-file voice fixes needed to reach passing rubric scores. ### What Requires Documentation - New user-facing features or commands - Changed keybindings or default behaviors - New or modified settings - Deprecated or removed functionality ### What Does NOT Require Documentation - Internal refactoring without behavioral changes - Performance optimizations (unless user-visible) - Bug fixes that restore documented behavior - Test changes, CI changes CONVENTIONS # Add preview callout instructions if needed if [[ "$ADD_PREVIEW_CALLOUT" == "true" ]]; then # Get current preview version for modification callouts local preview_version preview_version=$(gh release list --limit 5 2>/dev/null | grep -E '\-pre\s' | head -1 | grep -oE 'v[0-9]+\.[0-9]+' | head -1 || echo "v0.XXX") preview_version="${preview_version#v}" # Remove leading 'v' cat >> "$TMPDIR/prompt.md" << PREVIEW_INSTRUCTIONS ### Preview Release Callouts This change is going into Zed Preview first. Use the appropriate callout based on the type of change: #### For NEW/ADDITIVE features (new commands, new settings, new UI elements): \`\`\`markdown > **Preview:** This feature is available in Zed Preview. It will be included in the next Stable release. \`\`\` #### For BEHAVIOR MODIFICATIONS (changed defaults, altered behavior of existing features): \`\`\`markdown > **Changed in Preview (v${preview_version}).** See [release notes](/releases#${preview_version}). \`\`\` **Guidelines:** - Use the "Preview" callout for entirely new features or sections - Use the "Changed in Preview" callout when modifying documentation of existing behavior - Place callouts immediately after the section heading, before any content - Both callout types will be stripped when the feature ships to Stable PREVIEW_INSTRUCTIONS fi echo "" >> "$TMPDIR/prompt.md" echo "## Changed Files" >> "$TMPDIR/prompt.md" echo "" >> "$TMPDIR/prompt.md" echo '```' >> "$TMPDIR/prompt.md" cat "$TMPDIR/changed_files.txt" >> "$TMPDIR/prompt.md" echo '```' >> "$TMPDIR/prompt.md" echo "" >> "$TMPDIR/prompt.md" # Add the diff (truncated if huge) echo "## Code Diff" >> "$TMPDIR/prompt.md" echo "" >> "$TMPDIR/prompt.md" local diff_lines=$(wc -l < "$TMPDIR/changes.diff" | tr -d ' ') if [[ "$diff_lines" -gt 2000 ]]; then warn "Diff is large ($diff_lines lines), truncating to 2000 lines" echo '```diff' >> "$TMPDIR/prompt.md" head -2000 "$TMPDIR/changes.diff" >> "$TMPDIR/prompt.md" echo "" >> "$TMPDIR/prompt.md" echo "[... truncated, $((diff_lines - 2000)) more lines ...]" >> "$TMPDIR/prompt.md" echo '```' >> "$TMPDIR/prompt.md" else echo '```diff' >> "$TMPDIR/prompt.md" cat "$TMPDIR/changes.diff" >> "$TMPDIR/prompt.md" echo '```' >> "$TMPDIR/prompt.md" fi # Add output format instructions cat >> "$TMPDIR/prompt.md" << 'OUTPUT_FORMAT' ## Output Format Respond with ONE of these formats: ### If documentation updates ARE needed: ```markdown ## Documentation Suggestions ### Summary [1-2 sentence summary of what changed and why docs need updating] ### Suggested Changes #### 1. [docs/src/path/to/file.md] - **Section**: [existing section to update, or "New section"] - **Change**: [Add/Update/Remove] - **Target keyword**: [single keyword/intent phrase for this page] - **Frontmatter**: ```yaml --- title: ... description: ... --- ``` - **Links**: [List at least 3 internal docs links for non-reference pages; if this is a main feature page, include one relevant `zed.dev` marketing link] - **Suggestion**: [Specific text or description of what to add/change] - **Full-file brand pass**: [Required: yes. Note any additional voice edits elsewhere in the same file needed to pass rubric across the entire file.] - **Brand voice scorecard**: | Criterion | Score | Notes | | -------------------- | ----- | ----- | | Technical Grounding | /5 | | | Natural Syntax | /5 | | | Quiet Confidence | /5 | | | Developer Respect | /5 | | | Information Priority | /5 | | | Specificity | /5 | | | Voice Consistency | /5 | | | Earned Claims | /5 | | | **TOTAL** | /40 | | Pass threshold: all criteria 4+. #### 2. [docs/src/another/file.md] ... ### Notes for Reviewer [Any context or uncertainty worth flagging] ``` ### If NO documentation updates are needed: ```markdown ## No Documentation Updates Needed **Reason**: [Brief explanation - e.g., "Internal refactoring only", "Test changes", "Bug fix restoring existing behavior"] **Changes reviewed**: - [Brief summary of what the code changes do] - [Why they don't affect user-facing documentation] ``` Be conservative. Only suggest documentation changes when there's a clear user-facing impact. OUTPUT_FORMAT log "Context assembled: $(wc -l < "$TMPDIR/prompt.md" | tr -d ' ') lines" }