Files
backnotprop__plannotator/apps/hook/server/cli.ts
T
Michael Ramos f8d4969e73 feat(hook): improvement hook context injection for planning (#459)
* feat(shared): add improvement hook reader utility

Adds a runtime-agnostic module for reading improvement hook files from
~/.plannotator/hooks/. Uses a hardcoded base path with an allowlist of
known hook files and a 50KB size cap to prevent runaway context injection.

Designed for cross-harness reuse (Claude Code, OpenCode, Pi, etc.).

For provenance purposes, this commit was AI assisted.

* feat(hook): wire up improve-context PreToolUse hook for EnterPlanMode

Adds a PreToolUse hook on EnterPlanMode that injects corrective planning
instructions into Claude's context at plan mode entry. The new
`plannotator improve-context` subcommand reads the improvement hook file
and returns additionalContext, or silently passes through if no file exists.

For provenance purposes, this commit was AI assisted.

* fix(skill): update compound skill improvement hook path

Moves the improvement hook file path from ~/.plannotator/compound/ to
~/.plannotator/hooks/compound/ to namespace hook-injectable files
separately from other plannotator data.

For provenance purposes, this commit was AI assisted.

* fix(shared): add legacy path fallback for improvement hook reader

Users who ran the compound skill before the path migration have their
improvement hook file at ~/.plannotator/compound/ instead of the new
~/.plannotator/hooks/compound/ path. Add a fallback that checks the
legacy path only when the new path is absent — if the new path exists
but is invalid (empty, oversized), no fallback occurs to prevent
resurrecting stale instructions.

Includes tests for new-path-wins, legacy-fallback, and
invalid-new-blocks-legacy scenarios.

For provenance purposes, this commit was AI assisted.
2026-04-01 13:16:55 -07:00

44 lines
1.2 KiB
TypeScript

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",
" plannotator improve-context",
"",
"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");
}