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
183 changes: 183 additions & 0 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<!--
Sync Impact Report
==================
Version change: N/A → 1.0.0 (initial ratification)
Modified principles: N/A (first version)
Added sections:
- Core Principles (8 principles)
- Tech Stack Constraints
- Development Workflow
- Governance
Removed sections: N/A
Templates requiring updates:
- plan-template.md: ✅ Compatible (Constitution Check section exists)
- spec-template.md: ✅ Compatible (no constitution-specific references)
- tasks-template.md: ✅ Compatible (no constitution-specific references)
Follow-up TODOs: None
-->

# Nadle IntelliJ Plugin Constitution

## Core Principles

### I. Platform-First

Every feature MUST leverage IntelliJ Platform APIs and extension points
rather than custom implementations. The platform provides built-in
behavior for filtering, sorting, navigation, and UI — use it.

- MUST use `PsiStructureViewFactory`, `RunLineMarkerContributor`,
`LazyRunConfigurationProducer`, and other standard extension points
- MUST NOT reimplement functionality the platform already provides
(e.g., type-to-filter in File Structure, `Sorter.ALPHA_SORTER`)
- SHOULD prefer `TextFieldWithAutoCompletion` over raw `JTextField`
when suggestions are available

**Rationale**: Platform integration gives free upgrades, consistent UX,
and avoids maintenance burden of custom UI/behavior.

### II. Guard Pattern

Every extension point registration MUST guard on
`NadleFileUtil.isNadleConfigFile()` to ensure the plugin only activates
for nadle config files and never interferes with standard JS/TS editing.

- MUST return `null` / `false` / skip processing for non-nadle files
- MUST NOT modify behavior of regular JavaScript or TypeScript files
- Guard checks MUST happen as early as possible in the call chain

**Rationale**: The plugin registers for JavaScript and TypeScript
languages globally. Without guards, every JS/TS file would be affected.

### III. Compilation Gate

`./gradlew compileKotlin` MUST pass before any commit or pull request.
No exceptions.

- MUST run compilation check after every code change
- MUST NOT commit code that fails compilation
- CI build status MUST be green before merging

**Rationale**: The IntelliJ Platform SDK has complex type hierarchies.
Compilation is the first and cheapest line of defense.

### IV. Regex-Based Task Discovery

Task names MUST be discovered using `NadleFileUtil.TASK_REGISTER_PATTERN`
regex on file text content. No full AST parsing.

- MUST use `findAll()` for multi-match scenarios, `find()` only for
single-match extraction
- MUST handle multiline formatting (`tasks\s*\.register`)
- MUST reuse the shared pattern from `NadleFileUtil` — no duplicating
regex patterns across files
- PSI tree walking for task identification MUST stop when a parent
contains multiple `tasks.register()` calls

**Rationale**: Regex on text is language-agnostic (works for JS, TS,
CJS, MJS, CTS, MTS), fast, and proven reliable. Full AST parsing
would require language-specific handling.

### V. Explicit Imports

All Kotlin imports MUST be explicit. Wildcard imports (`import foo.*`)
are prohibited.

- MUST expand wildcard imports to individual class imports
- MUST remove unused imports

**Rationale**: Explicit imports make dependencies visible at a glance
and prevent accidental name collisions.

### VI. Package Organization

Source code MUST be organized into logical subpackages under
`com.github.nadlejs.intellij.plugin`:

- `run/` — Run configurations, execution, gutter markers, task scanning
- `lsp/` — Language Server Protocol integration
- `structure/` — File Structure popup (Cmd+F12)
- `navigation/` — Go-to-definition, references
- `util/` — Shared utilities (file detection, icons, Node.js resolver)

New classes MUST be placed in the appropriate subpackage. New
subpackages may be created when a clear domain boundary emerges
(3+ related classes).

**Rationale**: Flat package with 19+ files is hard to navigate.
Subpackages group related functionality and make the codebase
approachable for new contributors.

### VII. Manual Verification

Every feature MUST be manually verified with `./gradlew runIde` before
merging. Automated compilation alone is insufficient — runtime behavior
in the IDE must be confirmed.

