1#!/usr/bin/env bash
2#
3# Create a draft documentation PR by auto-applying batched suggestions.
4#
5# Usage:
6# script/docs-suggest-publish [--dry-run] [--model MODEL]
7#
8# This script:
9# 1. Reads pending suggestions from the docs/suggestions-pending branch
10# 2. Uses Droid to apply all suggestions directly to docs files
11# 3. Runs docs formatting
12# 4. Creates a draft PR for human review/merge
13# 5. Optionally resets the suggestions branch after successful PR creation
14#
15# Options:
16# --dry-run Show what would be done without creating PR
17# --keep-queue Don't reset the suggestions branch after PR creation
18# --model MODEL Override Droid model used for auto-apply
19# --verbose Show detailed progress
20#
21# Run this as part of the preview release workflow.
22
23set -euo pipefail
24
25DRY_RUN=false
26KEEP_QUEUE=false
27VERBOSE=false
28MODEL="${DROID_MODEL:-claude-sonnet-4-5-20250929}"
29
30SUGGESTIONS_BRANCH="docs/suggestions-pending"
31
32# Colors
33RED='\033[0;31m'
34GREEN='\033[0;32m'
35YELLOW='\033[0;33m'
36BLUE='\033[0;34m'
37NC='\033[0m'
38
39log() {
40 if [[ "$VERBOSE" == "true" ]]; then
41 echo -e "${BLUE}[docs-publish]${NC} $*" >&2
42 fi
43}
44
45error() {
46 echo -e "${RED}Error:${NC} $*" >&2
47 exit 1
48}
49
50# Parse arguments
51while [[ $# -gt 0 ]]; do
52 case $1 in
53 --dry-run)
54 DRY_RUN=true
55 shift
56 ;;
57 --keep-queue)
58 KEEP_QUEUE=true
59 shift
60 ;;
61 --verbose)
62 VERBOSE=true
63 shift
64 ;;
65 --model)
66 MODEL="$2"
67 shift 2
68 ;;
69 -h|--help)
70 head -26 "$0" | tail -24
71 exit 0
72 ;;
73 *)
74 error "Unknown option: $1"
75 ;;
76 esac
77done
78
79# Get repo root
80REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
81cd "$REPO_ROOT"
82
83# Check if suggestions branch exists
84log "Checking for suggestions branch..."
85if ! git ls-remote --exit-code --heads origin "$SUGGESTIONS_BRANCH" > /dev/null 2>&1; then
86 echo "No pending suggestions found (branch $SUGGESTIONS_BRANCH doesn't exist)."
87 echo "Suggestions are queued automatically when PRs are merged to main."
88 exit 0
89fi
90
91# Fetch the suggestions branch
92log "Fetching suggestions branch..."
93git fetch origin "$SUGGESTIONS_BRANCH"
94
95# Check for manifest
96if ! git show "origin/$SUGGESTIONS_BRANCH:manifest.json" > /dev/null 2>&1; then
97 echo "No manifest found on suggestions branch."
98 exit 0
99fi
100
101# Read manifest
102MANIFEST=$(git show "origin/$SUGGESTIONS_BRANCH:manifest.json")
103SUGGESTION_COUNT=$(echo "$MANIFEST" | jq '.suggestions | length')
104
105if [[ "$SUGGESTION_COUNT" -eq 0 ]]; then
106 echo "No pending suggestions in queue."
107 exit 0
108fi
109
110echo "Found $SUGGESTION_COUNT pending suggestion(s):"
111echo ""
112echo "$MANIFEST" | jq -r '.suggestions[] | " PR #\(.pr): \(.title)"'
113echo ""
114
115if [[ "$DRY_RUN" == "true" ]]; then
116 echo -e "${YELLOW}=== DRY RUN ===${NC}"
117 echo ""
118 echo "Would auto-apply suggestions to docs via Droid and create a draft PR."
119 echo "Model: $MODEL"
120 echo ""
121
122 # Show each suggestion file
123 for file in $(echo "$MANIFEST" | jq -r '.suggestions[].file'); do
124 echo "--- $file ---"
125 git show "origin/$SUGGESTIONS_BRANCH:$file" 2>/dev/null || echo "(file not found)"
126 echo ""
127 done
128
129 echo -e "${YELLOW}=== END DRY RUN ===${NC}"
130 echo ""
131 echo "Run without --dry-run to create the PR."
132 exit 0
133fi
134
135# Ensure clean working state (ignore untracked files with grep -v '??')
136if [[ -n "$(git status --porcelain | grep -v '^??' || true)" ]]; then
137 error "Working directory has uncommitted changes. Please commit or stash first."
138fi
139
140for command in git gh jq droid; do
141 if ! command -v "$command" > /dev/null 2>&1; then
142 error "Required command not found: $command"
143 fi
144done
145
146# Remember current branch
147ORIGINAL_BRANCH=$(git branch --show-current)
148log "Current branch: $ORIGINAL_BRANCH"
149
150# Create new branch for docs PR from latest main
151git fetch origin main
152DOCS_BRANCH="docs/preview-auto-$(date +%Y-%m-%d-%H%M%S)"
153log "Creating docs branch: $DOCS_BRANCH"
154
155git checkout -b "$DOCS_BRANCH" origin/main
156
157TMPDIR=$(mktemp -d)
158trap 'rm -rf "$TMPDIR"' EXIT
159
160SUGGESTIONS_FILE="$TMPDIR/suggestions.md"
161APPLY_PROMPT_FILE="$TMPDIR/apply-prompt.md"
162APPLY_SUMMARY_FILE="$TMPDIR/apply-summary.md"
163
164# Combine queued suggestion files into one input
165for file in $(echo "$MANIFEST" | jq -r '.suggestions[].file'); do
166 {
167 echo "## Source: $file"
168 echo ""
169 git show "origin/$SUGGESTIONS_BRANCH:$file" 2>/dev/null || error "Suggestion file missing: $file"
170 echo ""
171 echo "---"
172 echo ""
173 } >> "$SUGGESTIONS_FILE"
174done
175
176# Build auto-apply prompt
177cat > "$APPLY_PROMPT_FILE" << 'EOF'
178# Documentation Auto-Apply Request (Preview Release)
179
180Apply all queued documentation suggestions below directly to docs files in this repository.
181
182Before making edits, read and follow these rule files:
183- `.rules`
184- `docs/.rules`
185
186## Required behavior
187
1881. Apply concrete documentation edits (not suggestion text) to the appropriate files.
1892. Edit only docs content files under `docs/src/` unless a suggestion explicitly requires another docs path.
1903. For every docs file you modify, run a full-file brand voice pass (entire file, not only edited sections).
1914. Enforce the brand rubric exactly; final file content must score 4+ on every criterion:
192 - Technical Grounding
193 - Natural Syntax
194 - Quiet Confidence
195 - Developer Respect
196 - Information Priority
197 - Specificity
198 - Voice Consistency
199 - Earned Claims
2005. Keep SEO/frontmatter/linking requirements from the suggestions where applicable.
2016. Keep preview callout semantics correct:
202 - Additive features: `> **Preview:** ...`
203 - Behavior modifications: `> **Changed in Preview (vX.XXX).** ...`
2047. If a suggestion is too ambiguous to apply safely, skip it and explain why in the summary.
205
206## Output format (after making edits)
207
208Return markdown with:
209
210- `## Applied Suggestions`
211- `## Skipped Suggestions`
212- `## Files Updated`
213- `## Brand Voice Verification` (one line per updated file confirming full-file pass)
214
215Do not include a patch in the response; apply edits directly to files.
216
217## Queued Suggestions
218
219EOF
220
221cat "$SUGGESTIONS_FILE" >> "$APPLY_PROMPT_FILE"
222
223log "Running Droid auto-apply with model: $MODEL"
224if ! droid exec -m "$MODEL" -f "$APPLY_PROMPT_FILE" --auto high > "$APPLY_SUMMARY_FILE" 2>&1; then
225 echo "Droid exec output:"
226 cat "$APPLY_SUMMARY_FILE"
227 error "Droid exec failed. See output above."
228fi
229log "Droid completed, checking results..."
230
231if [[ -n "$(git status --porcelain | grep -v '^??' | grep -vE '^.. docs/' || true)" ]]; then
232 error "Auto-apply modified non-doc files. Revert and re-run."
233fi
234
235if [[ -z "$(git status --porcelain docs/ | grep '^.. docs/src/' || true)" ]]; then
236 error "Auto-apply produced no docs/src changes."
237fi
238
239log "Running docs formatter"
240./script/prettier --write
241
242if [[ -z "$(git status --porcelain docs/ | grep '^.. docs/src/' || true)" ]]; then
243 error "No docs/src changes remain after formatting; aborting PR creation."
244fi
245
246# Build PR body from suggestions
247PR_BODY_FILE="$TMPDIR/pr-body.md"
248cat > "$PR_BODY_FILE" << 'EOF'
249# Documentation Updates for Preview Release
250
251This draft PR auto-applies queued documentation suggestions collected from
252recently merged PRs.
253
254## How to Use This PR
255
2561. Review the applied changes file-by-file.
2572. Verify brand voice on each touched file as a full-file pass.
2583. Ensure docs use the correct callout type:
259 - Additive features: Preview callout
260 - Behavior modifications: Changed in Preview callout
2614. Merge when ready.
262
263## Auto-Apply Summary
264
265EOF
266
267cat "$APPLY_SUMMARY_FILE" >> "$PR_BODY_FILE"
268
269cat >> "$PR_BODY_FILE" << 'EOF'
270
271## Preview Callouts
272
273Use the Preview callout for new/additive features:
274
275```markdown
276> **Preview:** This feature is available in Zed Preview. It will be included in the next Stable release.
277```
278
279Use this callout for behavior modifications to existing functionality:
280
281```markdown
282> **Changed in Preview (v0.XXX).** See [release notes](/releases#0.XXX).
283```
284
285---
286
287## Pending Suggestions
288
289EOF
290
291# Append each suggestion to PR body
292for file in $(echo "$MANIFEST" | jq -r '.suggestions[].file'); do
293 log "Adding $file to PR body..."
294 echo "" >> "$PR_BODY_FILE"
295 git show "origin/$SUGGESTIONS_BRANCH:$file" >> "$PR_BODY_FILE" 2>/dev/null || true
296 echo "" >> "$PR_BODY_FILE"
297 echo "---" >> "$PR_BODY_FILE"
298done
299
300# Add tracking info
301cat >> "$PR_BODY_FILE" << EOF
302
303## PRs Included
304
305EOF
306
307echo "$MANIFEST" | jq -r '.suggestions[] | "- [PR #\(.pr)](\(.file)): \(.title)"' >> "$PR_BODY_FILE"
308
309cat >> "$PR_BODY_FILE" << 'EOF'
310
311Release Notes:
312
313- N/A
314EOF
315
316git add docs/
317git commit -m "docs: auto-apply preview release suggestions
318
319Auto-applied queued documentation suggestions from:
320$(echo "$MANIFEST" | jq -r '.suggestions[] | "- PR #\(.pr)"')
321
322Generated with script/docs-suggest-publish for human review in draft PR."
323
324# Push and create PR
325log "Pushing branch..."
326git push -u origin "$DOCS_BRANCH"
327
328log "Creating PR..."
329PR_URL=$(gh pr create \
330 --draft \
331 --title "docs: auto-apply preview release suggestions" \
332 --body-file "$PR_BODY_FILE")
333
334echo ""
335echo -e "${GREEN}PR created:${NC} $PR_URL"
336
337# Reset suggestions branch if not keeping
338if [[ "$KEEP_QUEUE" != "true" ]]; then
339 echo ""
340 echo "Resetting suggestions queue..."
341
342 git checkout --orphan "${SUGGESTIONS_BRANCH}-reset"
343 git rm -rf . > /dev/null 2>&1 || true
344
345 cat > README.md << 'EOF'
346# Documentation Suggestions Queue
347
348This branch contains batched documentation suggestions for the next Preview release.
349
350Each file represents suggestions from a merged PR. At preview branch cut time,
351run `script/docs-suggest-publish` to create a documentation PR from these suggestions.
352
353## Structure
354
355- `suggestions/PR-XXXXX.md` - Suggestions for PR #XXXXX
356- `manifest.json` - Index of all pending suggestions
357
358## Workflow
359
3601. PRs merged to main trigger documentation analysis
3612. Suggestions are committed here as individual files
3623. At preview release, suggestions are collected into a docs PR
3634. After docs PR is created, this branch is reset
364EOF
365
366 mkdir -p suggestions
367 echo '{"suggestions":[]}' > manifest.json
368 git add README.md suggestions manifest.json
369 git commit -m "Reset documentation suggestions queue
370
371Previous suggestions published in: $PR_URL"
372
373 git push -f origin "${SUGGESTIONS_BRANCH}-reset:$SUGGESTIONS_BRANCH"
374 git checkout "$ORIGINAL_BRANCH"
375 git branch -D "${SUGGESTIONS_BRANCH}-reset"
376
377 echo "Suggestions queue reset."
378else
379 git checkout "$ORIGINAL_BRANCH"
380 echo ""
381 echo "Suggestions queue kept (--keep-queue). Remember to reset manually after PR is merged."
382fi
383
384# Cleanup
385
386
387echo ""
388echo -e "${GREEN}Done!${NC}"
389echo ""
390echo "Next steps:"
391echo "1. Review the draft PR and verify all auto-applied docs changes"
392echo "2. Confirm full-file brand voice pass for each touched docs file"
393echo "3. Mark ready for review and merge"