Plugins

AI Projects

Lists AI projects - their harness, agents, status and last-opened time - and opens a project's persistent agent desktop.

Version 1.0.21 Official plugin Released 174 downloads

Install it from Commander: Plugins → Manage, or drop the signed bundle into the plugins/ directory.

What this bundle provides

One download, 3 plugins.

AI Project Desktop

dev.nuclr.plugin.core.ai.projects.screen

The persistent agent desktop for one AI project: an internal frame per agent, a strip of window chips, and the project's conversation history.

Fullscreen Apache-2.0 Editor

AI Project Summary

dev.nuclr.plugin.core.ai.projects.quickview

Ctrl+Q on a project row: its harness, model, agents and last session, without opening it.

QuickView Apache-2.0

AI Projects

dev.nuclr.plugin.core.ai.projects.panel

Lists AI projects - their harness, agents, status and last-opened time - and opens a project's persistent agent desktop.

FilePanel Apache-2.0

About

Nuclr Commander — AI Projects

An AI project is a persistent desktop for coding agents. This plugin adds a file panel that lists your projects, a quick view that summarises one, and a fullscreen screen that is one project's desktop — a JDesktopPane with an internal frame per agent, a strip of window chips beneath it, and an explicit view of what every agent is actually run with and told.

Conversation windows show the latest reported context size beside cost in the title, for example $1.25 - Context 48k / 200k (24%). Claude Code, Codex, Pi and ACP agents supply the usage; the limit and percentage appear only when the agent reports a context window size. This is the latest context reading, including cached tokens, not cumulative session token spend. The reading is saved with the conversation, restored on reopening, and cleared when starting a new conversation. Agents that do not report usage leave it hidden.

The abstraction it is built around:

AI Project → Project Harness → Agent Definitions → Agent Sessions → Agent Windows

The plugin entries

EntryTypeWhat it is
…ai.projects.panelFilePanelLists projects: name, status, agents, harness, model, storage, last opened, root. Alt+F1 → AI Projects.
…ai.projects.quickviewQuickViewCtrl+Q on a project row: its harness, model, agents and last session, without opening it.
…ai.projects.screenFullscreen (Editor)One project's desktop. Opened by pressing Enter on a project row.

A project row is deliberately not a folder and carries no local path, so Enter opens the desktop rather than navigating into the directory.

Welcome

On a fresh install Commander opens AI Projects in the left pane, and the empty list greets the user once with the Welcome dialog. It has two choices:

  • Continue where you left off: the conversations Claude Code, Codex, Pi and OpenCode kept on this machine, grouped by folder (the enclosing Git checkout, never the home folder). The newest three are ticked. Each one opens in a window that resumes it.
  • Start a new conversation: pick an installed agent and a folder. An agent that isn't installed shows the command that installs it, how to sign in, and Check again.

Either way the project is created private to Commander, so nothing is written into the user's folder, and it opens straight away. Just browse files goes to the home folder instead. Pressing Enter on the empty list brings the dialog back.

The project desktop's File Panels toolbar button opens Commander's file panels in a new workspace. The project keeps running in its own workspace.

Getting a project into the list

  • F7 — New project, choosing a root folder and where its metadata lives.
  • F5 from the other panel — copy a folder onto the AI Projects panel and it becomes a project. Commander is a file manager; being sent to a file chooser to find a folder the other pane is already showing would be the wrong gesture. A folder that already carries a definition is adopted rather than re-created.
  • Add existing project… — for a folder that is not in either pane.

One folder can carry several projects — say a backend and a frontend project over the same repository, each with its own agents, profiles and layout. The folder's first project-local project keeps its metadata in .nuclr/ai-project/; every further one gets .nuclr/ai-projects/<name>/. Copying a folder in (or adding it) adopts every definition there that is not in the list yet, and once all of them are listed it creates another one. Two projects in one folder cannot share a name. Commander-private projects are unaffected: they were always keyed by id.

Columns sort: Name, Status, Last opened, Agents and Harness are declared as sort descriptors, so clicking a header or pressing Ctrl+F3..F8 works the way it does in every other panel. An empty list shows one row saying so, and Enter on it creates a project.

Importing agent sessions

