Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/plugin-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Search the source tree:
- May have other sections. If other sections are present, ensure they are relevant and correct
- Must have a white background and body text must be black.
- Must be written in English
- Must be at the top-level of the plugin. Must have the same name as the plugin with the .html extension
- Must be at the top-level of the plugin folder, named after the **plugin folder** (kebab-case) with a `.html` extension: `template-manager/` → `template-manager.html`. The short folder-name form is preferred; a `-documentation` suffix (`random-xkcd-documentation.html`) is also acceptable. Do **not** name it after the lowercase `pluginBuilder { pluginName }` when that differs from the folder (i.e. not `templatemanagerplugin.html`).

#### 6.7 Tooltips and in-app help
Code On The Go has a three-tier in-IDE help model: Tier 1 (brief) and Tier 2 (more detail) are tooltips; Tier 3 is a full offline web page reached from a button on the tooltip. Plugins participate through `DocumentationExtension` (all symbols verifiable in `plugin-api.jar`). This is separate from the 6.6 install-decision page — grade them independently.
Expand Down
3 changes: 2 additions & 1 deletion .claude/skills/plugin-review/references/RUBRIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ Help must be available *inside* the running IDE, not only in the standalone 6.6

Requirements:

- The plugin implements `DocumentationExtension` and returns its `plugin_<pluginId>` category from `getTooltipCategory()`.
- The plugin implements `DocumentationExtension` and returns **exactly** `"plugin_<pluginId>"` (the full `plugin.id`) from `getTooltipCategory()`. Any other value (short slug, dotless/underscore form) registers entries under a category the lookup never queries, so tooltips render the literal `n/a` at runtime — **Fail**.
- **Manual `showTooltip` calls must pass the category.** If the plugin shows a tooltip on a custom view via `IdeTooltipService`, it must use the 3-arg `showTooltip(anchorView, category, tag)` with `category = "plugin_<pluginId>"`. A bare 2-arg `showTooltip(view, tag)` resolves under the wrong default category and renders `n/a` even though the entry is registered correctly — **Fail** (the entry exists but never displays; only device long-press reveals it). See the CLAUDE.md "In-app help wiring" recipe.
- **Every UI element the plugin contributes has a tooltip.** Each `NavigationItem`, `MenuItem`, `TabItem`, FAB/toolbar action, and `EditorTabItem` carries a `tooltipTag` (or `tooltip` for `EditorTabItem`); any custom `View` the plugin shows is wired to the tooltip system. No contributed element may be left without help.
- Every `tooltipTag` resolves to a `PluginTooltipEntry` returned from `getTooltipEntries()` — no dangling tags. Each entry provides a Tier 1 `summary` and a Tier 2 `detail`.
- **Complete help is available within the app.** The plugin ships a Tier 3 bundle via `getTier3DocsAssetPath()` that comprehensively covers its functionality, and tooltips link to it through `PluginTooltipButton`s. Tier 3 must work offline (served locally); it is not a link out to the public internet.
Expand Down
13 changes: 13 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ A plugin is an Android *application* module (despite installing as a library) wi

Available permission strings (declared comma-separated in `plugin.permissions`): `filesystem.read`, `filesystem.write`, `network.access`, `system.commands`, `ide.settings`, `project.structure`.

### In-app help wiring (tooltips + Tier 3, `DocumentationExtension`)

Every plugin with UI implements `com.itsaky.androidide.plugins.extensions.DocumentationExtension`. This wiring is fixed and foundational — get **all** of it right or the tooltip renders the literal string **`n/a`** at runtime. The build stays green and the manifest looks fine, so **only device long-press testing catches a mistake** (this bit us once). All symbols are in `plugin-api.jar`.