- MUST verify the primary user journey works end-to-end
- MUST verify non-nadle JS/TS files are unaffected
- MUST verify navigation, gutter icons, and popups function correctly

**Rationale**: IntelliJ plugin behavior depends on runtime extension
point resolution, PSI tree structure, and UI interactions that cannot
be validated by compilation alone.

### VIII. Simplicity

Start with the simplest approach that works. Avoid abstractions,
indirection, and configurability that aren't needed today.

- MUST NOT add abstraction layers for single implementations
- MUST NOT add configuration for behaviors with only one valid option
- SHOULD prefer 3 similar lines over a premature helper function
- MUST NOT add error handling for scenarios that cannot occur

**Rationale**: Plugin code is read far more than written. Every
abstraction is a cognitive tax on the next person reading the code.

## Tech Stack Constraints

- **Language**: Kotlin 2.1.20 targeting JVM 21
- **Platform**: IntelliJ Platform SDK 2024.2.5 (build range 242–253.*)
- **Build**: Gradle 8.13 with IntelliJ Platform Gradle Plugin 2.5.0
- **Plugin dependency**: JavaScript plugin (for JS/TS language support)
- **Runtime dependency**: Node.js (resolved via `NodeJsResolver`)
- **Execution**: `npx nadle <taskName>` for run, direct `node` for debug
- **Supported file extensions**: `nadle.config.[cm]?[jt]s`

Adding new dependencies MUST be justified and approved. The plugin
MUST remain lightweight with minimal external dependencies.

## Development Workflow

- **Branching**: Never commit directly to `main`. Use feature branches
with descriptive names (e.g., `001-file-structure-tasks`,
`chore/repo-polish`).
- **Spec-driven features**: New features SHOULD follow the speckit
workflow: `/speckit.specify` → `/speckit.plan` → `/speckit.tasks` →
`/speckit.implement`.
- **Commit discipline**: Commits MUST be atomic and focused. Run
`./gradlew compileKotlin` before every commit.
- **Pull requests**: PRs MUST include a summary of changes and a test
plan. Squash-merge is the default merge strategy.
- **plugin.xml**: All extension point class references MUST use
fully-qualified names matching the actual package structure. Update
plugin.xml whenever classes are moved or renamed.

## Governance

This constitution defines the non-negotiable engineering principles
for the Nadle IntelliJ Plugin. All code changes, reviews, and
architectural decisions MUST comply with these principles.

- **Amendments**: Any principle change MUST be documented with a version
bump, rationale, and migration plan if existing code is affected.
- **Versioning**: Constitution follows semantic versioning. MAJOR for
principle removals/redefinitions, MINOR for new principles or material
expansions, PATCH for clarifications and wording fixes.
- **Compliance review**: PRs SHOULD be checked against the Guard Pattern
(Principle II) and Compilation Gate (Principle III) at minimum.
- **Runtime guidance**: See `CLAUDE.md` for day-to-day development
reference (commands, project structure, key patterns).

**Version**: 1.0.0 | **Ratified**: 2026-02-21 | **Last Amended**: 2026-02-21
43 changes: 25 additions & 18 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
# intellij-plugin Development Guidelines
# Nadle IntelliJ Plugin

Auto-generated from all feature plans. Last updated: 2026-02-21

## Active Technologies
- Kotlin 2.1.20, JVM 21 + IntelliJ Platform SDK 2024.2.5, JavaScript plugin (001-file-structure-tasks)

- Kotlin 2.1.20, JVM 21 + IntelliJ Platform SDK 2024.2.5, IntelliJ Platform Gradle Plugin 2.5.0, JavaScript plugin (001-intellij-lsp-integration)
## Tech Stack
- Kotlin 2.1.20, JVM 21
- IntelliJ Platform SDK 2024.2.5
- IntelliJ Platform Gradle Plugin 2.5.0
- JavaScript plugin dependency

## Project Structure

```text
src/
tests/
src/main/kotlin/com/github/nadlejs/intellij/plugin/
run/ # Run configurations, execution, gutter markers
lsp/ # Language Server Protocol integration
structure/ # File Structure popup (Cmd+F12)
util/ # Shared utilities (file detection, icons, Node.js resolver)
src/main/resources/
META-INF/plugin.xml # Extension point registrations
messages/MyBundle.properties
icons/nadle.svg
```

## Commands

# Add commands for Kotlin 2.1.20, JVM 21

## Code Style

Kotlin 2.1.20, JVM 21: Follow standard conventions

## Recent Changes
- 001-file-structure-tasks: Added Kotlin 2.1.20, JVM 21 + IntelliJ Platform SDK 2024.2.5, JavaScript plugin
```bash
./gradlew compileKotlin # Check compilation
./gradlew build # Full build
./gradlew runIde # Launch sandboxed IDE
```

- 001-intellij-lsp-integration: Added Kotlin 2.1.20, JVM 21 + IntelliJ Platform SDK 2024.2.5, IntelliJ Platform Gradle Plugin 2.5.0, JavaScript plugin
## Key Patterns
- `NadleFileUtil.isNadleConfigFile()` guards all extension points to nadle config files only
- `NadleFileUtil.TASK_REGISTER_PATTERN` regex extracts task names from `tasks.register()` calls
- `NodeJsResolver` resolves Node.js binary across macOS GUI, shell PATH, and well-known paths
- Run configurations use `npx nadle <taskName>` for execution, direct node for debug

<!-- MANUAL ADDITIONS START -->
<!-- MANUAL ADDITIONS END -->
62 changes: 31 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
# intellij-plugin
# Nadle Task Runner for IntelliJ