Sessions ▾ → Import agent sessions… on the toolbar lists the conversations Claude Code, Codex, Pi and OpenCode kept on this machine for the project's root and allowed roots, and turns each one you tick into a conversation window that resumes it:

  • The window's session record gets the CLI's own conversation id, so Start carries on with everything the agent knew.
  • The window runs in the folder the session ran in, since that is where each CLI looks a conversation up. Folders outside the project's roots are not offered.
  • The conversation so far is written into the window's transcript, so it opens showing it. Tool output is cut short, and a very long conversation keeps its end.
  • The new agent has no profile: it starts the CLI as configured on the machine, like the session did. Pick a profile afterwards with Edit agent.

Where each CLI keeps them: Claude Code in ~/.claude/projects (CLAUDE_CONFIG_DIR), Codex in ~/.codex/sessions (CODEX_HOME), Pi in ~/.pi/agent/sessions (PI_CODING_AGENT_SESSION_DIR), and OpenCode is asked with opencode session list and opencode export. None of these is a documented format, so a record that is not understood is skipped rather than failing the list. A conversation already in the project is not offered again. Claude Code saves a resumed conversation again under a new id, so its copies - known by the id of their first message - are listed once, as the newest; forks of a Codex or Pi session are listed with the session they came from. Ctrl+F searches the list. Carry on with an imported conversation in one place only: resumed from two, it splits in two.

Harness and context

The harness says how an agent is run: executable, startup arguments, provider, model, environment, permissions, MCP servers, allowed roots and shared instructions. It is defined once on the project; a template may override parts of it, and an agent may override parts again.

  • null means inherit. An empty list or blank string means explicitly nothing — an agent can be given no MCP servers at all, which is not the same as failing to mention any.
  • Environment merges by key and MCP servers merge by name, so an agent can change one variable or swap one server without restating the rest. Every other list replaces wholesale.
  • Harness… shows the merged result with a From column naming the level that set each field, and edits it. The editor serves both levels: on the project it simply sets fields, on an agent every field carries an Inherit box, because the inherit / explicitly-empty distinction cannot be expressed by leaving a text box blank.

Changing a harness or a context tells you plainly when agents are already running: they were launched with the previous configuration and only a restart picks the change up.

The terminal provider applies the command, startup arguments, environment and approved working directory directly, and delivers the context as a briefing (see below). Provider-specific permissions, model selection and MCP wiring still require support from the selected CLI, and the launch notice names whatever was not applied.

The context says what an agent is told: instruction documents, skills, injected files and context variables. Unlike the harness it is additive — the project's context reaches every agent and each agent appends its own. That is what makes several specialised agents in one project worth having.

Resolved Context shows the effective project configuration after merging, including the environment variables the plugin itself sets at launch, with the contributing level and a mark on any file that is referenced but missing.

The briefing: how an agent is actually told

Resolving context only describes it. At every start the terminal provider builds a briefing from the same resolved context - the content of every instruction, skill and injected file, the context variables, and a list of anything referenced but missing - writes it to sessions/<agent>.briefing.md, and hands it to the CLI the way that CLI's own --help documents:

CLIMechanism
Claude Code--append-system-prompt <briefing>
Pi--append-system-prompt <briefing file>
Codexthe initial [PROMPT]: the briefing opens the session, and Codex replies "Ready"
OpenCodeOPENCODE_CONFIG_CONTENT naming the briefing as an instructions file; --prompt if you already set that variable

The CLI is recognised by the executable's name, not the window kind. Through a .cmd/.bat shim, where cmd.exe splits arguments at line breaks, or for a briefing too long for a Windows command line, only a one-line pointer to the file is passed. A shell or unknown command gets no briefing, and the launch notice says so. The transcript records how the briefing was delivered. A briefing that cannot be written stops the start rather than launching an agent without its instructions.

Allowed roots bound both halves

A project definition is a file, and a file can be hand-edited or arrive from a colleague, so a reference that climbs out of the project with .. is refused rather than followed. Allowed roots are the deliberate way to widen that, and they govern two things at once: where an agent may run, and what it may be pointed at. One instruction document shared across several checkouts works by declaring its directory in the harness.

Both questions go through a single containment check, so the launcher and the Resolved Context view cannot disagree about a path. Symlinks are resolved on both sides, so a link cannot be used to step outside and a project that itself lives under one is not wrongly refused.

A reference to a legal place that holds no file still resolves, and shows as missing. Dropping it would hide the most common configuration mistake there is.

Linking instructions and skills from elsewhere

