Skip to content

ADFA-4857 | Add project-to-template plugin - #56

Merged
hal-eisen-adfa merged 3 commits into
mainfrom
ADFA-4857-templatizer-plugin
Jul 31, 2026
Merged

ADFA-4857 | Add project-to-template plugin#56
hal-eisen-adfa merged 3 commits into
mainfrom
ADFA-4857-templatizer-plugin

Conversation

@hal-eisen-adfa

Copy link
Copy Markdown
Contributor

Summary

Incorporates the standalone templatize-project plugin into the monorepo as project-to-template/, conforming to repo conventions and wired into the build + website-deploy pipelines.

The plugin adds a Project to Template sidebar item that converts the currently-open Android project into a Code On The Go .cgt template bundle (original untouched — it copies then tokenizes with Pebble ${{TOKEN}}s) and can install it straight into the New Project template picker via IdeTemplateService.

What changed on the way in

  • Full rename to org.appdevforall.projecttotemplate / "Project to Template".
  • Shared infra: uses repo-root ../libs/*.jar and ../gradlew — no bundled jars or wrapper.
  • Tooltip fix: getTooltipCategory() now returns the full plugin_org.appdevforall.projecttotemplate id, so the in-IDE tooltip renders real text instead of the literal n/a.
  • Security: sanitizes the user-typed template name before using it as a filesystem path.
  • Docs: Tier 3 offline help (assets/docs/index.html) + top-level website project-to-template-documentation.html.
  • New icon: solid project card → arrow → dashed template card (day/night PNGs + monochrome ic_plugin vector), replacing the 4-square grid glyph. Source SVGs + generator under icon-src/.
  • Text: normalized user-facing strings to "Code On The Go".
  • Wiring: README Examples row + update-libs.yml website MAP entry.

Verification

  • ../gradlew assemblePlugin and assemblePluginDebug both build clean.
  • Passed the plugin-review rubric (green-light; 6.1 min-only IDE version is the repo norm).
  • Device-verified on emulator end-to-end: installs, activates (survives restart), sidebar + 3-tier tooltip render (no n/a), and a real conversion of an open project (15 modified) produces a .cgt that installs via IdeTemplateService ([OK] Installed template).

🤖 Generated with Claude Code

… .cgt template)

Incorporates the standalone templatize-project plugin into the monorepo as
project-to-template, conforming to repo conventions and wired into the build
and website-deploy pipelines.

- Full rename to org.appdevforall.projecttotemplate / "Project to Template".
- Uses shared repo-root ../libs/*.jar and ../gradlew (no bundled copies).
- Fixes tooltip category to the full plugin.id (plugin_org.appdevforall.projecttotemplate)
  so the in-IDE tooltip renders real text instead of "n/a".
- Sanitizes the user-typed template name before using it as a filesystem path.
- Adds Tier 3 offline docs (assets/docs/index.html) + website documentation page.
- New icon: solid project card -> arrow -> dashed template card (day/night PNGs +
  monochrome ic_plugin vector), replacing the 4-square grid glyph. Source SVGs +
  generator under icon-src/.
- Normalizes user-facing text to "Code On The Go".
- Adds README Examples row and update-libs.yml website MAP entry.

Device-verified on emulator: installs, activates, sidebar + 3-tier tooltip render,
and a real conversion of an open project produces a .cgt that installs via
IdeTemplateService.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@jatezzz jatezzz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Reviewed against freshly-fetched origin/main. I did not build or device-test — taking the PR's "builds clean + emulator-verified end-to-end" at face value.

What's right

The repo conventions are all here, including the ones that usually get missed: tooltip category is the full "plugin_org.appdevforall.projecttotemplate" form (not a slug), TOOLTIP_TAG is a shared const wired to the NavigationItem, Tier 3 getTier3DocsAssetPath() + a real assets/docs/index.html (white bg / black text), repo-root ../libs/*.jar + ../gradlew with no bundled copies, Theme.Material3.DayNight + values-night/, AAR metadata checks disabled, permission strings are exact enum values, sanitizeTemplateName genuinely kills traversal, views nulled in onDestroyView, dry run against a disposable temp copy, and both README + update-libs.yml MAP wiring are present.

Summary of findings

Should fix before merge:

  1. Dark-mode status colors are hardcoded — the values-night variants are dead resources.
  2. Re-running with a different template name zips the previous run into the new .cgt.
  3. skipCleanup suppresses the personal-info warning too, including keystores.
  4. local.properties ships in the .cgt by default.

Worth fixing (device-level cost):

  1. The entire build/ tree is copied, then deleted.
  2. Log rendering is O(n^2) on the main thread.

Minor: install button can't be retried after a failed install; the "unit-testable" KDoc claim is contradicted by android.util.Base64 + unitTests.isReturnDefaultValues = true; UI strings hardcoded rather than in strings.xml; duplicate log lines; isIgnoredUnderRoot is redundant with the onEnter prune; compileSdk/targetSdk = 34 will collide with #54.

Details are in the inline comments.

Verdict

Approve once #1 and #2 are fixed — those two produce visibly wrong output. #3/#4 are judgment calls, but both cut against the plugin's purpose (shipping a template someone else installs), so worth folding in here. Everything else is follow-up material.

Comment thread project-to-template/src/main/res/layout/fragment_main.xml Outdated
Comment thread project-to-template/build.gradle.kts
@hal-eisen-adfa
hal-eisen-adfa requested a review from a team July 31, 2026 16:14
Fixes from jatezzz's review:

Should-fix (produce visibly wrong output):
- Dark-mode status colors: use ContextCompat.getColor(status_*_text)
  instead of hardcoded light hex, so values-night/ is no longer dead.
- Re-run isolation: wipe the whole output dir (not just dest/) before a
  real run, so a previous template name's tree isn't zipped into the new
  .cgt.
- skipCleanup no longer suppresses the personal-info warning: keystores
  are flagged (not silently shipped) when cleanup is skipped, and
  flagPersonalInfoFiles always runs.
- local.properties is now in COPY_IGNORE (always machine-specific), so it
  is never bundled.

Worth-fixing:
- build/ is skipped at copy time when cleaning up, instead of copied then
  deleted (hundreds of MB of I/O on device).
- Log rendering uses TextView.append() (O(1)/line) instead of rebuilding
  the whole string per line.

Minor:
- Install button re-enables after a failed install so it can be retried.
- Switch android.util.Base64 -> java.util.Base64 (minSdk 26) and add JVM
  unit tests for sanitizeTemplateName + a dry-run substitution path.
- Move user-facing UI strings into strings.xml.
- Remove duplicate per-file [OK] log lines (writePeb already logs).
- Drop redundant isIgnoredUnderRoot (onEnter prune + name filter cover it).

Verified: testDebugUnitTest + assemblePlugin green; a JVM harness runs the
real createTemplateBundle twice (Foo then Bar) and confirms no stale-tree /
local.properties / build/ / keystore leakage into the .cgt.
@hal-eisen-adfa

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review, @jatezzz — addressed everything in 2957e4e.

Should-fix

  1. Dark-mode colorsshowStatus now uses ContextCompat.getColor(…, R.color.status_error_text / status_success_text), so values-night/ is live.
  2. Re-run bundling — a real run now wipes the whole outputDir (not just dest/) before copying, so a prior name's tree can't be zipped in. .cgt files live in outputDir.parentFile, so previous bundles are preserved.
  3. skipCleanup suppressing warningsflagPersonalInfoFiles now always runs; when cleanup is skipped, keystores are flagged ([REVIEW] … remove it before distributing) instead of silently shipped.
  4. local.properties — added to COPY_IGNORE outright; google-services.json left flag-only as you suggested.

Worth-fixing
5. build/ — added to the copy-time skip when !skipCleanup, so it's never copied (was copy-then-delete).
6. O(n²) log — switched to logText.append("$line\n").

Minor

  • Install button re-enables after a failed install (retry without re-converting).
  • android.util.Base64java.util.Base64 (minSdk 26), and added JVM unit tests: sanitizeTemplateName (incl. traversal) + a dry-run substitution path. The dry-run pipeline touches no Android APIs, so it's safe under isReturnDefaultValues.
  • UI strings moved to strings.xml.
  • Removed the duplicate per-file [OK] lines (writePeb already logs) at the three sites.
  • Deleted isIgnoredUnderRoot — the onEnter prune + name !in ignore filter cover it.

Note (12) — left compileSdk/targetSdk = 34 as-is. #54 (SDK 36) is still open and owns the toolchain bump; whichever lands second rebases, as you noted.

VerificationtestDebugUnitTest + assemblePlugin green. Beyond the unit tests I ran the real (non-dry) createTemplateBundle twice via a JVM harness (Foo then Bar) and asserted the resulting .cgt contains no stale Foo/ subtree, no local.properties, no build/, and no .jks by default — and that skipCleanup retains and flags the keystore. All passed.

@hal-eisen-adfa

Copy link
Copy Markdown
Contributor Author

Device verification (emulator, Code On The Go)

Installed the debug .cgp via IDE preferences → Plugin Manager (uninstalled the prior build, installed the new one, restarted) and drove the full flow against a real "My Application" project that contains a release.keystore, release.properties, and a build/ dir.

.cgt contents confirmed by pulling each bundle off the device and inspecting with unzip.

…-plugin

# Conflicts:
#	.github/workflows/update-libs.yml
#	README.md
@hal-eisen-adfa
hal-eisen-adfa merged commit 3a18cc7 into main Jul 31, 2026
@hal-eisen-adfa
hal-eisen-adfa deleted the ADFA-4857-templatizer-plugin branch July 31, 2026 23:29
hal-eisen-adfa added a commit that referenced this pull request Aug 6, 2026
Two plugins landed on main after this branch was cut, so they were never
standardized:

- project-to-template (#56): compileSdk/targetSdk 34 -> 36
- get-ai-models (#55):       Kotlin 2.1.0 -> 2.3.0

Both get-ai-models and template-manager pin Kotlin twice: the
kotlin-gradle-plugin classpath in settings.gradle.kts, and `kotlin` in
gradle/libs.versions.toml, which drives libs.kotlin.stdlib. The catalog
comment states the two must match, so both were bumped.

That surfaced a defect in this branch: it had already bumped
template-manager's classpath to 2.3.0 but left its catalog at 2.1.0, so
the compiler and stdlib disagreed. Fixed here.

Verified: all 27 plugins now report compileSdk/targetSdk 36, AGP 8.11.0,
Kotlin 2.3.0, with no catalog left at an older pin. All four affected
plugins build assemblePlugin successfully; kotlin-stdlib resolves to
2.3.0 in get-ai-models and template-manager, and the packaged manifests
of project-to-template and get-ai-models report targetSdkVersion=36.
hal-eisen-adfa added a commit that referenced this pull request Aug 7, 2026
…P 8.11.0, Kotlin 2.3.0, Gradle 8.14.3 (#54)

* ADFA-4907 | Standardize plugin toolchain: compileSdk/targetSdk 36, AGP 8.11.0, Kotlin 2.3.0, Gradle 8.14.3

Aligns all 25 example plugins with Code On The Go's on-device build toolchain
(verified on device: CoGo ships only android-36, AGP 8.11.0, Gradle 8.14.3;
does not pin Kotlin). Relevant because plugins are being built with CoGo itself
(ADFA-4693).

- compileSdk & targetSdk -> 36 (22 plugins; 3 template installers already 36)
- AGP -> 8.11.0 (ai-assistant, ai-core from 8.13.2; client-time-tracker,
  compose-preview, layout-editor from 8.8.2 -- downgrades intentional, match CoGo)
- Kotlin -> 2.3.0 (flutter-template, pebble, template-manager from 2.1.0)
- Gradle wrapper -> 8.14.3 (ai-assistant, ai-core from 8.14.4; client-time-tracker,
  compose-preview, layout-editor from 8.10.2)
- template-manager: migrate deprecated kotlinOptions.jvmTarget string DSL to
  compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } (required by Kotlin 2.3.0)

Java/jvmTarget already uniform at 17. 20/25 plugins build clean. 5 (ai-assistant,
ai-core, code-suggestions-plugin, speech-to-text-plugin, vector-search-plugin)
fail with pre-existing unresolved-reference errors (stale libs/plugin-api.jar,
missing newer API symbols) -- verified failing at baseline before this change,
out of scope here.

* ADFA-4907 | Extend toolchain standardization to 3 missed plugins

Two plugins landed on main after this branch was cut, so they were never
standardized:

- project-to-template (#56): compileSdk/targetSdk 34 -> 36
- get-ai-models (#55):       Kotlin 2.1.0 -> 2.3.0

Both get-ai-models and template-manager pin Kotlin twice: the
kotlin-gradle-plugin classpath in settings.gradle.kts, and `kotlin` in
gradle/libs.versions.toml, which drives libs.kotlin.stdlib. The catalog
comment states the two must match, so both were bumped.

That surfaced a defect in this branch: it had already bumped
template-manager's classpath to 2.3.0 but left its catalog at 2.1.0, so
the compiler and stdlib disagreed. Fixed here.

Verified: all 27 plugins now report compileSdk/targetSdk 36, AGP 8.11.0,
Kotlin 2.3.0, with no catalog left at an older pin. All four affected
plugins build assemblePlugin successfully; kotlin-stdlib resolves to
2.3.0 in get-ai-models and template-manager, and the packaged manifests
of project-to-template and get-ai-models report targetSdkVersion=36.

* ADFA-4907 | Enforce the standard toolchain in CI

Adds scripts/check-toolchain.sh and a pull_request-triggered workflow that
fails the build when any Gradle module drifts off the ADFA-4907 standard
(compileSdk/targetSdk 36, AGP 8.11.0, Kotlin 2.3.0, Gradle 8.14.3-bin,
Java 17).

Until now nothing enforced this. Both existing workflows are
workflow_dispatch-only, so a PR could move any of these values with zero CI
signal — which is how origin/main still carries compileSdk 33/34/35, AGP
8.8.2/8.13.2 and Gradle 8.10.2. The check is pure text inspection (no JDK,
no Gradle, no network), so it runs in seconds and is cheap to require.

It walks every build.gradle.kts, including subprojects. The earlier sweep
enumerated only top-level plugin folders, which is why ai-core/llama-impl
kept compileSdk = 34 through a dedicated standardization pass.

Files under src/main/assets/ are excluded: those are project templates that
Code On The Go stamps out for the user's own app, not our builds.

minSdk is deliberately not checked — it legitimately varies (21 template
installers, 26 most, 28 Beepy/sketch-to-ui, 33 the AI plugins).

Also fixes the nine violations the check found, so it lands green:
  * kotlin-stdlib pinned behind the Kotlin plugin -> 2.3.0
    (flutter-template, bookshelf, ndk-installer-plugin, cotg-ndk, pebble)
  * ai-core/llama-impl compileSdk 34 -> 36
  * ai-assistant + ai-core wrapper -all.zip -> -bin.zip
  * ai-core catalog agp "8.13.2" -> "8.11.0" (contradicted settings.gradle.kts)

* ADFA-4907 | Simplify check-toolchain.sh

Collapses the nine copy-pasted "extract, compare, report" blocks into two
helpers (check_value for first-match keys, check_every for all-match keys),
plus small file_matches/reject/module_of helpers.

That duplication had already caused a real defect: four of the nine copies
omitted the --list echo, so --list silently under-reported the
compose-compiler pin, the wrapper bin/all flavor, and both catalog entries.
Recording the value is now part of the helper, so it cannot be forgotten.
--list goes from 5 fact types / 134 lines to 9 / 164, sorted by module
instead of grouped by internal scan order.

Also:
  * One matching idiom. The header claimed grep|sed was avoided for BSD sed
    portability, then the kotlin-stdlib check used exactly that. Everything
    is bash =~ now, so the claim is true and no subprocesses are spawned.
  * One find traversal with a case dispatch instead of four full walks of
    the 12.8k-entry tree. Runtime 1.02s -> 0.38s.
  * Documents why discovery is deliberately broader than plugin_dirs() in
    .githooks/pre-push, so a future "DRY win" does not silently drop
    ai-core/llama-api and llama-impl from coverage.
  * Narrows the --list usage text, which overstated what it prints.
  * Workflow header no longer restates the version numbers (third copy).

Deliberately NOT changed: no declarative rules table (four of the checks
have genuinely different shapes and a table would gut the failure-message
hints), and the standard stays a set of literal constants rather than being
derived from the root wrapper -- deriving it would make a wrong root wrapper
undetectable.

* ADFA-4907 | Align dependency versions across the AI plugins

The toolchain check enforces compileSdk/AGP/Kotlin/Gradle/Java, but every
other library version is unchecked, and the AI plugins had drifted as a
group -- all four kotlinx-coroutines 1.7.3 pins in the repo were AI plugins,
against a repo majority of 1.8.1.

Library alignments (all upgrades to the repo majority):
  * kotlinx-coroutines 1.7.3 -> 1.8.1 in ai-core, code-suggestions-plugin,
    speech-to-text-plugin, vector-search-plugin; -test 1.7.3 -> 1.8.1 in
    ai-assistant; get-ai-models catalog 1.6.4 -> 1.8.1 (was the oldest pin
    in the repo)
  * androidx.lifecycle 2.6.2 -> 2.8.7 in code-suggestions-plugin,
    speech-to-text-plugin and the get-ai-models catalog, matching
    client-time-tracker / compose-preview / sketch-to-ui
  * ai-assistant fragment-ktx 1.6.2 -> 1.8.8. This was the only 1.6.2 in the
    repo against 17 modules on 1.8.8, and the comment directly above it
    claimed the block matched random-xkcd, sketch-to-ui-plugin and Beepy --
    all three of which are on 1.8.8. The comment is now true.
  * ai-assistant org.json 20231013 -> 20240303

Deliberately NOT changed: coordinates where the AI plugins are merely AHEAD
of the majority (get-ai-models material 1.12.0, ai-literacy webkit 1.11.0,
llama-impl slf4j 2.0.12). Being ahead is not a defect, and downgrading
carries real risk for no gain.

ai-core/gradle/libs.versions.toml: pruned 334 lines -> 40.

It was copied wholesale from the CodeOnTheGo app repo and declared 281
entries of which exactly 4 were ever referenced (98.6% dead). The dead bulk
had drifted into self-contradiction -- androidx.core:core-ktx declared at
three different versions, appcompat at two, gson at two, plus a malformed
ksp = "2.3.6" and typo'd keys ("anroidx"). That is not inert: it is the
number the next person greps for.

While pruning, the two surviving aliases that encoded their version in the
NAME (androidx-core-ktx-v1120, androidx-appcompat-v171) were renamed to
version-neutral names, since such a name lies the moment the version moves.
Call sites in llama-impl updated to match.

The catalog also resolved two real conflicts inside a single Gradle build:
appcompat was 1.7.1 there while ai-core/build.gradle.kts pinned 1.6.1, and
llama-api pulled coroutines-core 1.10.2 while ai-core pulled
coroutines-android 1.8.1 -- the coroutines artifacts are released as a set
and must not be split.

Verified: all six changed AI plugins build assemblePluginDebug clean and
emit a .cgp. Resolved classpaths confirm coroutines is now uniformly 1.8.1
in ai-core (previously split), lifecycle 2.8.7, fragment-ktx 1.8.8. The
renamed catalog aliases were verified by forcing llama-api/llama-impl into
the build, with a negative control proving an unknown alias fails loudly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants