Compare commits

...

17 Commits

Author SHA1 Message Date
morgankrey
7a462ca07b Quote Escape 2025-12-22 10:39:59 -06:00
morgankrey
ba75c7b0c4 Fix Excaping 2025-12-22 10:10:12 -06:00
morgankrey
79390a7d22 Speed up 2025-12-22 10:01:12 -06:00
morgankrey
baac7ce3dc adding local testing and removing step 2 2025-12-21 20:28:35 -06:00
morgankrey
c0918dba8e Merge branch 'main' of https://github.com/zed-industries/zed 2025-12-21 13:41:35 -06:00
morgankrey
f9bf1fc9ae Merge branch 'main' of https://github.com/zed-industries/zed 2025-12-19 11:37:24 -06:00
morgankrey
ba8583c8e5 Merge branch 'main' of https://github.com/zed-industries/zed 2025-12-19 05:55:10 -06:00
morgankrey
1798498ec4 Merge branch 'main' of https://github.com/zed-industries/zed 2025-11-24 13:25:05 -06:00
morgankrey
15e327b2c2 Merge branch 'main' of https://github.com/zed-industries/zed 2025-11-21 10:37:14 -06:00
morgankrey
d0e9243cdf Merge branch 'main' of https://github.com/zed-industries/zed 2025-11-14 00:09:21 -06:00
morgankrey
94dcf3fa6e Merge branch 'main' of https://github.com/zed-industries/zed 2025-11-07 13:04:31 -06:00
morgankrey
279364340e Merge branch 'main' of https://github.com/zed-industries/zed 2025-11-05 11:46:34 -06:00
morgankrey
8e2727721b Merge branch 'main' of https://github.com/zed-industries/zed 2025-11-05 05:42:57 -06:00
morgankrey
57d5fd0c0b undo 2025-10-10 10:23:55 -05:00
morgankrey
817e486ec0 Merge branch 'main' of https://github.com/zed-industries/zed 2025-10-10 10:21:27 -05:00
morgankrey
887570d852 Merge branch 'main' of https://github.com/zed-industries/zed 2025-10-10 08:08:53 -05:00
morgankrey
5fb4109309 Add Grok 2025-10-10 08:08:51 -05:00
4 changed files with 765 additions and 157 deletions

View File

