diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f4c925..f758829 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,8 @@ full session, `esc` close). Transcripts are parsed from the agent session JSONL (user/assistant/tool-call/tool-result/thinking blocks) and refresh every 1.5s while an agent is streaming; the tree shows status glyphs, elapsed time, - and per-agent tasks. `/agent-open` keeps the previous session-switching flow - (`Subagent Actions → Open`), with `/back` returning to the main session. + and per-agent tasks. Select `o` to open the full session, with `/back` + returning to the main session. - New `/agent [filter]` command opens a subagent's full chat session in the TUI (OpenCode-style `Subagent Actions → Open`): a selector lists the run's agents diff --git a/README.md b/README.md index 962e70e..b3a3b08 100644 --- a/README.md +++ b/README.md @@ -93,8 +93,7 @@ background branches are controlled with `agent_control`. - `/team` — tree, tasks, and statuses; - `/agent [filter]` — OpenCode-style inspector: agent tree on the left, live chat transcript of the selected agent on the right (`↑↓` select, `enter` open chat, `t` toggle thinking, `o` open the full session, `esc` close); -- `/agent-open [filter]` — open a subagent's full chat as its own session; -- `/back` — return to the main session after `/agent-open`; +- `/back` — return to the main session after opening an agent chat; - `/research ` — start the explicit bounded loop; - `/research-status`, `/research-pause`, `/research-resume`, `/research-stop`; - `/findings`, `/hypotheses`. diff --git a/src/index.ts b/src/index.ts index a5da746..bd40a89 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,6 @@ export default function hypothesisMachine(pi: ExtensionAPI): void { pi.registerCommand("team", { description: "Show the recursive research team", handler: async (_args, ctx) => { ctx.ui.notify(supervisor.team(), "info"); } }); pi.registerCommand("agent", { description: "Open the OpenCode-style subagent chat inspector (tree + live transcripts)", handler: async (args, ctx) => { await supervisor.showAgentChatPanel(ctx, args.trim() || undefined); } }); - pi.registerCommand("agent-open", { description: "Open a subagent's full chat as a separate session (like OpenCode)", handler: async (args, ctx) => { await supervisor.showAgentChat(ctx, args.trim() || undefined); } }); pi.registerCommand("back", { description: "Return to the main session from an agent chat", handler: async (_args, ctx) => { const main = supervisor.mainSessionFile; if (!main) { ctx.ui.notify("Main session is not recorded", "warning"); return; } if (ctx.sessionManager.getSessionFile() === main) { ctx.ui.notify("Already on the main session", "info"); return; } await ctx.switchSession(main); } }); pi.registerCommand("research", { description: "Start a bounded research run", handler: async (args, ctx) => { const goal = args.trim(); if (!goal) { ctx.ui.notify("Usage: /research ", "warning"); return; } const state = supervisor.loop; if (!state || !supervisor.tree) throw new Error("Not initialized"); supervisor.tree.setGoal(goal); state.setGoal(goal); state.start(); const prompt = `Research goal: ${goal}\nUse research_control and the recursive agent tools. Create specialized children only when useful. Record each iteration and stop on the coded conditions. Report important progress without flooding the chat.`; if (ctx.isIdle()) pi.sendUserMessage(prompt); else pi.sendUserMessage(prompt, { deliverAs: "followUp" }); } }); pi.registerCommand("research-status", { description: "Show research loop state", handler: async (_args, ctx) => { ctx.ui.notify(JSON.stringify(supervisor.loop?.snapshot() ?? {}, null, 2), "info"); } }); diff --git a/tests/extension-smoke.test.ts b/tests/extension-smoke.test.ts index e7fb11c..6ad4f5c 100644 --- a/tests/extension-smoke.test.ts +++ b/tests/extension-smoke.test.ts @@ -2,5 +2,5 @@ import { describe, expect, it } from "vitest"; import extension from "../src/index.js"; describe("Pi extension smoke", () => { - it("loads as an extension factory and registers all commands", () => { const commands: string[] = []; const events: string[] = []; const renderers: string[] = []; const fakePi = { on: (name: string) => events.push(name), registerCommand: (name: string) => commands.push(name), registerMessageRenderer: (name: string) => renderers.push(name) } as any; extension(fakePi); expect(commands).toEqual(expect.arrayContaining(["team", "agent", "agent-open", "research", "research-status", "research-pause", "research-resume", "research-stop", "findings", "hypotheses"])); expect(events).toEqual(expect.arrayContaining(["session_start", "session_shutdown"])); expect(renderers).toContain("hypothesis-machine-agent-update"); }); + it("loads as an extension factory and registers all commands", () => { const commands: string[] = []; const events: string[] = []; const renderers: string[] = []; const fakePi = { on: (name: string) => events.push(name), registerCommand: (name: string) => commands.push(name), registerMessageRenderer: (name: string) => renderers.push(name) } as any; extension(fakePi); expect(commands).toEqual(expect.arrayContaining(["team", "agent", "research", "research-status", "research-pause", "research-resume", "research-stop", "findings", "hypotheses"])); expect(events).toEqual(expect.arrayContaining(["session_start", "session_shutdown"])); expect(renderers).toContain("hypothesis-machine-agent-update"); }); });