Instruction and skill documents are worth sharing: a house style kept in one repository, a skill written once and used in every project. An absolute path (or ~/…) to a Markdown file in an instruction or skill list is a deliberate link, and resolves without declaring its folder as an allowed root — which would also let agents run there, a far wider grant than reading one document.

  • Link instruction… / Link skill… in the sidebar pick files and add them to the project context; the Context editor has a Link Markdown file… button on the same two tabs for agent- and template-level links.
  • Linked documents appear in the Instructions and Skills sections and are marked linked in Resolved Context. Right-click offers Unlink instead of Rename and Delete, because the file belongs to the project it came from.
  • The link is narrow on purpose, since a definition can arrive from someone else: Markdown only (.md, .markdown), absolute paths only — a .. climb is still refused — and a .md symlink whose real target is not Markdown is refused too. Injected files are not linkable this way; they still need an allowed root.

Agent window kinds

AgentWindowProvider is the extension point. "Terminal" is one implementation of it, not a built-in assumption: a log viewer, a task board, an embedded browser or a diff/review agent are the same shape — a stable kind() recorded in project.json, a display name, and a factory.

Shipped kinds: terminal.codex, terminal.claude-code, terminal.pi, terminal.opencode, terminal.shell. An agent naming a kind nothing provides still gets a frame, which says what is missing; its definition is left untouched.

Register another with AgentWindowRegistry.register(...).

Chat attachments

A chat window's message can carry more than words. What is pasted, dropped or picked with the attach button becomes a chip above the box, and each kind reaches the agent differently:

AttachmentHow it gets thereChip
PictureMade fit to send - scaled to a 2000-pixel edge, PNG or JPEG under the model's size limit - and sent as an imageThe picture, small
Long pasteKept as a file and sent as text in front of the message, marked off from what was typedA page thumbnail of the paste, lines and size
FileLeft where it is; the agent is given its path, in front of the message, and reads it with its own toolsA thumbnail of what it holds - first page, cover, model - with its type and size

A dropped file is attached only when one of Commander's Quick View plugins can show it; anything else, and any folder, has its path written into the box as a terminal agent is given it. Right-click a file's chip to put its path in the box instead, or a paste's to put the text back. A message carries at most 20 attachments; sent ones are shown in the conversation, where a click opens them.

Thumbnails and the "can a viewer show this?" question come from Commander over the event bus - quickview.thumbnail.request and quickview.supports.request, answered by its QuickViewThumbnailService with the Quick View plugins' SDK 6.0.0 thumbnail methods. With a Commander that does not answer, chips keep their glyph and a dropped file is named by its path after three seconds.

Just a terminal

A plain shell is one of the window kinds, so a terminal in the project folder is an agent like any other - it just does not need the form filled in:

  • Tools ▾ → Terminal in the project folder on the project toolbar,
  • Ctrl+O, the same key Commander uses for a console in a panel (its own handler is guarded on the file panels being visible, so inside this screen the key is free),
  • Shift+F2 on the function-key bar,
  • Tools ▾ → Terminal in for each of the project's allowed roots.

It starts immediately, is named Terminal (or Terminal - <folder>), and gets the platform shell as an explicit agent-level override so it does not inherit the project's agent CLI. Its working directory is stored relative to the project root when it is inside, because project.json is the committable half and an absolute path from one machine is no use in it.

Because it is an ordinary agent it persists with the desktop and can be renamed, duplicated or deleted like any other - a terminal that vanished on reopen would be the odd one out on a desktop whose whole point is that it does not.

A folder outside every allowed root would silently fall back to the project root at launch, so choosing one offers to add it to the allowed roots instead.

Incognito agents

New Incognito Agent on the toolbar (or Ctrl+Shift+I) opens a conversation that leaves nothing behind on this computer. It is chosen like any agent - like the agent in focus, from a profile, or from a CLI with its own settings - so it runs in the same folder with the same instructions, skills, model and tools. What differs is only what is kept:

  • It is never added to project.json, desktop.json, the session records or the History, so it is not there when the project reopens.
  • The CLI is told not to keep it either: Claude Code gets --no-session-persistence, Codex starts the thread with ephemeral: true, Pi runs with --no-session. OpenCode has no such switch, so it is not offered.
  • Its transcript, pictures, pastes and briefing live in a temporary folder (<tmp>/nuclr-incognito/<agent>/) that is deleted when the window closes. /clear and /new erase the conversation they end. A folder a crash left behind is deleted the next time a project opens; a lock file tells it from one another Commander still has open.
  • Stopping it ends the conversation for good: there is nothing to resume, so the next start is a new one, and the window says so.
  • It is left out of Start all and Broadcast, and its notifications say that it finished or asks for a decision, never what it said.

