// @generated — DO NOT EDIT. Source: packages/shared/annotate-args.ts /** * Parse CLI-style args arriving as a single whitespace-delimited string. * * Extracts the `--gate`, `--json`, and `--hook` flags (issue #570) * from the remainder, which is treated as the target path. Leading `@` is * stripped via the shared at-reference helper — reference-mode is primary. * Scoped-package-style literal `@` paths are handled by a fallback that the * downstream resolver opts into (see at-reference.ts). * * Used by the OpenCode plugin and Pi extension, where the whole args string * arrives pre-joined from the harness slash-command dispatcher. The Claude * Code binary parses argv directly with indexOf/splice and does not use * this helper. * * Implementation: walks the raw string once, preserving whitespace runs and * non-whitespace tokens as separate segments. Only known flag tokens * (whole-word match) plus one adjacent whitespace run are removed. * This keeps double-spaces and tabs inside file paths intact — which * matches the pre-PR behavior on `main`, where OpenCode and Pi passed * the raw args string straight through to the filesystem resolver. * * Remaining edge: if a path literally contains a known flag as a standalone * whitespace-separated token (e.g. `"Feature --gate spec.md"`), that token * is stripped. Supporting this would need shell-style quoting, which isn't * worth the complexity for a vanishingly rare naming pattern. */ import { stripAtPrefix } from "./at-reference"; import { stripWrappingQuotes } from "./resolve-file"; export interface ParsedAnnotateArgs { /** * Primary resolution path with any leading `@` stripped (reference-mode * convention). Most call sites should use this directly. */ filePath: string; /** * Raw path with the `@` prefix preserved (if the user supplied one). * Callers that want the literal-`@` fallback for scoped-package-style * paths pair this with `resolveAtReference` from at-reference.ts. */ rawFilePath: string; gate: boolean; json: boolean; hook: boolean; } type Segment = { type: "ws" | "tok"; text: string }; const FLAG_MAP = { "--gate": "gate", "--json": "json", "--hook": "hook", } as const satisfies Record>; export function parseAnnotateArgs(raw: string): ParsedAnnotateArgs { const s = (raw ?? "").trim(); const flags = { gate: false, json: false, hook: false }; const segments: Segment[] = []; for (let i = 0; i < s.length;) { const isWs = /\s/.test(s[i]); const start = i; while (i < s.length && /\s/.test(s[i]) === isWs) i++; segments.push({ type: isWs ? "ws" : "tok", text: s.slice(start, i) }); } const keep = segments.map(() => true); for (let j = 0; j < segments.length; j++) { const seg = segments[j]; if (seg.type !== "tok") continue; const key = FLAG_MAP[seg.text as keyof typeof FLAG_MAP]; if (!key) continue; flags[key] = true; keep[j] = false; // Drop one adjacent whitespace run so removed flags don't leave dangling // spaces. Prefer trailing whitespace; fall back to leading if at the end. if (j + 1 < segments.length && segments[j + 1].type === "ws") { keep[j + 1] = false; } else if (j > 0 && segments[j - 1].type === "ws") { keep[j - 1] = false; } } // Trim covers the case where two adjacent flags (`... --gate --json`) // both claim the single whitespace between them, leaving a trailing space // after the kept token. Wrapping quotes come from OpenCode/Pi users who // quote paths with spaces (shell muscle memory); strip them here so // downstream callers never see tokenization artifacts. const rawFilePath = stripWrappingQuotes( segments .filter((_, j) => keep[j]) .map((seg) => seg.text) .join("") .trim(), ); if (flags.hook) flags.gate = true; return { filePath: stripAtPrefix(rawFilePath), rawFilePath, ...flags }; }