Files
intellijai/AGENTS.md
hgranthorner cb5a1d9555 initial commit
2026-08-04 10:12:15 -04:00

5.7 KiB
Raw Blame History

AGENTS.md — Jai IntelliJ Plugin

Orientation for agents working on this repo. Read this first.

Goal: an IntelliJ plugin for the Jai programming language.

Hard rule: do not research Jai online — public material is outdated and wrong. The only source of truth is the local install at ~/.local/jai (how_to/, modules/, examples/). Researching IntelliJ plugin development online is fine and encouraged.


Run anything with ./jaigradle

./jaigradle test     # unit tests
./jaigradle check    # full verification
./jaigradle tasks    # discover tasks

Never call ./gradlew directly. There is no JDK on PATH and JAVA_HOME is unset in a fresh shell. ./jaigradle resolves the JDK via mise, then delegates. It works from an environment with no Java at all.

Do not try to fix this with org.gradle.java.home in gradle.properties — already tried, it does not work, because gradlew is a shell script that needs a JVM to launch itself before it ever reads that property.

Java must be 21+. The IntelliJ Platform test-framework jars are Java 21 bytecode (class major version 65); on JDK 17 every test fails with UnsupportedClassVersionError. mise.toml pins java = "temurin-21".

Note that ./jaigradle compileKotlin passes on JDK 17 — the template sources never touch the test framework. Compiling is not evidence the toolchain is correct. Run tests.


Verify with the JUnit XML, not the exit code

A green test task does not prove tests ran. Always confirm:

cat build/test-results/test/*.xml | head -3

Look for tests="N" with failures="0" errors="0" and N > 0.

Last verified state (all green):

TEST-dev.hgh.HarnessSmokeTest.xml -> tests=2 skipped=0 failures=0 errors=0

Current state

Done

  • docs/JAI_LANGUAGE_REFERENCE.md — language reference derived from ~/.local/jai, keywords/tokens transcribed from the compiler's own lexer. Read this before writing any lexer or parser code.
  • docs/BUILD_PLAN.md — phased plan, testing strategy, risks, open questions.
  • mise.tomljava = "temurin-21" (was 17, which was broken).
  • jaigradle — JDK-resolving Gradle wrapper. Executable, verified.
  • src/test/kotlin/dev/hgh/HarnessSmokeTest.kt — proves the platform boots headlessly. If this fails, no other test can be trusted.

Not done — pick up here

  1. git init. Still not a repository. Do this first; there is no rollback point right now. Add .kotlin to .gitignore (currently untracked and unignored).
  2. Strip the template. src/main/kotlin/MyToolWindowFactory.kt, MyMessageBundle.kt, messages/MyMessageBundle.properties, and the toolWindow registration + resource-bundle in plugin.xml are all JetBrains scaffold and must go. plugin.xml metadata (name, vendor, description) is still placeholder text.
  3. Phase 1: lexer — see the plan.

Open questions for the user (unanswered)

Scope (Phases 04 vs 07), compiler integration, target IDEs, JDK policy. See docs/BUILD_PLAN.md §6. Scope is the one that most affects the work.


Architecture decisions already made

Both are justified in docs/BUILD_PLAN.md §2. Do not silently reverse them.

  • Hand-written lexer, not JFlex. Jai here-strings (#string DONE … DONE) have a runtime-captured terminator, which a DFA generator cannot express. Nested block comments need a depth counter. Both would end up as hand-written Java inside JFlex actions anyway.
  • Grammar-Kit BNF for parser + PSI. Verified headless: the plugin ID is org.jetbrains.intellij.platform.grammarkit, bundled in the IntelliJ Platform Gradle Plugin since 2.12.0 (we run 2.18.1). generateLexer / generateParser already appear in ./jaigradle tasks --all — no extra dependency needed.
  • Ship highlighting before the parser. Highlighting needs only the lexer.

The test corpus is the main asset

~/.local/jai holds 714 .jai files, 16.4 MB of real code by the language authors. Use it as the primary correctness gate instead of hand-written samples.

The highest-value assertion, per the plan: lex every file and check the concatenation of all token texts reproduces the source byte for byte. An IntelliJ lexer must tile its input with no gaps or overlaps, so this single invariant over 16 MB catches nearly every lexer bug — with no fixture boot and no human review. Also assert no BAD_CHARACTER and that every advance() strictly increases the offset (catches infinite loops, which hang the IDE rather than failing visibly).

Same idea later: zero PsiErrorElement across the corpus for the parser; formatting is idempotent for the formatter.


Environment gotchas

  • Stale Gradle daemons. Mixing JDK 17 and 21 daemons caused Timeout waiting to lock journal cache. Fix: ./jaigradle --stop.
  • timeout does not exist on this macOS box. Do not use it in scripts.
  • Noisy test stderr. Fixture runs log Vue/JS PluginExceptions from bundled plugins. Tests pass regardless, but real failures can be buried — check the XML. Narrowing the test fixture later would help.
  • runIde is never required. Everything is verifiable headlessly; that is a hard project requirement, not a preference. Do not ask the user to open an IDE to check your work.

Working agreement

  • Verify claims by running commands; prefer empirical checks over docs. Several plan decisions came from testing assumptions that turned out false.
  • Keep the working tree clean — revert throwaway probes.
  • Generated sources (once Grammar-Kit is wired up) go in src/main/gen, are committed, and are never hand-edited; regenerate instead.
  • Update this file when project state changes.