The window is marked in the theme's incognito colour - purple, or AiProjects.incognitoColor if set - with a border, a band saying what it is, and a count in the status bar.

What it cannot cover: the provider still receives the conversation under your account and keeps it as that account's data policy says, and files the agent changes in the project stay changed.

Templates

A project starts with Coder, Reviewer, Researcher and Architect. A template is a window kind plus harness overrides plus context, and its instruction document is written into the project as a file — templates and their instructions are the part a team may want to commit. New agent → Coder is one click on the toolbar.

Storage, and what belongs in git

Two layouts, chosen per project when it is created:

  • Project-local — <root>/.nuclr/ai-project/
  • Commander-private — ~/.nuclr/commander/ai-projects/<id>/, for a repository nothing should be written into.
.nuclr/ai-project/
├── project.json      committable — harness, agents, templates, shared context
├── instructions/     committable
├── skills/           committable
├── .gitignore        generated, and ignores the three below
├── desktop.json      runtime — window geometry, background
├── sessions/         runtime — pid, exit code, command line, per agent
└── transcripts/      runtime — terminal output, per agent

Persistence is continuous, not tied to a clean shutdown: changes are marked and a background writer coalesces them a few hundred milliseconds later, writing atomically through a temporary file. Killing Commander outright costs the last gesture, not the session.

Restoration, and being honest about it

Reopening a project restores the exact desktop: the same frames, in the same places, folded the same way. What it cannot restore is the processes — they were children of a JVM that has exited, and none of these CLIs can be reattached.

So they are not pretended into life. Every session record is stamped with the Commander run that wrote it; a record claiming RUNNING from an earlier run is corrected to stopped on sight, and the window says "Session stopped — the process did not survive the Commander restart" above the transcript of what that session printed, with a Start button. The transcript is the continuity.

Status and notifications

Each frame's title carries its status and a coloured dot: stopped, starting, running, waiting for input, finished, failed.

WAITING_INPUT is the one inferred value — no CLI reports that it is waiting, so it is derived from quiet output ending in something prompt-shaped. An idle prompt is not worth interrupting anyone for, so it only sets the status. A confirmation ("(y/n)", "Do you want…", "Allow…") also raises an attention flag on the frame, on its chip in the window strip, and on the project's row in the file panel.

A flag nobody is looking at is not a notification, so an agent asking for a decision also flashes Commander's taskbar entry and marks the window title until it is answered. The desktop's status bar carries the running total, and its tooltip is the colour legend — including the plain statement that "waiting" is a good guess rather than something the agent reported.

Agents are windows; conversations are the work

An agent is a window on a conversation, and it lasts exactly as long as its frame. Closing a frame forgets the agent: its definition, layout and session record go. The conversation stays in the project's History (F3, or Ctrl+Shift+H), with the window kind, profile, working directory and launch command it ran with, and opening it from there brings back a window that resumes it. Terminal windows are not conversations and are not kept: closing one is its end.

Commands

Per window — Start/Stop, Restart, Send instruction…, Duplicate, Folder, Context…, and behind More (or a right-click anywhere on the frame): Edit agent, Copy all output, Open transcript file, Clear screen, Delete conversation, text size, Close.

Per project — New agent, Start all, Stop all, Tile, Cascade, Save layout, Reset layout, Broadcast…, History…, Close project. On the toolbar, on the F-key bar and on the keyboard alike; all three route through the same methods.

Window strip — beneath the desktop, one chip per open window with its status, the logo of the CLI it runs and its name. Click a chip to bring its window forward (click it again to minimise it); right-click for the window's own menu. A JDesktopPane drops minimised frames into its bottom-left corner and offers no way back to one you cannot see; the strip, and the list under UI ▾ Layout, are the way back.

Keyboard, all on Ctrl+Shift so a terminal keeps what it needs: N new agent, W close window, G tile, D cascade, S save layout, B broadcast, M minimise all, H history, PageUp/PageDown previous/next window. Every one is also a button; the keyboard is an accelerator, never the only way in.

