diff --git a/.agents/skills/implement/SKILL.md b/.agents/skills/implement/SKILL.md new file mode 100644 index 000000000..410a9c8cc --- /dev/null +++ b/.agents/skills/implement/SKILL.md @@ -0,0 +1,197 @@ +--- +name: implement +description: "End-to-end implementation workflow: plan, implement, verify, commit, and open a draft PR. Use when asked to implement, fix, build, or work on something." +--- + +# Implement Workflow + +This skill handles the full lifecycle: plan -> implement -> verify -> commit -> draft PR. + +## Phase 1: Plan (requires approval) + +1. **Understand the request** — Read the issue description, linked context, or user instructions. +2. **Explore the codebase** — Find relevant files, understand existing patterns, read tests. +3. **Create an implementation plan**: + - What files will be modified/created + - What the changes will do + - What edge cases to consider + - What tests to add or update +4. **Present the plan and STOP** — Wait for explicit user approval before proceeding. + +Do NOT start coding until the plan is approved. If requirements are ambiguous, ask. + +## Phase 2: Implement + +Execute the approved plan: +- Follow existing code patterns and conventions in the repo +- Keep changes minimal and focused — don't refactor unrelated code +- Kotlin warnings are treated as errors (`allWarningsAsErrors = true`) — write clean code +- Use `org.wordpress.aztec` package conventions +- **Unit tests**: Add or update tests covering new/changed logic. Follow existing test patterns in the repo. If writing meaningful tests would require disproportionate effort (e.g., complex setup, heavy mocking of framework internals), skip but notify the developer explaining why. + +## Phase 3: Verify + +Run verification checks and fix any failures. Iterate until all checks pass. + +### 3a: Compile + +```bash +./gradlew :aztec:assembleRelease :app:assembleDebug +``` + +If other modules were changed, compile those too: +```bash +./gradlew assembleRelease +``` + +### 3b: Lint + +```bash +./gradlew ktlint +``` + +- Fix violations — prefer real fixes over suppression +- Only suppress when it's a false positive and document why + +### 3c: Unit Tests + +```bash +./gradlew :aztec:testRelease +``` + +Or run specific tests related to the changes: +```bash +./gradlew :aztec:testReleaseUnitTest --tests "org.wordpress.aztec.SpecificTest" --info +``` + +**Test rules:** +- NEVER weaken or remove assertions to make tests pass +- Don't make unrelated or incorrect changes to production code solely to appease a test. If a test reveals a real issue, fix it properly. +- Tests that pass only by not crashing are invalid — every test needs meaningful assertions +- If a test won't pass after reasonable attempts: stop and ask + +### 3d: Fix Errors + +If any step fails: +1. Analyze the error +2. Fix the issue +3. Re-run the failing check +4. Repeat until green + +## Phase 4: Present Changes (requires approval) + +Show a summary of all changes made: +- Files modified/created +- Key behavioral changes +- Test coverage + +**STOP and wait for user approval** before committing. + +## Phase 5: Commit and Draft PR + +Only proceed after explicit approval. + +### 5a: Run Final Lint + +```bash +./gradlew ktlint +``` + +Fix any remaining issues. + +### 5b: Inspect Changes + +```bash +git status +git diff --stat +git diff +``` + +### 5c: Plan Commits + +Review the changes and determine if they should be split into multiple commits: +- Independent logical units = separate commits +- Bug fix + feature = separate commits +- Formatting + logic = separate commits + +For **each commit**, prepare: +1. **Commit message**: Imperative summary + brief body +2. **Files**: Paths to stage +3. **Summary**: What and why + +**Commit message format** — use direct multi-line strings: +```bash +git commit -m "Imperative summary + +- Detail one +- Detail two +" +``` + +**Rules:** +- NO co-author lines — NEVER add "Co-Authored-By" or AI attribution +- Each commit should be cohesive and buildable +- Use `git add -p` to split mixed concerns if needed + +### 5d: Stage and Commit + +```bash +git add +git commit -m "message" +``` + +### 5e: Push and Create Draft PR + +```bash +# Get the correct remote owner/repo +git remote get-url origin + +# Push +git push -u origin HEAD + +# Create draft PR +PAGER=cat gh pr create --draft --title "PR title" --body "$(cat <<'EOF' +### Fix + + +### Test +1. Step 1 +2. Step 2 +EOF +)" +``` + +**PR rules:** +- Title: short, under 70 characters +- Body: follows the repo's PR template format (### Fix, ### Test, ### Review) +- Always create as **draft** +- Return the PR URL when done + +## Handling Edge Cases + +### Working on an issue with a branch name +If the user mentions an issue with a known branch name: +```bash +git checkout trunk && git pull && git checkout -b +``` + +### Determining diff base for existing PRs +PRs can be stacked — check the actual base: +```bash +PAGER=cat gh pr view --json baseRefName +``` + +### Posting review comments +When reviewing or addressing PR feedback: +```bash +# Create review JSON +printf '%s\n' '{ + "event": "COMMENT", + "body": "Review comment", + "comments": [ + {"path": "file.kt", "line": 42, "body": "Inline comment"} + ] +}' > /tmp/pr_review.json + +PAGER=cat gh api repos/{owner}/{repo}/pulls/{number}/reviews --method POST --input /tmp/pr_review.json +``` diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 000000000..2b7a412b8 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..41724695c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,140 @@ +# Instructions + +This file provides guidance to coding agents when working with code in this repository. + +## General Approach + +- Analyze assumptions and provide counterpoints — prioritize truth over agreement. +- Treat me as an expert Android developer. Give overviews, not tutorials. +- Always give an overview of the solution before diving into implementation (unless explicitly asked to implement right away). +- Do not add comments for trivial logic or when the name is descriptive enough. + +## Architecture Overview + +**AztecEditor-Android** is a rich-text HTML editor library for Android, built on the `EditText` / `Spannable` API. + +**Stack**: Kotlin, Android Spannable API, Plugin architecture, Gradle with Kotlin DSL + +### Module Structure + +``` +:app — Demo application +:aztec — Core editor library (main deliverable) +:glide-loader — Image loading via Glide +:picasso-loader — Image loading via Picasso +:wordpress-comments — WordPress comments plugin +:wordpress-shortcodes — WordPress shortcodes plugin +:media-placeholders — Media placeholders with Compose support +``` + +All plugin/loader modules depend on `:aztec`. Published independently to Automattic S3 Maven (`org.wordpress:aztec`, `org.wordpress.aztec:*`). + +### Code Navigation (package: `org.wordpress.aztec`) + +| To find... | Look in... | +|---------------------------------|------------------------------------------------------| +| Main editor component | `aztec/.../AztecText.kt` (extends EditText, ~3K LOC) | +| Editor facade / initialization | `aztec/.../Aztec.kt` | +| HTML parsing | `aztec/.../AztecParser.kt`, `AztecTagHandler.kt` | +| Custom spans (visual rendering) | `aztec/.../spans/` (62 files — one per HTML element) | +| Text formatting logic | `aztec/.../formatting/` (Block, Inline, Line, List) | +| Block element handlers | `aztec/.../handlers/` (Heading, List, Quote, etc.) | +| Plugin interfaces | `aztec/.../plugins/` (IAztecPlugin, IToolbarButton) | +| HTML source editor | `aztec/.../source/SourceViewEditText.kt` | +| Text change watchers | `aztec/.../watchers/` (30+ files, API-version buckets)| +| Toolbar | `aztec/.../toolbar/AztecToolbar.kt` | +| Undo/redo | `aztec/.../History.kt` | +| Compose placeholders | `media-placeholders/.../ComposePlaceholder*.kt` | + +### Key Architectural Patterns + +1. **Span-based rendering** — Each HTML element has a custom `Span` class. Spans carry both visual rendering and semantic meaning. The `Spannable` API is central to everything. + +2. **Plugin architecture** — `IAztecPlugin` with sub-interfaces (`IToolbarButton`, `IClipboardPastePlugin`, `IOnDrawPlugin`). Plugins for `html2visual` and `visual2html` conversion pipelines. + +3. **Facade pattern** — `Aztec` class provides a builder-like fluent API to wire up editor, toolbar, source view, and plugins. + +4. **Handler/Formatter split** — Handlers deal with block element structure (headings, lists, quotes). Formatters apply styling (inline, block, line-block, list, link, indent). + +5. **Bidirectional HTML conversion** — HTML string <-> Spannable representation, with plugin hooks at each stage. + +6. **Watcher pattern** — Multiple `TextWatcher` implementations with API-version-specific buckets (API 25, 26+) for Samsung/OEM compatibility. + +### Common Crash Patterns (from recent PRs) + +- `IndexOutOfBoundsException` from span start/end being out of bounds — always clamp to `[0, text.length]` +- `IllegalArgumentException` when span end < span start — validate before applying +- Samsung/custom emoji OEM issues causing unexpected span states +- Thread safety in `AztecAttributes` — operations should be synchronized + +## Git Operations (CRITICAL) +- **NEVER commit without explicit permission** +- **NEVER push without explicit permission** +- **NEVER create a PR without explicit permission** +- When asked to "fix" or "update" something, that does NOT imply permission to commit/push +- Always wait for explicit "commit", "push", or "create PR" commands +- Do not create branches, rebase, or modify git history unless explicitly asked + +## Build Commands + +```bash +# Build and run the demo app +./gradlew :app:installDebug && adb shell am start -n org.wordpress.aztec/org.wordpress.aztec.demo.MainActivity + +# Build the demo app (without installing) +./gradlew :app:assembleDebug + +# Build the library +./gradlew :aztec:assembleRelease + +# Lint (ktlint + Android lint) +./gradlew ktlint +./gradlew lintRelease + +# Unit tests (core library) +./gradlew aztec:testRelease + +# All unit tests +./gradlew testRelease + +# Specific test class +./gradlew :aztec:testReleaseUnitTest --tests "org.wordpress.aztec.AztecParserTest" --info + +# Specific test method +./gradlew :aztec:testReleaseUnitTest --tests "org.wordpress.aztec.AztecParserTest.testMethodName" --info +``` + +### Build Configuration + +- All Kotlin warnings are errors (`allWarningsAsErrors = true`) +- Versions are defined in root `build.gradle` — check there for current Kotlin, AGP, SDK, and dependency versions + +## GitHub Commands + +```bash +PAGER=cat gh pr list +PAGER=cat gh pr view +PAGER=cat gh pr view --comments +PAGER=cat gh pr diff +``` + +## PR Template + +PRs follow this format (from `.github/PULL_REQUEST_TEMPLATE.md`): +``` +### Fix + + +### Test +1. Step 1 +2. Step 2 + +### Review +@reviewer +``` + +## Skills + +| Skill | Trigger phrases | +|-------------|------------------------------------------------------------------------------------------------------| +| `implement` | "implement", "fix this", "work on this", "build this", "add feature", any implementation request | diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 000000000..47dc3e3d8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file