1. **Category is `"plugin_<pluginId>"` — exactly.** `getTooltipCategory()` MUST return `"plugin_"` + the full `plugin.id` (e.g. `"plugin_org.appdevforall.templatemanagerplugin"`). The host registers your entries under this string **and** derives the same string when resolving a lookup. Any other value — a short slug, a dotless/underscore form — silently mismatches → `n/a`.
2. **Entries.** `getTooltipEntries()` returns `PluginTooltipEntry(tag, summary, detail, buttons)`: `summary` = Tier 1 (one line shown on long-press), `detail` = Tier 2 (HTML behind "See more"). Keep the `tag` in one shared `const val` used by steps 3–4.
3. **Look tooltips up with the 3-arg overload.** Call `IdeTooltipService.showTooltip(anchorView, category, tag)` and pass `category = "plugin_<pluginId>"` explicitly. **Never use the 2-arg `showTooltip(view, tag)`** — it resolves under a different default category and renders `n/a` even when the entry is registered correctly. Param order is `(anchorView, category, tag)`.
4. **Attach tags to UI.** Set `tooltipTag = <that same tag>` on every contributed `NavigationItem` / `TabItem` / menu item / FAB; `EditorTabItem` instead takes a literal `tooltip = "..."` string. A contributed element with no tooltip fails review clause 6.7.
5. **Tier 3 (offline page).** Override `getTier3DocsAssetPath()` to return an assets subdir name (convention: `"docs"`), ship real HTML at `src/main/assets/<dir>/index.html` (white background, black text, English), and link it from an entry via `PluginTooltipButton(description, uri = "index.html", order = 0)` — leave `directPath` false (`true` targets the host's shared docs tree, not your bundle).

Debug a mismatch against the on-device store (`adb root` first):
`sqlite3 /data/data/com.itsaky.androidide/databases/documentation.db "SELECT c.category, t.tag, substr(t.summary,1,40) FROM Tooltips t JOIN TooltipCategories c ON c.id=t.categoryId WHERE c.category LIKE 'plugin_%'"`. If the row is present but the tooltip still shows `n/a`, the bug is the **lookup** (step 1 or 3), not registration. (The unused `ide_tooltip_table` is a red herring — plugin entries live in `Tooltips` + `TooltipCategories`.)

### Convention: AAR metadata checks are disabled

Most plugins end with:
Expand Down
2 changes: 2 additions & 0 deletions docs/process/learnings.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Cross-session gotchas, discoveries, and patterns worth not re-deriving.

- **On-device install markers hide code changes.** `ai-literacy-course`'s `CourseInstaller` extracts its bundle once and gates it behind `.installed-v<INSTALL_VERSION>`; if the marker exists, extraction *and* `CourseShell.generate()` are skipped. A logic fix (e.g. lesson-item ordering) has zero on-device effect until `INSTALL_VERSION` is bumped — it looks like "the fix didn't work" and costs a device round-trip. Bump the version constant as part of any extraction/generation change.
- **`assemblePlugin` silently ships broken `.cgp`s when downloaded assets are missing.** Plugins with a `downloadAssets` task (`ai-literacy-course` → course ZIP + `pdfjs.zip`; `ndk-installer-plugin`) don't fetch those assets during a plain `assemblePlugin`, and there's no build-time warning — the missing asset only surfaces as a runtime failure on device (`Bundled asset not found: pdfjs.zip` → "Could not prepare the course"). Run `./gradlew downloadAssets assemblePlugin` (or `scripts/update-libs.sh`) and `unzip -l` the `.cgp` to confirm assets are present before handing it over.
- **Plugin tooltips render `n/a` when the category/lookup is wrong — build stays green.** `DocumentationExtension` help is registered into `documentation.db` (`Tooltips` + `TooltipCategories`), but a static build/manifest check can't see a category or overload mismatch. Two traps: (1) `getTooltipCategory()` must be exactly `"plugin_<pluginId>"` (full `plugin.id`); (2) manual lookups must use the 3-arg `IdeTooltipService.showTooltip(anchorView, category, tag)` — the 2-arg `showTooltip(view, tag)` resolves under a different default category and shows `n/a` even though the entry is registered. Only a device long-press reveals it. To debug, `adb root` then query `documentation.db`: if the row is present under `plugin_<id>` but the tooltip shows `n/a`, the bug is the lookup, not registration. Full recipe now in `CLAUDE.md` → "In-app help wiring".
- **Plugin Manager icons need `plugin.icon_day`/`_night` → real PNGs, and the Glide cache defeats icon re-verification.** The card icon comes from those two manifest paths (not `android:icon`); ship `src/main/assets/icon_{day,night}.png` (~192px). When re-verifying an icon change under the same plugin id, the Plugin Manager caches via Glide keyed by path with no mtime invalidation — the old icon persists until you `adb shell rm -rf /data/data/com.itsaky.androidide/cache/image_manager_disk_cache` (needs `adb root`) or install on a clean device.

## CoGo project templates (Pebble `.cgt`)

Expand Down
40 changes: 40 additions & 0 deletions docs/process/retrospective.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,43 @@
| Agent waited to be asked before running plugin-review | CLAUDE.md | Added "Proactively offer `/plugin-review`" paragraph to the "Plugin review skill" section, listing the triggering changes (new plugin import, dep change, API touch, new asset, libs/ update) |
| Agent marked builds "verified" without device-level proof | CLAUDE.md | Added new "Verification" section before "Adding a new plugin", stating build success is necessary but never sufficient and device install is the terminal verification step |
| Same as above, reinforcement | Memory | `feedback_plugin_verify_on_device.md` created mid-session — per-project memory layer reinforcing the CLAUDE.md rule |

## 2026-07-24 - Template Manager plugin: review → fix blockers → device verify → icons/naming → PR

### Time Breakdown
| Started | Phase | 👤 Hands-On | 🤖 Agent | Problems |
|---------|-------|-------------|----------|----------|
| 3:28pm | Initial review (research subagent, build via symlink, security audit, rubric scorecard) | ██ 8m | ██ 11m | |
| 3:39pm | Fix blockers + first device install (`../libs`, manifest, Tier 3, HTML docs; build; emulator install; sidebar + list verified) | ██ 10m | ████ 39m | ⚠ tooltip showed `n/a` |
| 4:18pm | Root-cause tooltip + icons + card + permissions (`documentation.db` → 3-arg `showTooltip`; icons v1→v2 CGT; card title; reinstall; Tier 1/2/3 verified) | ██ 8m | ██ 22m | ⚠ 1 wrong hypothesis; icons redone once |
| 4:40pm | Naming normalization + "Template Manager" rename (reinstall + verify) | █ 3m | █ 12m | |
| 4:52pm | Commit + push + PR #51 | █ 1m | █ 5m | |

### Metrics
| Metric | Duration |
|--------|----------|
| Total wall-clock | ~1h 32m |
| Hands-on | ~30m (33%) |
| Automated agent time | ~62m (67%) |
| Idle/testing/away | minimal |
| Retro analysis time | ~2 min |

_Note: the transcript script reported 108 min "hands-on" but over-counted — it billed two skill injections (plugin-review SKILL text; commit-push-pr context) as user typing (~54 min phantom). Real hands-on ≈ 30 min, mostly reading review reports._

### Key Observations
- **Device verification, not the build, found the defect.** Build green + manifest correct, yet the tooltip rendered `n/a` — a bug the original code also had. Only a device long-press exposed it. Strongest evidence yet for "build success ≠ verification."
- **First tooltip fix hypothesis was wrong.** Changing `getTooltipCategory` alone didn't work; the fix came from inspecting `documentation.db` (entry was registered; the 2-arg `showTooltip` lookup was at fault). Lesson: go to ground truth sooner instead of reasoning from sibling-plugin comparison.
- **Icons rendered twice** (stacked-cards → "meh" → CGT-file). Partly driven by the later CGT requirement; a quick direction sketch before a full render could have saved a pass.
- **~20 turns of autonomous device driving** (install/uninstall/reinstall/DB queries/tooltip tests) with no input needed. High productive-to-rework ratio.
- **Platform reinstall cost is inherent** (release signature mismatch forces uninstall→restart→clear-cache→reinstall→restart); batching changes to minimize cycles was correct.

### Feedback
**What worked:** (from user) The tooltip issue should never recur — asked to codify the foundational wiring so it's right the first time.
**What didn't:** Getting the tooltip wiring right required a device round-trip and one wrong hypothesis; it's deterministic tech that shouldn't have been ambiguous.

### Actions Taken
| Issue | Action Type | Change |
|---|---|---|
| Tooltip wiring got `n/a` and cost a device round-trip; it's foundational and deterministic | CLAUDE.md | Added "### In-app help wiring (tooltips + Tier 3, `DocumentationExtension`)" under Architecture — the exact recipe: category = `plugin_<pluginId>`, always 3-arg `showTooltip(anchorView, category, tag)`, tooltipTag rules, Tier 3 setup, and the `documentation.db` debug query |
| `/plugin-review` couldn't statically catch the 2-arg `showTooltip` / wrong-category trap | Skill (RUBRIC.md 6.7) | Added two Fail-level checks: category must be exactly `plugin_<pluginId>`, and manual `showTooltip` must use the 3-arg category overload (bare 2-arg → `n/a`) |
| Tooltip + icon-cache gotchas would otherwise be re-learned | Docs (learnings.md) | Added the tooltip `n/a` root-cause + debug query and the Glide icon-cache invalidation note to "Plugin build & install gotchas" |
78 changes: 78 additions & 0 deletions template-manager/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Built application files
*.apk
*.aar
*.ap_
*.aab

# Files for the ART/Dalvik VM
*.dex

# Java class files
*.class

# Generated files
/bin/
/gen/
/out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# Gradle files
.gradle/
/build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
captures/

# IntelliJ
*.iml
.idea
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml

# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore

# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/

# Google Services (e.g. APIs or Firebase)
# google-services.json

# Freeline
freeline.py
freeline/
freeline_project_description.json

# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md

# Version control
vcs.xml

# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
Loading