Documents — the skill and instruction editor has Ctrl+S, undo and redo, and a Reload. Before saving it checks whether the file changed on disk since it was opened, because agents in this project write files in it; an external change is put to the user rather than overwritten. Closing the project asks about unsaved edits instead of discarding them, which is what JInternalFrame.dispose() would otherwise do in silence.

Before anything irreversible

Stopping agents cannot be undone, so Stop all and Close project confirm first, saying how many are running and what survives. Close is on the plugin's own action (Ctrl+F4) rather than a host shortcut precisely so it can confirm — Commander's teardown cannot be vetoed once it starts. Bare F10 is deliberately left alone: that is Commander's own "quit the application".

Desktop backgrounds

The toolbar background picker offers None, Neon Network, Synthwave Grid and Arcade Starfield. The selected effect is saved with the project's desktop layout. Effects are isolated behind a paint-only background interface, so new visuals can be added without coupling them to agent windows or project logic.

Rainy Sanctuary is a procedural midnight shrine with clouded moonlight, misty mountain silhouettes, wet stone, maple branches and a torii reflected in moving water. Layered rain, refracting lens droplets, candle bloom and an animated incense density field provide motion. Static scenery is cached and the software water and smoke buffers have fixed resolutions to bound rendering work.

Relationship to workspaces

An AI project is a domain object that owns its own persistent desktop; a Commander workspace is general UI state. They stay separate. The panel keeps only which project row it was showing in its workspace state — the desktop belongs to the project and travels with it.

Icons

Agents, statuses, sections, commands and context kinds all carry a glyph: ✅ finished, 🟢 running, 🟡 waiting, 🔴 failed, 🤖 agents, 📄 instructions, 🎓 skills, 🔌 tools, ⚙️ harness, and so on.

Getting an emoji onto a Swing label is not as simple as typing one. Java2D draws a character the font lacks as an empty box rather than falling back, and the theme font Commander uses on Windows — Segoe UI — contains none of these. So Glyphs declares every glyph with a plainer stand-in and:

  • this plugin'''s own widgets get the emoji wrapped in an HTML span naming a font that can draw it, so the picture comes from the emoji font while the words keep the theme'''s;
  • anything handed to the host — file-panel column values, function-key labels — stays plain text and falls back to a symbol, because how the host draws its own widgets is the host'''s business.

Force one or the other with -Dnuclr.ai.glyphs=emoji or =symbols.

One detail worth knowing: the Status column shows a glyph, but the value the sort comparator reads does not. Sorting by status orders by the word rather than by whichever codepoint happens to sit in front of it.

Build

mvn clean package     # runs the tests, produces target/plugin-ai-projects-<version>.zip

Java 25 and Maven 3.9+. The SDK, Jackson, JediTerm and pty4j are all provided: the Commander host supplies them at runtime and none are bundled — a second copy of pty4j would mean a second extraction of its native helpers. JediTerm resolves from JetBrains' Maven repository, declared in the POM as the host declares it.

If a host is ever built without the terminal libraries, TerminalStack notices and the terminal kinds report themselves unavailable, rather than the plugin failing to load.

Licence

Apache-2.0. See LICENSE.

Credits

The agent CLI logos in src/main/resources/dev/nuclr/plugin/core/ai/projects/icons/agents/ are from Lobe Icons (MIT). Claude Code, Codex, OpenCode and Pi are trademarks of their respective owners; their logos are shown only to identify which CLI an agent runs.

Releases

Version Released Size Download
1.0.21 11.9 MB Download
1.0.20 11.7 MB Download
1.0.19 11.6 MB Download
1.0.18 11.5 MB Download
1.0.17 11.5 MB Download
1.0.16 7.5 MB Download
1.0.15 7.4 MB Download
1.0.14 1.7 MB Download
1.0.13 1.6 MB Download
1.0.12 1.5 MB Download
1.0.11 1.4 MB Download
1.0.10 1.3 MB Download
1.0.9 1.0 MB Download
1.0.8 0.9 MB Download
1.0.7 0.9 MB Download
1.0.6 0.8 MB Download

Details

Plugin ID
dev.nuclr.plugin.core.ai.projects.panel
Repository
nuclrdev/plugin-ai-projects
Platform SDK
5.0.0
SHA-256
05c92e617dc876f0550b301c57f39d6d510196440aca6cff516c147db54bddc2