@@ -1,55 +0,0 @@
# Phase 2: Explore Repository
You are analyzing a codebase to understand its structure before reviewing documentation impact.
## Objective
Produce a structured overview of the repository to inform subsequent documentation analysis.
## Instructions
1. **Identify Primary Languages and Frameworks**
- Scan for Cargo.toml, package.json, or other manifest files
- Note the primary language(s) and key dependencies
2. **Map Documentation Structure**
- This project uses **mdBook** (https://rust-lang.github.io/mdBook/)
- Documentation is in `docs/src/`
- Table of contents: `docs/src/SUMMARY.md` (mdBook format: https://rust-lang.github.io/mdBook/format/summary.html)
- Style guide: `docs/.rules`
- Agent guidelines: `docs/AGENTS.md`
- Formatting: Prettier (config in `docs/.prettierrc`)
3. **Identify Build and Tooling**
- Note build systems (cargo, npm, etc.)
- Identify documentation tooling (mdbook, etc.)
4. **Output Format**
Produce a JSON summary:
```json
{
"primary_language": "Rust",
"frameworks": ["GPUI"],
"documentation": {
"system": "mdBook",
"location": "docs/src/",
"toc_file": "docs/src/SUMMARY.md",
"toc_format": "https://rust-lang.github.io/mdBook/format/summary.html",
"style_guide": "docs/.rules",
"agent_guidelines": "docs/AGENTS.md",
"formatter": "prettier",
"formatter_config": "docs/.prettierrc",
"custom_preprocessor": "docs_preprocessor (handles {#kb action::Name} syntax)"
},
"key_directories": {
"source": "crates/",
"docs": "docs/src/",
"extensions": "extensions/"
}
}
```
## Constraints
- Read-only: Do not modify any files
- Focus on structure, not content details
- Complete within 2 minutes

View File

@@ -23,7 +23,8 @@ permissions:
env:
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
DROID_MODEL: claude-opus-4-5-20251101
ANALYSIS_MODEL: gemini-3-flash-preview
WRITING_MODEL: claude-opus-4-5-20251101
jobs:
docs-automation:
@@ -83,111 +84,103 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
# Phase 0: Guardrails are loaded via AGENTS.md in each phase
# Phase 2: Explore Repository (Read-Only - default)
- name: "Phase 2: Explore Repository"
id: phase2
# Filter for docs-relevant files
- name: "Filter docs-relevant files"
id: filter
run: |
"$DROID_BIN" exec \
-m "$DROID_MODEL" \
-f .factory/prompts/docs-automation/phase2-explore.md \
> /tmp/phase2-output.txt 2>&1 || true
echo "Repository exploration complete"
cat /tmp/phase2-output.txt
# Phase 3: Analyze Changes (Read-Only - default)
- name: "Phase 3: Analyze Changes"
id: phase3
run: |
CHANGED_FILES=$(tr '\n' ' ' < /tmp/changed_files.txt)
echo "Analyzing changes in: $CHANGED_FILES"
# Build prompt with context
cat > /tmp/phase3-prompt.md << 'EOF'
$(cat .factory/prompts/docs-automation/phase3-analyze.md)
## Context
### Changed Files
$CHANGED_FILES
### Phase 2 Output
$(cat /tmp/phase2-output.txt)
EOF
"$DROID_BIN" exec \
-m "$DROID_MODEL" \
"$(cat .factory/prompts/docs-automation/phase3-analyze.md)
Changed files: $CHANGED_FILES" \
> /tmp/phase3-output.md 2>&1 || true
echo "Change analysis complete"
cat /tmp/phase3-output.md
# Phase 4: Plan Documentation Impact (Read-Only - default)
- name: "Phase 4: Plan Documentation Impact"
id: phase4
run: |
"$DROID_BIN" exec \
-m "$DROID_MODEL" \
-f .factory/prompts/docs-automation/phase4-plan.md \
> /tmp/phase4-plan.md 2>&1 || true
echo "Documentation plan complete"
cat /tmp/phase4-plan.md
# Check if updates are required
if grep -q "NO_UPDATES_REQUIRED" /tmp/phase4-plan.md; then
echo "updates_required=false" >> "$GITHUB_OUTPUT"
# Patterns for files that could affect documentation
PATTERNS="crates/.*/src/.*\.rs|assets/settings/.*|assets/keymaps/.*|extensions/.*|docs/.*"
RELEVANT=$(grep -E "$PATTERNS" /tmp/changed_files.txt || true)
if [ -z "$RELEVANT" ]; then
echo "No docs-relevant files changed"
echo "has_relevant=false" >> "$GITHUB_OUTPUT"
else
echo "updates_required=true" >> "$GITHUB_OUTPUT"
echo "Docs-relevant files found:"
echo "$RELEVANT"
echo "has_relevant=true" >> "$GITHUB_OUTPUT"
fi
# Phase 5: Apply Plan (Write-Enabled with --auto medium)
- name: "Phase 5: Apply Documentation Plan"
id: phase5
if: steps.phase4.outputs.updates_required == 'true'
# Combined: Analyze + Plan (using fast model)
- name: "Analyze & Plan"
id: analyze
if: steps.filter.outputs.has_relevant == 'true'
run: |
CHANGED_FILES=$(tr '\n' ' ' < /tmp/changed_files.txt)
GUIDELINES='## Documentation Guidelines
### Requires Update: New features, changed keybindings, modified settings, deprecated functionality
### No Update: Internal refactoring, performance fixes, bug fixes, test/CI changes
### Output JSON: {"updates_required": bool, "summary": str, "planned_changes": [{file, section, change_type, description}]}'
"$DROID_BIN" exec \
-m "$DROID_MODEL" \
-m "$ANALYSIS_MODEL" \
--auto low \
"Analyze code changes for documentation impact.
$GUIDELINES
Changed files: $CHANGED_FILES
Output the JSON structure. Be conservative - only flag user-visible changes." \
> /tmp/analysis.json 2>&1 || true
echo "Analysis complete:"
cat /tmp/analysis.json
# Check if updates required
if grep -q '"updates_required":\s*true' /tmp/analysis.json; then
echo "updates_required=true" >> "$GITHUB_OUTPUT"
else
echo "updates_required=false" >> "$GITHUB_OUTPUT"
fi
# Combined: Apply + Summarize (using writing model)
- name: "Apply Documentation Changes"
id: apply
if: steps.analyze.outputs.updates_required == 'true'
run: |
ANALYSIS=$(cat /tmp/analysis.json)
"$DROID_BIN" exec \
-m "$WRITING_MODEL" \
--auto medium \
-f .factory/prompts/docs-automation/phase5-apply.md \
> /tmp/phase5-report.md 2>&1 || true
echo "Documentation updates applied"
cat /tmp/phase5-report.md
"Apply documentation changes from this analysis:
$ANALYSIS
Instructions:
1. Edit each specified file
2. Follow mdBook format, use {#kb action::Name} for keybindings
3. Output summary:
## Changes Applied
- [file]: [change]
## Summary for PR
[2-3 sentences]" \
> /tmp/apply-report.md 2>&1 || true
echo "Changes applied:"
cat /tmp/apply-report.md
cp /tmp/apply-report.md /tmp/phase6-summary.md
# Phase 5b: Format with Prettier
- name: "Phase 5b: Format with Prettier"
id: phase5b
if: steps.phase4.outputs.updates_required == 'true'
# Format with Prettier (only changed files)
- name: "Format with Prettier"
id: format
if: steps.analyze.outputs.updates_required == 'true'
run: |
echo "Formatting documentation with Prettier..."
cd docs && prettier --write src/
CHANGED_DOCS=$(git diff --name-only docs/src/ | sed 's|^docs/||' | tr '\n' ' ')
if [ -n "$CHANGED_DOCS" ]; then
echo "Formatting: $CHANGED_DOCS"
cd docs && prettier --write "$CHANGED_DOCS"
fi
echo "Verifying Prettier formatting passes..."
cd docs && prettier --check src/
echo "Prettier formatting complete"
# Phase 6: Summarize Changes (Read-Only - default)
- name: "Phase 6: Summarize Changes"
id: phase6
if: steps.phase4.outputs.updates_required == 'true'
run: |
# Get git diff of docs
git diff docs/src/ > /tmp/docs-diff.txt || true
"$DROID_BIN" exec \
-m "$DROID_MODEL" \
-f .factory/prompts/docs-automation/phase6-summarize.md \
> /tmp/phase6-summary.md 2>&1 || true
echo "Summary generated"
cat /tmp/phase6-summary.md
# Phase 7: Commit and Open PR
- name: "Phase 7: Create PR"
id: phase7
if: steps.phase4.outputs.updates_required == 'true'
# Create PR
- name: "Create PR"
id: create_pr
if: steps.analyze.outputs.updates_required == 'true'
run: |
# Check if there are actual changes
if git diff --quiet docs/src/; then
@@ -202,6 +195,21 @@ jobs:
# Daily batch branch - one branch per day, multiple commits accumulate
BRANCH_NAME="docs/auto-update-$(date +%Y-%m-%d)"
# Get source PR info for attribution
SOURCE_PR_INFO=""
if [ "${{ steps.changed.outputs.source }}" == "pr" ]; then
PR_NUM="${{ steps.changed.outputs.ref }}"
PR_DETAILS=$(gh pr view "$PR_NUM" --json title,author,url 2>/dev/null || echo "{}")
SOURCE_TITLE=$(echo "$PR_DETAILS" | jq -r '.title // "Unknown"')
SOURCE_AUTHOR=$(echo "$PR_DETAILS" | jq -r '.author.login // "Unknown"')
SOURCE_URL=$(echo "$PR_DETAILS" | jq -r '.url // ""')
SOURCE_PR_INFO="
---
**Source**: [#$PR_NUM]($SOURCE_URL) - $SOURCE_TITLE
**Author**: @$SOURCE_AUTHOR
"
fi
# Stash local changes from phase 5
git stash push -m "docs-automation-changes" -- docs/src/
@@ -232,16 +240,37 @@ jobs:
# Push
git push -u origin "$BRANCH_NAME"
# Check if PR already exists for this branch
EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number' || echo "")
# Build the PR body section for this update
PR_BODY_SECTION="## Update from $(date '+%Y-%m-%d %H:%M')
$SOURCE_PR_INFO
$(cat /tmp/phase6-summary.md)
"
if [ -n "$EXISTING_PR" ]; then
echo "PR #$EXISTING_PR already exists for branch $BRANCH_NAME, updated with new commit"
# Check if PR already exists for this branch
EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number,url,body --jq '.[0]' || echo "")
if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
PR_NUM=$(echo "$EXISTING_PR" | jq -r '.number')
PR_URL=$(echo "$EXISTING_PR" | jq -r '.url')
EXISTING_BODY=$(echo "$EXISTING_PR" | jq -r '.body // ""')
# Append new summary to existing PR body
NEW_BODY="${EXISTING_BODY}
---
${PR_BODY_SECTION}"
echo "$NEW_BODY" > /tmp/updated-pr-body.md
gh pr edit "$PR_NUM" --body-file /tmp/updated-pr-body.md
echo "PR #$PR_NUM updated: $PR_URL"
else
# Create new PR
echo "$PR_BODY_SECTION" > /tmp/new-pr-body.md
gh pr create \
--title "docs: automated documentation update ($(date +%Y-%m-%d))" \
--body-file /tmp/phase6-summary.md \
--body-file /tmp/new-pr-body.md \
--base main || true
echo "PR created on branch: $BRANCH_NAME"
fi
@@ -255,10 +284,12 @@ jobs:
echo "## Documentation Automation Summary" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
if [ "${{ steps.phase4.outputs.updates_required }}" == "false" ]; then
if [ "${{ steps.filter.outputs.has_relevant }}" == "false" ]; then
echo "No docs-relevant files changed. Skipped analysis." >> "$GITHUB_STEP_SUMMARY"
elif [ "${{ steps.analyze.outputs.updates_required }}" == "false" ]; then
echo "No documentation updates required for this change." >> "$GITHUB_STEP_SUMMARY"
elif [ -f /tmp/phase6-summary.md ]; then
cat /tmp/phase6-summary.md >> "$GITHUB_STEP_SUMMARY"
else
echo "Workflow completed. Check individual phase outputs for details." >> "$GITHUB_STEP_SUMMARY"
echo "Workflow completed. Check individual step outputs for details." >> "$GITHUB_STEP_SUMMARY"
fi

View File

@@ -2,6 +2,10 @@
This file governs automated documentation updates triggered by code changes. All automation phases must comply with these rules.
## Repository Context
This is the **Zed code editor** repository, a Rust-based application using the custom **GPUI** UI framework. The project is a large monorepo with ~200 crates organized under `crates/`. Documentation is built with **mdBook** and uses a custom preprocessor (`docs_preprocessor`) that handles special syntax like `{#kb action::Name}` for keybindings. The documentation source is in `docs/src/` with a table of contents in `SUMMARY.md`, and all docs must pass Prettier formatting (80 char line width). The style guide (`docs/.rules`) and agent guidelines (`docs/AGENTS.md`) provide specific conventions for documentation writing.
## Documentation System
This documentation uses **mdBook** (https://rust-lang.github.io/mdBook/).

628
script/test-docs-automation Executable file
View File

@@ -0,0 +1,628 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
PROMPTS_DIR="$REPO_ROOT/.factory/prompts/docs-automation"
OUTPUT_DIR="${TMPDIR:-/tmp}/docs-automation-test"
# Default values
BASE_BRANCH="main"
# Use fast model for analysis, powerful model for writing
ANALYSIS_MODEL="${ANALYSIS_MODEL:-gemini-3-flash-preview}"
WRITING_MODEL="${WRITING_MODEL:-claude-opus-4-5-20251101}"
DRY_RUN=false
VERBOSE=false
PR_NUMBER=""
SOURCE_BRANCH=""
# Patterns for files that could affect documentation
DOCS_RELEVANT_PATTERNS=(
"crates/.*/src/.*\.rs" # Rust source files
"assets/settings/.*" # Settings schemas
"assets/keymaps/.*" # Keymaps
"extensions/.*" # Extensions
"docs/.*" # Docs themselves
)
usage() {
cat << EOF
Usage: $(basename "$0") [OPTIONS]
Test the documentation automation workflow locally.
OPTIONS:
-p, --pr NUMBER PR number to analyze (uses gh pr diff)
-r, --branch BRANCH Remote branch to compare (e.g., origin/feature-branch)
-b, --base BRANCH Base branch to compare against (default: main)
-d, --dry-run Preview changes without modifying files
-s, --skip-apply Alias for --dry-run
-v, --verbose Show full output from each phase
-o, --output DIR Output directory for phase artifacts (default: $OUTPUT_DIR)
-h, --help Show this help message
EXAMPLES:
# Analyze a PR (most common use case)
$(basename "$0") --pr 12345
# Analyze a PR with dry run (no file changes)
$(basename "$0") --pr 12345 --dry-run
# Analyze a remote branch against main
$(basename "$0") --branch origin/feature-branch
ENVIRONMENT:
FACTORY_API_KEY Required: Your Factory API key
ANALYSIS_MODEL Model for analysis (default: gemini-2.0-flash)
WRITING_MODEL Model for writing (default: claude-opus-4-5-20251101)
GH_TOKEN Required for --pr option (or gh auth login)
EOF
exit 0
}
while [[ $# -gt 0 ]]; do
case $1 in
-p|--pr)
PR_NUMBER="$2"
shift 2
;;
-r|--branch)
SOURCE_BRANCH="$2"
shift 2
;;
-b|--base)
BASE_BRANCH="$2"
shift 2
;;
-d|--dry-run|-s|--skip-apply)
DRY_RUN=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-o|--output)
OUTPUT_DIR="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1"
usage
;;
esac
done
# Cleanup function for restoring original branch
cleanup_on_exit() {
if [[ -f "$OUTPUT_DIR/original-branch.txt" ]]; then
ORIGINAL_BRANCH=$(cat "$OUTPUT_DIR/original-branch.txt")
CURRENT=$(git -C "$REPO_ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
if [[ "$CURRENT" != "$ORIGINAL_BRANCH" && -n "$ORIGINAL_BRANCH" ]]; then
echo ""
echo "Restoring original branch: $ORIGINAL_BRANCH"
git -C "$REPO_ROOT" checkout "$ORIGINAL_BRANCH" 2>/dev/null || true
if [[ "$CURRENT" == temp-analysis-* ]]; then
git -C "$REPO_ROOT" branch -D "$CURRENT" 2>/dev/null || true
fi
fi
fi
}
trap cleanup_on_exit EXIT
# Check for required tools
if ! command -v droid &> /dev/null; then
echo "Error: droid CLI not found. Install from https://app.factory.ai/cli"
exit 1
fi
if [[ -z "${FACTORY_API_KEY:-}" ]]; then
echo "Error: FACTORY_API_KEY environment variable is not set"
exit 1
fi
# Check gh CLI if PR mode
if [[ -n "$PR_NUMBER" ]]; then
if ! command -v gh &> /dev/null; then
echo "Error: gh CLI not found. Install from https://cli.github.com/"
echo "Required for --pr option"
exit 1
fi
fi
# Create output directory
mkdir -p "$OUTPUT_DIR"
echo "========================================"
echo "Documentation Automation Test"
echo "========================================"
echo "Output directory: $OUTPUT_DIR"
echo "Analysis model: $ANALYSIS_MODEL"
echo "Writing model: $WRITING_MODEL"
echo "Started at: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
cd "$REPO_ROOT"
# Get changed files based on mode
echo "=== Getting changed files ==="
if [[ -n "$PR_NUMBER" ]]; then
# PR mode: use gh pr diff like the workflow does
echo "Analyzing PR #$PR_NUMBER"
# Get PR info for context
echo "Fetching PR details..."
gh pr view "$PR_NUMBER" --json title,headRefName,baseRefName,state > "$OUTPUT_DIR/pr-info.json" 2>/dev/null || true
if [[ -f "$OUTPUT_DIR/pr-info.json" ]]; then
PR_TITLE=$(jq -r '.title // "Unknown"' "$OUTPUT_DIR/pr-info.json")
PR_HEAD=$(jq -r '.headRefName // "Unknown"' "$OUTPUT_DIR/pr-info.json")
PR_BASE=$(jq -r '.baseRefName // "Unknown"' "$OUTPUT_DIR/pr-info.json")
PR_STATE=$(jq -r '.state // "Unknown"' "$OUTPUT_DIR/pr-info.json")
echo " Title: $PR_TITLE"
echo " Branch: $PR_HEAD -> $PR_BASE"
echo " State: $PR_STATE"
fi
echo ""
# Get the list of changed files
gh pr diff "$PR_NUMBER" --name-only > "$OUTPUT_DIR/changed_files.txt"
# Also save the full diff for analysis
gh pr diff "$PR_NUMBER" > "$OUTPUT_DIR/pr-diff.patch" 2>/dev/null || true
# Checkout the PR branch to have the code available for analysis
echo "Checking out PR branch for analysis..."
ORIGINAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
echo "$ORIGINAL_BRANCH" > "$OUTPUT_DIR/original-branch.txt"
gh pr checkout "$PR_NUMBER" --force 2>/dev/null || {
echo "Warning: Could not checkout PR branch. Analysis will use current branch state."
}
elif [[ -n "$SOURCE_BRANCH" ]]; then
# Remote branch mode
echo "Analyzing branch: $SOURCE_BRANCH"
echo "Base branch: $BASE_BRANCH"
# Fetch the branches
git fetch origin 2>/dev/null || true
# Resolve branch refs
SOURCE_REF="$SOURCE_BRANCH"
BASE_REF="origin/$BASE_BRANCH"
# Get merge base
MERGE_BASE=$(git merge-base "$BASE_REF" "$SOURCE_REF" 2>/dev/null) || {
echo "Error: Could not find merge base between $BASE_REF and $SOURCE_REF"
exit 1
}
echo "Merge base: $MERGE_BASE"
# Get changed files
git diff --name-only "$MERGE_BASE" "$SOURCE_REF" > "$OUTPUT_DIR/changed_files.txt"
# Checkout the source branch for analysis
echo "Checking out $SOURCE_BRANCH for analysis..."
ORIGINAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
echo "$ORIGINAL_BRANCH" > "$OUTPUT_DIR/original-branch.txt"
git checkout "$SOURCE_BRANCH" 2>/dev/null || git checkout -b "temp-analysis-$$" "$SOURCE_REF" || {
echo "Warning: Could not checkout branch. Analysis will use current branch state."
}
else
# Current branch mode (original behavior)
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
echo "Analyzing current branch: $CURRENT_BRANCH"
echo "Base branch: $BASE_BRANCH"
# Fetch the base branch
git fetch origin "$BASE_BRANCH" 2>/dev/null || true
# Get merge base
MERGE_BASE=$(git merge-base "origin/$BASE_BRANCH" HEAD 2>/dev/null || git merge-base "$BASE_BRANCH" HEAD)
echo "Merge base: $MERGE_BASE"
git diff --name-only "$MERGE_BASE" HEAD > "$OUTPUT_DIR/changed_files.txt"
fi
if [[ ! -s "$OUTPUT_DIR/changed_files.txt" ]]; then
echo "No changed files found."
exit 0
fi
echo ""
echo "Changed files ($(wc -l < "$OUTPUT_DIR/changed_files.txt" | tr -d ' ') files):"
cat "$OUTPUT_DIR/changed_files.txt"
echo ""
# Early exit: Filter for docs-relevant files only
echo "=== Filtering for docs-relevant files ==="
DOCS_RELEVANT_FILES=""
while IFS= read -r file; do
for pattern in "${DOCS_RELEVANT_PATTERNS[@]}"; do
if [[ "$file" =~ $pattern ]]; then
DOCS_RELEVANT_FILES="$DOCS_RELEVANT_FILES $file"
break
fi
done
done < "$OUTPUT_DIR/changed_files.txt"
# Trim leading space
DOCS_RELEVANT_FILES="${DOCS_RELEVANT_FILES# }"
if [[ -z "$DOCS_RELEVANT_FILES" ]]; then
echo "No docs-relevant files changed (only tests, configs, CI, etc.)"
echo "Skipping documentation analysis."
exit 0
fi
echo "Docs-relevant files: $(echo "$DOCS_RELEVANT_FILES" | wc -w | tr -d ' ')"
echo "$DOCS_RELEVANT_FILES" | tr ' ' '\n' | head -20
echo ""
# Combined Phase: Analyze + Plan (using fast model)
echo "=== Analyzing Changes & Planning Documentation Impact ==="
echo "Model: $ANALYSIS_MODEL"
echo "Started at: $(date '+%H:%M:%S')"
echo ""
CHANGED_FILES=$(tr '\n' ' ' < "$OUTPUT_DIR/changed_files.txt")
# Write prompt to temp file to avoid escaping issues
cat > "$OUTPUT_DIR/analysis-prompt.txt" << 'PROMPT_EOF'
Analyze these code changes and determine if documentation updates are needed.
## Documentation Guidelines
### Requires Documentation Update
- New user-facing features or commands
- Changed keybindings or default behaviors
- Modified settings schema or options
- Deprecated or removed functionality
### Does NOT Require Documentation Update
- Internal refactoring without behavioral changes
- Performance optimizations (unless user-visible)
- Bug fixes that restore documented behavior
- Test changes, CI/CD changes
### In-Scope: docs/src/**/*.md
### Out-of-Scope: CHANGELOG.md, README.md, code comments, rustdoc
### Output Format Required
You MUST output a JSON object with this exact structure:
{
"updates_required": true or false,
"summary": "Brief description of changes",
"planned_changes": [
{
"file": "docs/src/path/to/file.md",
"section": "Section name",
"change_type": "update or add or deprecate",
"description": "What to change"
}
],
"skipped_files": ["reason1", "reason2"]
}
Be conservative - only flag documentation updates for user-visible changes.
## Changed Files
PROMPT_EOF
echo "$CHANGED_FILES" >> "$OUTPUT_DIR/analysis-prompt.txt"
ANALYSIS_START=$(date +%s)
droid exec \
-m "$ANALYSIS_MODEL" \
--auto low \
-f "$OUTPUT_DIR/analysis-prompt.txt" \
> "$OUTPUT_DIR/analysis.json" 2>&1 || true
ANALYSIS_END=$(date +%s)
ANALYSIS_DURATION=$((ANALYSIS_END - ANALYSIS_START))
echo "Completed in ${ANALYSIS_DURATION}s"
echo ""
echo "--- Analysis Result ---"
cat "$OUTPUT_DIR/analysis.json"
echo ""
echo "-----------------------"
echo ""
# Check if updates are required (parse JSON output)
UPDATES_REQUIRED=$(grep -o '"updates_required":\s*true' "$OUTPUT_DIR/analysis.json" || echo "")
if [[ -z "$UPDATES_REQUIRED" ]]; then
echo "=== No documentation updates required ==="
echo "Analysis determined no documentation changes are needed."
cat "$OUTPUT_DIR/analysis.json"
exit 0
fi
echo "Documentation updates ARE required."
echo ""
# Extract planned changes for the next phase
ANALYSIS_OUTPUT=$(cat "$OUTPUT_DIR/analysis.json")
if [[ "$DRY_RUN" == "true" ]]; then
# Combined Preview Phase (dry-run): Show what would change
echo "=== Preview: Generating Proposed Changes ==="
echo "Model: $WRITING_MODEL"
echo "Started at: $(date '+%H:%M:%S')"
echo ""
PREVIEW_START=$(date +%s)
# Write preview prompt to temp file
cat > "$OUTPUT_DIR/preview-prompt.txt" << PREVIEW_EOF
Generate a PREVIEW of the documentation changes. Do NOT modify any files.
Based on this analysis:
$ANALYSIS_OUTPUT
For each planned change:
1. Read the current file
2. Show the CURRENT section that would be modified
3. Show the PROPOSED new content
4. Generate a unified diff
Output format:
---
## File: [path]
### Current:
(paste exact current content)
### Proposed:
(paste proposed new content)
### Diff:
(unified diff with - and + lines)
---
Show the ACTUAL content, not summaries.
PREVIEW_EOF
droid exec \
-m "$WRITING_MODEL" \
--auto low \
-f "$OUTPUT_DIR/preview-prompt.txt" \
> "$OUTPUT_DIR/preview.md" 2>&1 || true
PREVIEW_END=$(date +%s)
PREVIEW_DURATION=$((PREVIEW_END - PREVIEW_START))
echo "Completed in ${PREVIEW_DURATION}s"
echo ""
echo "--- Proposed Changes ---"
cat "$OUTPUT_DIR/preview.md"
echo "------------------------"
echo ""
echo "=== Dry run complete ==="
echo "Total time: Analysis ${ANALYSIS_DURATION}s + Preview ${PREVIEW_DURATION}s = $((ANALYSIS_DURATION + PREVIEW_DURATION))s"
echo "To apply changes, run without --dry-run flag."
echo "Output saved to: $OUTPUT_DIR/"
exit 0
fi
# Combined Phase: Apply Changes + Generate Summary (using writing model)
echo "=== Applying Documentation Changes ==="
echo "Model: $WRITING_MODEL"
echo "Started at: $(date '+%H:%M:%S')"
echo ""
APPLY_START=$(date +%s)
# Write apply prompt to temp file
cat > "$OUTPUT_DIR/apply-prompt.txt" << APPLY_EOF
Apply the documentation changes specified in this analysis:
$ANALYSIS_OUTPUT
Instructions:
1. For each planned change, edit the specified file
2. Follow the mdBook format and style from docs/AGENTS.md
3. Use {#kb action::Name} syntax for keybindings
4. After making changes, output a brief summary
Output format:
## Changes Applied
- [file]: [what was changed]
## Summary for PR
[2-3 sentence summary suitable for a PR description]
APPLY_EOF
droid exec \
-m "$WRITING_MODEL" \
--auto medium \
-f "$OUTPUT_DIR/apply-prompt.txt" \
> "$OUTPUT_DIR/apply-report.md" 2>&1 || true
APPLY_END=$(date +%s)
APPLY_DURATION=$((APPLY_END - APPLY_START))
echo "Completed in ${APPLY_DURATION}s"
echo ""
echo "--- Apply Report ---"
cat "$OUTPUT_DIR/apply-report.md"
echo "--------------------"
echo ""
# Format with Prettier (only changed files)
echo "=== Formatting with Prettier ==="
cd "$REPO_ROOT"
CHANGED_DOCS=$(git diff --name-only docs/src/ 2>/dev/null | sed 's|^docs/||' | tr '\n' ' ')
if [[ -n "$CHANGED_DOCS" ]]; then
echo "Formatting: $CHANGED_DOCS"
if command -v pnpm &> /dev/null; then
(cd docs && pnpm dlx prettier@3.5.0 $CHANGED_DOCS --write) 2>/dev/null || true
elif command -v prettier &> /dev/null; then
(cd docs && prettier --write $CHANGED_DOCS) 2>/dev/null || true
fi
echo "Done"
else
echo "No changed docs files to format"
fi
echo ""
# Generate summary from the apply report
cp "$OUTPUT_DIR/apply-report.md" "$OUTPUT_DIR/phase6-summary.md"
# Phase 7: Create Branch and PR
echo "=== Phase 7: Create Branch and PR ==="
# Check if there are actual changes
if git -C "$REPO_ROOT" diff --quiet docs/src/; then
echo "No documentation changes detected after Phase 5"
echo ""
echo "=== Test Complete (no changes to commit) ==="
exit 0
fi
# Check if gh CLI is available
if ! command -v gh &> /dev/null; then
echo "Warning: gh CLI not found. Skipping PR creation."
echo "Install from https://cli.github.com/ to enable automatic PR creation."
echo ""
echo "Documentation changes (git status):"
git -C "$REPO_ROOT" status --short docs/src/
echo ""
echo "To review the diff:"
echo " git diff docs/src/"
echo ""
echo "To discard changes:"
echo " git checkout docs/src/"
exit 0
fi
cd "$REPO_ROOT"
# Daily batch branch - one branch per day, multiple commits accumulate
BRANCH_NAME="docs/auto-update-$(date +%Y-%m-%d)"
# Stash local changes from phase 5
echo "Stashing documentation changes..."
git stash push -m "docs-automation-changes" -- docs/src/
# Check if branch already exists on remote
if git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null 2>&1; then
echo "Branch $BRANCH_NAME exists, checking out and updating..."
git fetch origin "$BRANCH_NAME"
git checkout -B "$BRANCH_NAME" "origin/$BRANCH_NAME"
else
echo "Creating new branch $BRANCH_NAME from main..."
git fetch origin main
git checkout -B "$BRANCH_NAME" origin/main
fi
# Apply stashed changes
echo "Applying documentation changes..."
git stash pop || true
# Stage and commit
git add docs/src/
# Get source PR info for attribution
SOURCE_PR_INFO=""
TRIGGER_INFO=""
if [[ -n "$PR_NUMBER" ]]; then
# Fetch PR details: title, author, url
PR_DETAILS=$(gh pr view "$PR_NUMBER" --json title,author,url 2>/dev/null || echo "{}")
SOURCE_TITLE=$(echo "$PR_DETAILS" | jq -r '.title // "Unknown"')
SOURCE_AUTHOR=$(echo "$PR_DETAILS" | jq -r '.author.login // "Unknown"')
SOURCE_URL=$(echo "$PR_DETAILS" | jq -r '.url // ""')
TRIGGER_INFO="Triggered by: PR #$PR_NUMBER"
SOURCE_PR_INFO="
---
**Source**: [#$PR_NUMBER]($SOURCE_URL) - $SOURCE_TITLE
**Author**: @$SOURCE_AUTHOR
"
elif [[ -n "$SOURCE_BRANCH" ]]; then
TRIGGER_INFO="Triggered by: branch $SOURCE_BRANCH"
SOURCE_PR_INFO="
---
**Source**: Branch \`$SOURCE_BRANCH\`
"
fi
# Build commit message
SUMMARY=$(head -50 < "$OUTPUT_DIR/phase6-summary.md" 2>/dev/null || echo "Automated documentation update")
git commit -m "docs: auto-update documentation
${SUMMARY}
${TRIGGER_INFO}
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>" || {
echo "Nothing to commit"
exit 0
}
# Push
echo "Pushing to origin/$BRANCH_NAME..."
git push -u origin "$BRANCH_NAME"
# Build the PR body section for this update
PR_BODY_SECTION="## Update from $(date '+%Y-%m-%d %H:%M')
$SOURCE_PR_INFO
$(cat "$OUTPUT_DIR/phase6-summary.md")
"
# Check if PR already exists for this branch
EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number,url,body --jq '.[0]' 2>/dev/null || echo "")
if [[ -n "$EXISTING_PR" && "$EXISTING_PR" != "null" ]]; then
PR_NUM=$(echo "$EXISTING_PR" | jq -r '.number')
PR_URL=$(echo "$EXISTING_PR" | jq -r '.url')
EXISTING_BODY=$(echo "$EXISTING_PR" | jq -r '.body // ""')
# Append new summary to existing PR body
echo "Updating PR body with new summary..."
NEW_BODY="${EXISTING_BODY}
---
${PR_BODY_SECTION}"
echo "$NEW_BODY" > "$OUTPUT_DIR/updated-pr-body.md"
gh pr edit "$PR_NUM" --body-file "$OUTPUT_DIR/updated-pr-body.md"
echo ""
echo "=== Updated existing PR ==="
echo "PR #$PR_NUM: $PR_URL"
echo "New commit added and PR description updated."
else
# Create new PR with full body
echo "Creating new PR..."
echo "$PR_BODY_SECTION" > "$OUTPUT_DIR/new-pr-body.md"
PR_URL=$(gh pr create \
--title "docs: automated documentation update ($(date +%Y-%m-%d))" \
--body-file "$OUTPUT_DIR/new-pr-body.md" \
--base main 2>&1) || {
echo "Failed to create PR: $PR_URL"
exit 1
}
echo ""
echo "=== PR Created ==="
echo "$PR_URL"
fi
echo ""
echo "=== Test Complete ==="
echo "Total time: Analysis ${ANALYSIS_DURATION}s + Apply ${APPLY_DURATION}s = $((ANALYSIS_DURATION + APPLY_DURATION))s"
echo "All outputs saved to: $OUTPUT_DIR/"