mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
cli: clarify no-arg usage and add top-level help (#448)
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
formatInteractiveNoArgClarification,
|
||||
formatTopLevelHelp,
|
||||
isInteractiveNoArgInvocation,
|
||||
isTopLevelHelpInvocation,
|
||||
} from "./cli";
|
||||
|
||||
describe("CLI top-level help", () => {
|
||||
test("recognizes top-level --help", () => {
|
||||
expect(isTopLevelHelpInvocation(["--help"])).toBe(true);
|
||||
expect(isTopLevelHelpInvocation([])).toBe(false);
|
||||
expect(isTopLevelHelpInvocation(["review", "--help"])).toBe(false);
|
||||
});
|
||||
|
||||
test("renders concise top-level usage", () => {
|
||||
const output = formatTopLevelHelp();
|
||||
|
||||
expect(output).toContain("plannotator --help");
|
||||
expect(output).toContain("plannotator [--browser <name>]");
|
||||
expect(output).toContain("plannotator review [PR_URL]");
|
||||
expect(output).toContain("plannotator annotate <file.md | folder/>");
|
||||
expect(output).toContain("running 'plannotator' without arguments is for hook integration");
|
||||
});
|
||||
});
|
||||
|
||||
describe("interactive no-arg invocation", () => {
|
||||
test("detects bare interactive invocation only when stdin is a TTY", () => {
|
||||
expect(isInteractiveNoArgInvocation([], true)).toBe(true);
|
||||
expect(isInteractiveNoArgInvocation([], false)).toBe(false);
|
||||
expect(isInteractiveNoArgInvocation([], undefined)).toBe(false);
|
||||
expect(isInteractiveNoArgInvocation(["review"], true)).toBe(false);
|
||||
});
|
||||
|
||||
test("renders clarification for interactive users", () => {
|
||||
const output = formatInteractiveNoArgClarification();
|
||||
|
||||
expect(output).toContain("usually launched automatically by Claude Code hooks");
|
||||
expect(output).toContain("It expects hook JSON on stdin.");
|
||||
expect(output).toContain("plannotator review");
|
||||
expect(output).toContain("plannotator sessions");
|
||||
expect(output).toContain("Run 'plannotator --help' for top-level usage.");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
export function isTopLevelHelpInvocation(args: string[]): boolean {
|
||||
return args[0] === "--help";
|
||||
}
|
||||
|
||||
export function isInteractiveNoArgInvocation(
|
||||
args: string[],
|
||||
stdinIsTTY: boolean | undefined,
|
||||
): boolean {
|
||||
return args.length === 0 && stdinIsTTY === true;
|
||||
}
|
||||
|
||||
export function formatTopLevelHelp(): string {
|
||||
return [
|
||||
"Usage:",
|
||||
" plannotator --help",
|
||||
" plannotator [--browser <name>]",
|
||||
" plannotator review [PR_URL]",
|
||||
" plannotator annotate <file.md | folder/>",
|
||||
" plannotator last",
|
||||
" plannotator archive",
|
||||
" plannotator sessions",
|
||||
"",
|
||||
"Note:",
|
||||
" running 'plannotator' without arguments is for hook integration and expects JSON on stdin",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export function formatInteractiveNoArgClarification(): string {
|
||||
return [
|
||||
"plannotator (without arguments) is usually launched automatically by Claude Code hooks.",
|
||||
"It expects hook JSON on stdin.",
|
||||
"",
|
||||
"For interactive use, try:",
|
||||
" plannotator review",
|
||||
" plannotator annotate <file.md>",
|
||||
" plannotator last",
|
||||
" plannotator archive",
|
||||
" plannotator sessions",
|
||||
"",
|
||||
"Run 'plannotator --help' for top-level usage.",
|
||||
].join("\n");
|
||||
}
|
||||
@@ -38,6 +38,7 @@
|
||||
* - Parses events.jsonl from session state
|
||||
*
|
||||
* Global flags:
|
||||
* --help - Show top-level usage information
|
||||
* --browser <name> - Override which browser to open (e.g. "Google Chrome")
|
||||
*
|
||||
* Environment variables:
|
||||
@@ -71,6 +72,12 @@ import type { Origin } from "@plannotator/shared/agents";
|
||||
import { findSessionLogsForCwd, resolveSessionLogByPpid, findSessionLogsByAncestorWalk, getLastRenderedMessage, type RenderedMessage } from "./session-log";
|
||||
import { findCodexRolloutByThreadId, getLastCodexMessage } from "./codex-session";
|
||||
import { findCopilotPlanContent, findCopilotSessionForCwd, getLastCopilotMessage } from "./copilot-session";
|
||||
import {
|
||||
formatInteractiveNoArgClarification,
|
||||
formatTopLevelHelp,
|
||||
isInteractiveNoArgInvocation,
|
||||
isTopLevelHelpInvocation,
|
||||
} from "./cli";
|
||||
import path from "path";
|
||||
|
||||
// Embed the built HTML at compile time
|
||||
@@ -92,6 +99,16 @@ if (browserIdx !== -1 && args[browserIdx + 1]) {
|
||||
args.splice(browserIdx, 2);
|
||||
}
|
||||
|
||||
if (isTopLevelHelpInvocation(args)) {
|
||||
console.log(formatTopLevelHelp());
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (isInteractiveNoArgInvocation(args, process.stdin.isTTY)) {
|
||||
console.log(formatInteractiveNoArgClarification());
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Ensure session cleanup on exit
|
||||
process.on("exit", () => unregisterSession());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user