diff --git a/.changeset/cli-session-ls-terminated.md b/.changeset/cli-session-ls-terminated.md new file mode 100644 index 000000000..792882282 --- /dev/null +++ b/.changeset/cli-session-ls-terminated.md @@ -0,0 +1,5 @@ +--- +"@aoagents/ao-cli": patch +--- + +`ao session ls` hides terminal sessions in text output by default; use `--include-terminated` for the full text list. diff --git a/packages/cli/__tests__/commands/session.test.ts b/packages/cli/__tests__/commands/session.test.ts index 768c02590..57d30967a 100644 --- a/packages/cli/__tests__/commands/session.test.ts +++ b/packages/cli/__tests__/commands/session.test.ts @@ -370,6 +370,23 @@ describe("session ls", () => { }); }); + it("filters terminal sessions from JSON by default and reports hidden count", async () => { + writeFileSync(join(sessionsDir, "app-1"), "branch=main\nstatus=working\n"); + writeFileSync(join(sessionsDir, "app-done"), "branch=main\nstatus=merged\nactivity=exited\n"); + + mockTmux.mockResolvedValue(null); + mockGit.mockResolvedValue(null); + + await program.parseAsync(["node", "test", "session", "ls", "--json"]); + + const parsed = JSON.parse(String(consoleSpy.mock.calls[0][0])) as { + data: Array<{ id: string }>; + meta: { hiddenTerminatedCount: number }; + }; + expect(parsed.data.map((entry) => entry.id)).toEqual(["app-1"]); + expect(parsed.meta.hiddenTerminatedCount).toBe(1); + }); + it("marks metadata-based orchestrators correctly in JSON output", async () => { writeFileSync( join(sessionsDir, "app-control"), @@ -573,6 +590,71 @@ describe("session ls", () => { expect(parsed.data.map((e: { id: string }) => e.id)).toEqual(["app-1"]); expect(parsed.meta.hiddenTerminatedCount).toBe(3); }); + + it("hides terminal-status sessions by default", async () => { + writeFileSync(join(sessionsDir, "app-1"), "branch=main\nstatus=working\n"); + writeFileSync(join(sessionsDir, "app-done"), "branch=main\nstatus=merged\n"); + + mockTmux.mockImplementation(async (...args: string[]) => { + if (args[0] === "list-sessions") return "app-1\napp-done"; + if (args[0] === "display-message") { + return String(Math.floor(Date.now() / 1000) - 60); + } + return null; + }); + mockGit.mockResolvedValue(null); + + await program.parseAsync(["node", "test", "session", "ls"]); + + const output = consoleSpy.mock.calls.map((c) => String(c[0])).join("\n"); + expect(output).toContain("app-1"); + expect(output).not.toContain("app-done"); + }); + + it("lists terminal sessions when --include-terminated is set", async () => { + writeFileSync(join(sessionsDir, "app-done"), "branch=main\nstatus=merged\n"); + + mockTmux.mockImplementation(async (...args: string[]) => { + if (args[0] === "list-sessions") return "app-done"; + return null; + }); + mockGit.mockResolvedValue(null); + + await program.parseAsync(["node", "test", "session", "ls", "--include-terminated"]); + + const output = consoleSpy.mock.calls.map((c) => String(c[0])).join("\n"); + expect(output).toContain("app-done"); + }); + + it("prints a hint when only terminal sessions exist", async () => { + writeFileSync(join(sessionsDir, "app-done"), "branch=main\nstatus=merged\n"); + + mockTmux.mockImplementation(async (...args: string[]) => { + if (args[0] === "list-sessions") return "app-done"; + return null; + }); + + await program.parseAsync(["node", "test", "session", "ls"]); + + const output = consoleSpy.mock.calls.map((c) => String(c[0])).join("\n"); + expect(output).toContain("--include-terminated"); + expect(output).not.toContain("app-done"); + }); + + it("prints a hint when terminal sessions are hidden alongside active ones", async () => { + writeFileSync(join(sessionsDir, "app-1"), "branch=main\nstatus=working\n"); + writeFileSync(join(sessionsDir, "app-done"), "branch=main\nstatus=merged\n"); + + mockTmux.mockResolvedValue(null); + mockGit.mockResolvedValue(null); + + await program.parseAsync(["node", "test", "session", "ls"]); + + const output = consoleSpy.mock.calls.map((c) => String(c[0])).join("\n"); + expect(output).toContain("app-1"); + expect(output).toContain("terminated session"); + expect(output).toContain("--include-terminated"); + }); }); describe("session kill", () => {