Android Agent Skills & AI Rules

GitHub

Bitwarden mobile apps (Password Manager and Authenticator) for Android.

9,099 stars Kotlin Kotlin 1 file ยท ~17,892 tokens #android#bitwarden#compose#jetpack#kotlin
RAW Rules
5 Discovered Rules โ€ข ~3,093 Estimated Tokens
Quick:
Loading skill content...

AI Agent Protocol & Instructions

AGENTS.md root-instruction ~1,648 tokens
Raw
# AGENTS.md โ€” DuckDuckGo Android Browser

DuckDuckGo Android is a privacy-focused browser built as a large multi-module Gradle project,
providing built-in search, tracker blocking, HTTPS enforcement and other privacy features.

**Versions** (SDK levels, Kotlin, Gradle, libraries) live in the build files โ€” don't restate them here:
`min_sdk` / `target_sdk` / `compile_sdk` in `build.gradle`, `version.kotlin` in `versions.properties`,
and the Gradle version in `gradle/wrapper/gradle-wrapper.properties`.
**Build:** AGP via refreshVersions. DI is **Metro** (KSP generators in `:codegen-ksp`; see `.claude/docs/architecture.md`).
**Toolchain:** Kotlin JVM target 17; building requires **JDK 21** (Metro compiler plugin).

Notable libraries: Room, Retrofit/OkHttp/Moshi, Coroutines, Jetpack Compose (selective), WorkManager,
`logcat` (Square), KSP for annotation processing. Versions are in `versions.properties` and the module
`build.gradle` files.

---

## Privacy invariants for anything leaving the device

This is a privacy browser; these hold for every outbound value โ€” pixels, wide events, logs, crash
reports, debug output โ€” not just telemetry you are deliberately designing.

- **No PII.** Never emails, names, account IDs, usernames or phone numbers, in a name or a value.
- **No URLs, domains or page titles.** Breakage reports are the one controlled exception, with explicit
  user consent.
- **No correlation IDs.** No session IDs, GUIDs or exact timestamps โ€” anything that links events to one
  user session.
- **Bucket numeric values.** Exact durations, byte counts and item counts fingerprint users; send ranges.
- **Bounded enums over free-form strings.** High-cardinality unbounded strings are both a privacy risk
  and unanalysable.

Adding or changing a pixel or the pixel registry also requires privacy triage โ€” see
`.claude/docs/pixels.md`.

---

## Read these when the situation applies

These files are not in context. Read the whole file before doing the work it covers โ€” don't rely on
what you remember of it.

| Read | When |
|---|---|
| `.claude/docs/architecture.md` | **planning or implementing** any new code, refactoring, or module changes โ€” `-api`/`-impl` structure, DI scopes, navigation, coroutine job patterns |
| `.claude/docs/android-design-system.md` | **planning or implementing** any UI โ€” which ADS component to use, colors, spacing; raw Android/Material widgets are lint-blocked |
| `.claude/docs/contributions.md` | **planning** a change that touches a `-api` surface or spans multiple modules โ€” it needs an approved API Proposal and/or Tech Design, and that has to be raised before implementation, not at PR time. Also read it when naming a branch, writing a commit, or opening a PR |
| `.claude/docs/lateinit-hazards.md` | writing or reviewing any `lateinit var`, especially `@Inject lateinit var` in a View |
| `.claude/docs/plugin-system.md` | declaring a plugin point or contributing a plugin (`PluginPoint` / `ActivePluginPoint`) |
| `.claude/docs/native-input.md` | **changing** the native input (`NativeInputModeWidget`, its plugins and footers, `NativeInputManager`, the contextual sheet or the edit screen) โ€” adding a control, state-store rules, host gotchas, and the refactor still in flight |
| `.claude/docs/pixels.md` | adding or changing pixel telemetry โ€” including whether a pixel or a wide event is the right instrument |
| `.claude/docs/di-scopes.md` | an injection fails at runtime ("Could not find the injector"), or you're deciding which scope to pass to `@InjectWith` โ€” has the component/subcomponent diagram and the `injectorFactoryMap` lookups |
| `.claude/docs/navigation.md` | adding a screen or navigating to one โ€” `ActivityParams`, `@ContributeToActivityStarter`, deeplinks, and which `GlobalActivityStarter` overload to use |
| `.claude/docs/url-classification.md` | routing typed input to navigation vs search โ€” use `QueryUrlPredictor`, not `UriString.isWebUrl()` |
| `.claude/docs/icons.md` | the change needs an icon the project doesn't have yet โ€” it must be fetched from the internal Icons repository, never invented |
| `.claude/docs/translations.md` | adding or changing any user-facing string โ€” `donottranslate.xml` vs `strings-<module>.xml`, placeholder/`instruction` rules, how to change or remove already-translated copy |

---

## Build & Test Commands

```bash
# Unit tests (all modules)
./gradlew jvm_tests

# Unit tests for a single module
./gradlew :my-feature-impl:testDebugUnitTest

# Code quality (spotless + lint + unit tests)
./gradlew jvm_checks

# Lint only
./gradlew lint_check

# Code formatting check / fix
./gradlew spotlessCheck
./gradlew spotlessApply

# Install app
./gradlew installInternalRelease    # internal build (more testing features)
./gradlew installPlayRelease        # play store build
```

`jvm_tests` and `jvm_checks` resolve to `testPlayDebugUnitTest` in `:app` and `testDebugUnitTest` in
library modules. To run a single test class, use `--tests`:

```bash
./gradlew :my-feature-impl:testDebugUnitTest --tests "com.duckduckgo.my.feature.RealFooTest"
```

### Build Variants

| Dimension | Flavors |
|---|---|
| store | `internal`, `fdroid`, `play` |
| Build types | `debug`, `release`, `upload` |

### Proprietary Fonts

The app uses a proprietary DuckSans font from a private GitHub Packages repository. The build
conditionally swaps between the proprietary `ddg-proprietary-fonts` AAR and a local `:fonts` fallback
module (empty `<font-family/>` stubs) based on credential availability. See `build.gradle` for the
credential detection logic and `android-design-system/fonts/readme.md` for details.

---

## Code Formatting

- **Spotless** with ktlint for Kotlin; Google Java Format in AOSP style for Java
- Max line length: 150 characters
- Ratchet from `origin/develop` โ€” only changed code is enforced

---

## Code Comments

Comments explain the **why** (intent, assumptions, non-obvious decisions), not the **what** the code
already shows.

Default to no comment: prefer self-documenting code (clear names, small functions), and add one only
where code alone can't carry the reasoning, and only if it still answers *"why was this done this
way?"* for someone reading it two years from now. `-api` module declarations may carry KDoc describing
the contract โ€” that's documentation for consumers, not narration.

Never add:

- **narration** of trivial code
- **conversational / temporal residue**
- **process / plan references**

```kotlin
count++ // increment the counter -> narration of trivial code
val user = repo.load() // fixes the bug from the previous task -> temporal residue
val state = flow // changed from LiveData -> temporal residue
cache.clear() // step 3 of the plan -> process / plan reference
retryCount = 3 // per the Asana task -> process / plan reference
```

When reviewing a diff, flag any added comment that doesn't follow these rules.