![Build](https://github.com/nadlejs/intellij-plugin/workflows/Build/badge.svg)
[![Version](https://img.shields.io/jetbrains/plugin/v/MARKETPLACE_ID.svg)](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID)
[![Downloads](https://img.shields.io/jetbrains/plugin/d/MARKETPLACE_ID.svg)](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID)

## Template ToDo list
- [x] Create a new [IntelliJ Platform Plugin Template][template] project.
- [ ] Get familiar with the [template documentation][template].
- [ ] Adjust the [pluginGroup](./gradle.properties) and [pluginName](./gradle.properties), as well as the [id](./src/main/resources/META-INF/plugin.xml) and [sources package](./src/main/kotlin).
- [ ] Adjust the plugin description in `README` (see [Tips][docs:plugin-description])
- [ ] Review the [Legal Agreements](https://plugins.jetbrains.com/docs/marketplace/legal-agreements.html?from=IJPluginTemplate).
- [ ] [Publish a plugin manually](https://plugins.jetbrains.com/docs/intellij/publishing-plugin.html?from=IJPluginTemplate) for the first time.
- [ ] Set the `MARKETPLACE_ID` in the above README badges. You can obtain it once the plugin is published to JetBrains Marketplace.
- [ ] Set the [Plugin Signing](https://plugins.jetbrains.com/docs/intellij/plugin-signing.html?from=IJPluginTemplate) related [secrets](https://github.com/JetBrains/intellij-platform-plugin-template#environment-variables).
- [ ] Set the [Deployment Token](https://plugins.jetbrains.com/docs/marketplace/plugin-upload.html?from=IJPluginTemplate).
- [ ] Click the <kbd>Watch</kbd> button on the top of the [IntelliJ Platform Plugin Template][template] to be notified about releases containing new features and fixes.

<!-- Plugin description -->
Integrates [Nadle](https://github.com/nadlejs/nadle) task execution into IntelliJ IDEA.
Expand All @@ -23,32 +9,46 @@ Integrates [Nadle](https://github.com/nadlejs/nadle) task execution into Intelli
- Run icons next to `tasks.register("name", fn)` definitions for one-click execution
- Language intelligence (diagnostics, completions, hover, go-to-definition) for `nadle.config.*` files via the bundled Nadle Language Server
- Navigate from `dependsOn` references to task definitions
- File Structure popup (Cmd+F12) lists all registered tasks with filtering
- Task name autocomplete in run configuration editor with monorepo support
- Debug support with Node.js inspector
- Works with `nadle.config.ts`, `nadle.config.js`, and other supported extensions
<!-- Plugin description end -->

## Installation

- Using the IDE built-in plugin system:

<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > <kbd>Search for "intellij-plugin"</kbd> >
<kbd>Install</kbd>

- Using JetBrains Marketplace:
### From JetBrains Marketplace

Go to [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID) and install it by clicking the <kbd>Install to ...</kbd> button in case your IDE is running.
<kbd>Settings</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > Search for **"Nadle"** > <kbd>Install</kbd>

You can also download the [latest release](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID/versions) from JetBrains Marketplace and install it manually using
<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>⚙️</kbd> > <kbd>Install plugin from disk...</kbd>
### From Disk

- Manually:
Download the [latest release](https://github.com/nadlejs/intellij-plugin/releases/latest) and install manually:

Download the [latest release](https://github.com/nadlejs/intellij-plugin/releases/latest) and install it manually using
<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>⚙️</kbd> > <kbd>Install plugin from disk...</kbd>
<kbd>Settings</kbd> > <kbd>Plugins</kbd> > <kbd>&#9881;</kbd> > <kbd>Install plugin from disk...</kbd>

## Usage

---
Plugin based on the [IntelliJ Platform Plugin Template][template].
1. Open a project containing `nadle.config.ts` (or `.js`, `.mjs`, `.cjs`, `.mts`, `.cts`)
2. Click the run icon in the gutter next to any `tasks.register()` call
3. Press <kbd>Cmd+F12</kbd> to browse all tasks in the File Structure popup
4. Use <kbd>Ctrl+Space</kbd> in the run configuration editor for task name autocomplete

[template]: https://github.com/JetBrains/intellij-platform-plugin-template
[docs:plugin-description]: https://plugins.jetbrains.com/docs/intellij/plugin-user-experience.html#plugin-description-and-presentation
## Requirements

- IntelliJ IDEA 2024.2+
- Node.js (for task execution and language server)
- [Nadle](https://github.com/nadlejs/nadle) installed as a project dependency

## Development

```bash
# Build the plugin
./gradlew build

# Run a sandboxed IDE instance with the plugin
./gradlew runIde

# Check compilation
./gradlew compileKotlin
```
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.nadlejs.intellij.plugin
package com.github.nadlejs.intellij.plugin.lsp

import com.github.nadlejs.intellij.plugin.util.NadleFileUtil
import com.github.nadlejs.intellij.plugin.util.NodeJsResolver
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
Expand All @@ -12,11 +14,11 @@
project: Project
) : ProjectWideLspServerDescriptor(project, "Nadle Language Server") {

override fun isSupportedFile(file: VirtualFile): Boolean =

Check warning on line 17 in src/main/kotlin/com/github/nadlejs/intellij/plugin/lsp/NadleLspServerDescriptor.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

Overridden method 'isSupportedFile(com.intellij.openapi.vfs.VirtualFile)' is declared in unstable 'com.intellij.platform.lsp.api.LspServerDescriptor' marked with @ApiStatus.Experimental
NadleFileUtil.isNadleConfigFile(file)

override fun createCommandLine(): GeneralCommandLine {

Check warning on line 20 in src/main/kotlin/com/github/nadlejs/intellij/plugin/lsp/NadleLspServerDescriptor.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

Overridden method 'createCommandLine()' is declared in unstable 'com.intellij.platform.lsp.api.LspServerDescriptor' marked with @ApiStatus.Experimental
val nodePath = NodeJsResolver.resolve(project)

Check warning on line 21 in src/main/kotlin/com/github/nadlejs/intellij/plugin/lsp/NadleLspServerDescriptor.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'project' is declared in unstable 'com.intellij.platform.lsp.api.LspServerDescriptor' marked with @ApiStatus.Experimental
?: throw IllegalStateException(
"Node.js is required for Nadle language intelligence features. " +
"Please configure Node.js in Settings > Languages & Frameworks > Node.js, " +
Expand All @@ -34,7 +36,7 @@
}

private fun resolveServerPath(): String? {
val basePath = project.basePath

Check warning on line 39 in src/main/kotlin/com/github/nadlejs/intellij/plugin/lsp/NadleLspServerDescriptor.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'project' is declared in unstable 'com.intellij.platform.lsp.api.LspServerDescriptor' marked with @ApiStatus.Experimental

if (basePath != null) {
val projectCandidates = listOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.nadlejs.intellij.plugin
package com.github.nadlejs.intellij.plugin.lsp

import com.intellij.openapi.application.PathManager
import com.intellij.openapi.diagnostic.Logger
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
package com.github.nadlejs.intellij.plugin
package com.github.nadlejs.intellij.plugin.lsp

import com.github.nadlejs.intellij.plugin.util.NadleFileUtil
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.lsp.api.LspServerSupportProvider

class NadleLspServerSupportProvider : LspServerSupportProvider {

Check warning on line 9 in src/main/kotlin/com/github/nadlejs/intellij/plugin/lsp/NadleLspServerSupportProvider.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'com.intellij.platform.lsp.api.LspServerSupportProvider' is marked unstable with @ApiStatus.Experimental

override fun fileOpened(

Check warning on line 11 in src/main/kotlin/com/github/nadlejs/intellij/plugin/lsp/NadleLspServerSupportProvider.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

Overridden method 'fileOpened(com.intellij.openapi.project.Project, com.intellij.openapi.vfs.VirtualFile, com.intellij.platform.lsp.api.LspServerSupportProvider.LspServerStarter)' is declared in unstable 'com.intellij.platform.lsp.api.LspServerSupportProvider' marked with @ApiStatus.Experimental
project: Project,
file: VirtualFile,
serverStarter: LspServerSupportProvider.LspServerStarter

Check warning on line 14 in src/main/kotlin/com/github/nadlejs/intellij/plugin/lsp/NadleLspServerSupportProvider.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'com.intellij.platform.lsp.api.LspServerSupportProvider.LspServerStarter' is declared in unstable 'com.intellij.platform.lsp.api.LspServerSupportProvider' marked with @ApiStatus.Experimental

Check warning on line 14 in src/main/kotlin/com/github/nadlejs/intellij/plugin/lsp/NadleLspServerSupportProvider.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'com.intellij.platform.lsp.api.LspServerSupportProvider' is marked unstable with @ApiStatus.Experimental
) {
if (!NadleFileUtil.isNadleConfigFile(file)) {
return
}

try {
serverStarter.ensureServerStarted(

Check warning on line 21 in src/main/kotlin/com/github/nadlejs/intellij/plugin/lsp/NadleLspServerSupportProvider.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'ensureServerStarted(com.intellij.platform.lsp.api.LspServerDescriptor)' is declared in unstable 'com.intellij.platform.lsp.api.LspServerSupportProvider' marked with @ApiStatus.Experimental
NadleLspServerDescriptor(project)
)
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.github.nadlejs.intellij.plugin
package com.github.nadlejs.intellij.plugin.navigation

import com.github.nadlejs.intellij.plugin.util.NadleFileUtil
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil

class NadleTaskGotoDeclarationHandler : GotoDeclarationHandler {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.nadlejs.intellij.plugin
package com.github.nadlejs.intellij.plugin.run

import com.github.nadlejs.intellij.plugin.util.NadleIcons
import com.intellij.ui.TextFieldWithAutoCompletionListProvider
import javax.swing.Icon

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.nadlejs.intellij.plugin
package com.github.nadlejs.intellij.plugin.run

import com.github.nadlejs.intellij.plugin.util.NadleFileUtil
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.LazyRunConfigurationProducer
import com.intellij.execution.configurations.ConfigurationFactory
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.nadlejs.intellij.plugin
package com.github.nadlejs.intellij.plugin.run

import com.github.nadlejs.intellij.plugin.util.NadleIcons
import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.execution.configurations.ConfigurationType
import com.intellij.execution.configurations.RunConfiguration
Expand Down
Loading
Loading