@nanhara/hara 0.118.0 → 0.119.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to `@nanhara/hara`.
5
5
  > Versioning (pre-1.0, SemVer-style): the **minor** (middle) number bumps for a **new feature**; the
6
6
  > **patch** (last) number bumps for **optimizations/fixes of existing features**.
7
7
 
8
+ ## 0.119.0 — project panels: the chat ↔ live-preview split
9
+
10
+ - **Plugin panels become project-aware.** A plugin panel can declare `detect` markers (e.g.
11
+ `.hara/design`, `remotion.config.ts`); `project.panels` returns the panels applicable to a
12
+ project's cwd. In Hara Desktop, opening a design/video project surfaces a preview toggle right
13
+ on the conversation — talk to the agent on the left, watch the live preview react on the right.
14
+ Detect-less panels stay global-only (settings page).
15
+
8
16
  ## 0.118.0 — session delete & fork · slash skills over serve
9
17
 
10
18
  - **`session.delete`** — permanent removal (codex thread/delete; archive stays the soft path).
@@ -174,6 +174,23 @@ export function uninstallPlugin(name) {
174
174
  rmSync(dest, { recursive: true, force: true });
175
175
  return true;
176
176
  }
177
+ /** Panels applicable to a project cwd — a panel matches when any of its `detect` markers exists under
178
+ * the cwd. Pure over the given plugin list (testable); `panelsForProject` binds it to enabled plugins. */
179
+ export function matchPanels(plugins, cwd) {
180
+ const out = [];
181
+ for (const pl of plugins) {
182
+ for (const panel of pl.manifest.panels ?? []) {
183
+ if (!panel.detect?.length)
184
+ continue; // global-only panels stay off project surfaces
185
+ if (panel.detect.some((m) => existsSync(join(cwd, m))))
186
+ out.push({ plugin: pl.name, panel });
187
+ }
188
+ }
189
+ return out;
190
+ }
191
+ export function panelsForProject(cwd) {
192
+ return matchPanels(enabledPlugins(), cwd);
193
+ }
177
194
  /** Persist a plugin's enabled flag in ~/.hara/config.json (`plugins.enabled[name]`). */
178
195
  export function setPluginEnabled(name, on) {
179
196
  const p = join(homedir(), ".hara", "config.json");
@@ -20,7 +20,7 @@ import { fuzzyRank } from "../fuzzy.js";
20
20
  import { loadAgentsMd } from "../context/agents-md.js";
21
21
  import { expandMentions } from "../context/mentions.js";
22
22
  import { memoryDigest } from "../memory/store.js";
23
- import { listInstalled, enabledPlugins, setPluginEnabled } from "../plugins/plugins.js";
23
+ import { listInstalled, enabledPlugins, setPluginEnabled, panelsForProject } from "../plugins/plugins.js";
24
24
  import { loadSkillIndex, loadSkillBody } from "../skills/skills.js";
25
25
  import { loadJobs, saveJobs, addJob } from "../cron/store.js";
26
26
  import { parseSchedule, describeSchedule } from "../cron/schedule.js";
@@ -210,7 +210,7 @@ export async function startServe(opts, deps) {
210
210
  const methods = [
211
211
  "session.list", "session.create", "session.resume", "session.send", "session.interrupt", "session.set-model",
212
212
  "session.rename", "session.archive", "session.compact", "session.rewind", "session.context", "session.delete", "session.fork",
213
- "approval.reply", "plugins.list", "plugins.set", "skills.list", "models.list", "files.search",
213
+ "approval.reply", "plugins.list", "plugins.set", "skills.list", "models.list", "files.search", "project.panels",
214
214
  "automation.list", "automation.add", "automation.toggle", "automation.delete",
215
215
  ];
216
216
  return reply(rpcResult(id, { name: "hara", version: deps.version, protocol: PROTOCOL_VERSION, cwd: opts.cwd, provider: deps.providerId, model: deps.model, capabilities: { methods } }));
@@ -399,6 +399,14 @@ export async function startServe(opts, deps) {
399
399
  const cwd = typeof p.cwd === "string" && p.cwd ? p.cwd : opts.cwd;
400
400
  return reply(rpcResult(id, { skills: loadSkillIndex(cwd).map((s) => ({ id: s.id, description: s.description, source: s.source })) }));
401
401
  }
402
+ case "project.panels": {
403
+ // panels applicable to a project (plugin manifest `detect` markers under the cwd) — powers
404
+ // the desktop's chat ↔ live-preview split for design/video projects.
405
+ const ps = typeof p.sessionId === "string" ? hub.get(p.sessionId) : undefined;
406
+ const pcwd = typeof p.cwd === "string" && p.cwd ? p.cwd : (ps?.meta.cwd ?? opts.cwd);
407
+ const panels = panelsForProject(pcwd).map(({ plugin, panel }) => ({ plugin, id: panel.id, title: panel.title, command: panel.command, args: panel.args ?? [], port: panel.port }));
408
+ return reply(rpcResult(id, { cwd: pcwd, panels }));
409
+ }
402
410
  case "files.search": {
403
411
  // fuzzy file lookup for the composer's @-mention autocomplete (codex fuzzyFileSearch).
404
412
  // Relative POSIX paths; empty query returns the first files as a browse list.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanhara/hara",
3
- "version": "0.118.0",
3
+ "version": "0.119.0",
4
4
  "description": "hara — a coding agent CLI that runs like an engineering org.",
5
5
  "bin": {
6
6
  "hara": "dist/index.js"