@delorenj/pjangler 1.4.3 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +528 -0
- package/contracts/fleet-contract.yaml +513 -0
- package/dist/index.js +6135 -288
- package/dist/mcp-server.js +5335 -479
- package/package.json +4 -2
- package/templates/hermes-agent/template/.scripts/lib/ticket-provider.sh +84 -4
- package/templates/hermes-agent/template/.scripts/providers/linear.sh +114 -24
- package/templates/hermes-agent/template/.scripts/providers/plane.sh +354 -43
- package/templates/hermes-agent/template/.scripts/providers/trello.sh +29 -4
- package/templates/hermes-agent/template/.scripts/sentinel/bin/issue-autonomous-review.sh +253 -28
- package/templates/hermes-agent/template/.scripts/sentinel/bin/issue-close-gate.sh +142 -11
- package/templates/hermes-agent/template/.scripts/sentinel/docs/continuous-ticket-orchestration.md +3 -1
- package/templates/hermes-agent/template/role.yaml.jinja +4 -0
- package/dist/index.js.map +0 -7
- package/dist/mcp-server.js.map +0 -7
- package/dist/prompt.js.map +0 -7
package/dist/prompt.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/prompt.ts", "../src/describe/activity.ts", "../src/project/boardUrl.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n// `pjangler-prompt` \u2014 the shell-prompt surface.\n//\n// This is a SEPARATE entry point from the main CLI on purpose. `dist/index.js`\n// pulls in the whole parity rule set (a ~280KB module) and takes ~52ms just to\n// boot, which is far too much to pay on every shell prompt. This bundle\n// imports only the activity probe and node builtins, so it lands near node's\n// own startup floor.\n//\n// Contract with starship: print ONE line and exit 0 when the cwd is inside a\n// pjangler project, print NOTHING otherwise. Starship renders a custom\n// module's format wrapper even when the command fails, so \"no output\" \u2014 not a\n// non-zero exit \u2014 is what makes the extra prompt line disappear cleanly.\n//\n// `--url [ref]` is the second contract: print the board (or work-item) URL for\n// this project and nothing else, exiting non-zero when there is nothing to\n// point at so a shell widget can tell \"no project here\" from \"here it is\".\n// It lives on THIS entry point rather than the main CLI for one reason \u2014 the\n// prompt and the shortcut must never disagree about which board you are on,\n// and the cheapest way to guarantee that is to make them the same program\n// reading the same facts.\n//\n// Never throws. A broken manifest degrades to the directory name rather than\n// spilling a stack trace into someone's prompt.\n\nimport { readFileSync, realpathSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { computeRepoActivity } from \"./describe/activity\";\nimport { findProjectRoot, resolveBoardUrl } from \"./project/boardUrl\";\n\nexport interface PromptFacts {\n root: string;\n slug: string;\n identifier?: string;\n age?: string;\n active: boolean;\n}\n\n/**\n * Nearest ancestor holding a `.project.json`, starting at `from`.\n *\n * Walking up matters: most of the time you are in `src/` or `docs/`, not the\n * repo root. Node resolves symlinks in `process.cwd()`, so unlike the shell's\n * `$PWD` this needs no physical-path fallback.\n *\n * Re-exported rather than reimplemented: `--url` resolution needs the same\n * walk, and two copies of \"where is the project root\" is exactly the kind of\n * drift that makes the prompt and the shortcut disagree.\n */\nexport { findProjectRoot };\n\nexport function readPromptFacts(root: string, now?: Date): PromptFacts {\n let slug = basename(root);\n let identifier: string | undefined;\n try {\n const manifest = JSON.parse(readFileSync(join(root, \".project.json\"), \"utf8\")) as Record<string, unknown>;\n if (typeof manifest.project_slug === \"string\" && manifest.project_slug) slug = manifest.project_slug;\n const provider = manifest.ticket_provider as Record<string, unknown> | undefined;\n if (provider && typeof provider.identifier === \"string\" && provider.identifier) identifier = provider.identifier;\n } catch {\n // A malformed manifest still identifies a project; the directory name is a\n // truthful fallback and keeps the prompt line from vanishing confusingly.\n }\n\n const activity = computeRepoActivity(root, { now });\n return {\n root,\n slug,\n identifier,\n age: activity.updatedUnix ? activity.compact : undefined,\n active: activity.active,\n };\n}\n\n/** `pjangler (PJAN) \u00B7 3m` \u2014 deliberately terse; a prompt is not a report. */\nexport function formatPromptLine(facts: PromptFacts): string {\n const parts = [facts.slug];\n if (facts.identifier) parts.push(`(${facts.identifier})`);\n const head = parts.join(\" \");\n return facts.age ? `${head} \u00B7 ${facts.age}` : head;\n}\n\n/** Returns the line to print, or undefined when there is nothing to say. */\nexport function promptLine(cwd: string, now?: Date): string | undefined {\n const root = findProjectRoot(cwd);\n if (!root) return undefined;\n return formatPromptLine(readPromptFacts(root, now));\n}\n\nfunction main(): void {\n try {\n const args = process.argv.slice(2);\n if (args[0] === \"--url\") {\n // Trailing newline here, unlike the prompt line: this output is consumed\n // by `$(...)` and read by humans, not spliced into a prompt string.\n const url = resolveBoardUrl(process.cwd(), args[1]);\n if (url) process.stdout.write(`${url}\\n`);\n else process.exitCode = 1;\n return;\n }\n const line = promptLine(process.cwd());\n if (line) process.stdout.write(line);\n } catch {\n // A prompt must never be the thing that breaks a shell.\n }\n}\n\n/**\n * Run only when executed directly, so the pieces above stay importable.\n * `realpathSync` matters: npm installs bins as symlinks, so argv[1] is\n * `.bin/pjangler-prompt` while import.meta.url is the real `dist/prompt.js`.\n */\nfunction isMainModule(): boolean {\n if (!process.argv[1]) return false;\n try {\n return import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href;\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) main();\n", "// Repo activity \u2014 \"when was work last done here?\"\n//\n// This replaces the `status` field, which claimed `planned` for 22 of the 27\n// registered projects \u2014 pjangler itself among them, on a day it had commits.\n// A repo in the registry is by definition past planning, so the field never\n// varied and never informed.\n//\n// Activity is temporal instead of categorical, and COMPUTED instead of stored.\n// A stored status goes stale the moment someone commits, which is exactly how\n// the old one ended up wrong everywhere; deriving it from git at read time\n// means it cannot drift, and no cron has to walk every project to keep it\n// fresh. (The registry's own `updated_at` is not a substitute: it records the\n// last registry write, not the last piece of work.)\n//\n// \"Work\" deliberately spans more than the checked-out branch:\n//\n// refs every local branch, remote-tracking branch, and tag\n// worktrees every linked worktree's HEAD, which catches detached HEADs\n// whose commits are on no ref at all\n// uncommitted changes in the working tree that are not committed yet\n//\n// That breadth is not theoretical. Across the live registry, the newest work\n// in several projects (deckard, intelliforia, slowburns) lives in a worktree,\n// so a naive `git log -1` on the checked-out branch reports them as stale.\n//\n// Honesty note: remote-tracking refs reflect the last fetch, not the live\n// remote. Asking the network would mean a round trip per project, which is\n// unacceptable on a shell prompt. The winning source is always reported, so a\n// reader can see whether the answer came from local or remote state.\n//\n// Structure: the parsing is pure and the IO is a thin driver, in two flavours\n// (sync for one repo, async for scanning the whole registry concurrently).\n// The tricky part is the parsing, and keeping it pure means it is unit\n// testable without constructing a repo for every edge case.\n\nimport { spawn, spawnSync } from \"node:child_process\";\nimport { statSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/** A repo counts as active when the newest work is within this window. */\nexport const ACTIVE_WINDOW_SECONDS = 24 * 60 * 60;\n\n/** Stop stat-ing dirty files past this many; the newest mtime converges fast. */\nconst MAX_DIRTY_STATS = 500;\n\nexport type ActivityKind = \"ref\" | \"worktree\" | \"uncommitted\";\n\nexport interface ActivitySource {\n kind: ActivityKind;\n /** Human label for the winning source: \"main\", \"origin/feat/x\", \"3 files\". */\n label: string;\n unix: number;\n}\n\nexport interface RepoActivity {\n /** ISO-8601 of the newest work, or null when the repo has no history. */\n updated: string | null;\n updatedUnix: number | null;\n /** \"3 hours ago\" \u2014 or \"never\" when nothing has happened yet. */\n relative: string;\n /** Compact form for space-constrained surfaces like a shell prompt: \"3h\". */\n compact: string;\n /** Newest work is within ACTIVE_WINDOW_SECONDS. */\n active: boolean;\n source: ActivitySource | null;\n scanned: { refs: number; worktrees: number; dirtyFiles: number };\n}\n\nexport interface ActivityOptions {\n /** Injected clock. Tests pass a fixed instant; production passes nothing. */\n now?: Date;\n}\n\n// ---------------------------------------------------------------------------\n// git plumbing\n// ---------------------------------------------------------------------------\n\nconst GIT_TIMEOUT_MS = 5_000;\nconst GIT_MAX_BUFFER = 16 * 1024 * 1024;\n\n/** Run a git subcommand in `repo`. Returns raw stdout, or undefined on failure. */\nexport function git(repo: string, args: string[]): string | undefined {\n const result = spawnSync(\"git\", [\"-C\", repo, ...args], {\n encoding: \"utf8\",\n timeout: GIT_TIMEOUT_MS,\n maxBuffer: GIT_MAX_BUFFER,\n });\n if (result.status !== 0 || typeof result.stdout !== \"string\") return undefined;\n return result.stdout;\n}\n\n/** Async twin of `git`, so a registry scan can run repos concurrently. */\nexport function gitAsync(repo: string, args: string[]): Promise<string | undefined> {\n return new Promise((resolve) => {\n const child = spawn(\"git\", [\"-C\", repo, ...args], { stdio: [\"ignore\", \"pipe\", \"ignore\"] });\n let out = \"\";\n let size = 0;\n let settled = false;\n const finish = (value: string | undefined) => {\n if (settled) return;\n settled = true;\n resolve(value);\n };\n const timer = setTimeout(() => {\n child.kill(\"SIGKILL\");\n finish(undefined);\n }, GIT_TIMEOUT_MS);\n timer.unref?.();\n\n child.stdout.setEncoding(\"utf8\");\n child.stdout.on(\"data\", (chunk: string) => {\n size += chunk.length;\n if (size > GIT_MAX_BUFFER) {\n child.kill(\"SIGKILL\");\n finish(undefined);\n return;\n }\n out += chunk;\n });\n child.on(\"error\", () => {\n clearTimeout(timer);\n finish(undefined);\n });\n child.on(\"close\", (code) => {\n clearTimeout(timer);\n finish(code === 0 ? out : undefined);\n });\n });\n}\n\nfunction trimmed(raw: string | undefined): string | undefined {\n if (raw === undefined) return undefined;\n const value = raw.trim();\n return value === \"\" ? undefined : value;\n}\n\n/** Run a git subcommand and trim; undefined for failure or empty output. */\nexport function gitLine(repo: string, args: string[]): string | undefined {\n return trimmed(git(repo, args));\n}\n\nexport function isGitRepo(repo: string): boolean {\n return gitLine(repo, [\"rev-parse\", \"--is-inside-work-tree\"]) === \"true\";\n}\n\n// ---------------------------------------------------------------------------\n// Relative time\n// ---------------------------------------------------------------------------\n\nconst MINUTE = 60;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\nconst WEEK = 7 * DAY;\nconst MONTH = 30 * DAY;\nconst YEAR = 365 * DAY;\n\nfunction plural(count: number, unit: string): string {\n return `${count} ${unit}${count === 1 ? \"\" : \"s\"} ago`;\n}\n\n/**\n * Human relative age. Deterministic and clock-injected so it is testable \u2014 no\n * \"about a minute\" fuzz, because a tool reporting staleness should be precise\n * about which bucket it chose.\n */\nexport function formatRelativeAge(deltaSeconds: number): string {\n const delta = Math.max(0, Math.floor(deltaSeconds));\n if (delta < MINUTE) return \"just now\";\n if (delta < HOUR) return plural(Math.floor(delta / MINUTE), \"minute\");\n if (delta < DAY) return plural(Math.floor(delta / HOUR), \"hour\");\n if (delta < WEEK) return plural(Math.floor(delta / DAY), \"day\");\n if (delta < MONTH) return plural(Math.floor(delta / WEEK), \"week\");\n if (delta < YEAR) return plural(Math.floor(delta / MONTH), \"month\");\n return plural(Math.floor(delta / YEAR), \"year\");\n}\n\n/** Same ladder, one or two characters wide, for prompts: \"now\", \"5m\", \"3d\". */\nexport function formatCompactAge(deltaSeconds: number): string {\n const delta = Math.max(0, Math.floor(deltaSeconds));\n if (delta < MINUTE) return \"now\";\n if (delta < HOUR) return `${Math.floor(delta / MINUTE)}m`;\n if (delta < DAY) return `${Math.floor(delta / HOUR)}h`;\n if (delta < WEEK) return `${Math.floor(delta / DAY)}d`;\n if (delta < MONTH) return `${Math.floor(delta / WEEK)}w`;\n if (delta < YEAR) return `${Math.floor(delta / MONTH)}mo`;\n return `${Math.floor(delta / YEAR)}y`;\n}\n\n// ---------------------------------------------------------------------------\n// Commands (pure) \u2014 what to ask git\n// ---------------------------------------------------------------------------\n\nexport const REF_ARGS = [\n \"for-each-ref\",\n \"--sort=-committerdate\",\n \"--format=%(committerdate:unix)%09%(refname:short)\",\n \"refs/heads\",\n \"refs/remotes\",\n \"refs/tags\",\n];\n\nexport const WORKTREE_ARGS = [\"worktree\", \"list\", \"--porcelain\"];\n\n/**\n * `--porcelain -z` is deliberate: the non-`-z` form C-quotes paths containing\n * spaces or unicode, and un-quoting that correctly is a parser nobody should\n * write. NUL separation sidesteps it.\n *\n * `--ignore-submodules=dirty` is also deliberate, on both cost and correctness\n * grounds. Cost: without it `git status` recurses into every submodule working\n * tree \u2014 measured at 100ms on the 33GOD superproject versus under a\n * millisecond with it, which alone would rule out the shell-prompt path.\n * Correctness: a submodule is its own repo with its own registry entry and its\n * own activity, so edits inside it are that project's work, not the\n * superproject's. Committed submodule POINTER moves still count, because those\n * genuinely are changes to this repo.\n */\nexport const STATUS_ARGS = [\"status\", \"--porcelain\", \"-z\", \"--ignore-submodules=dirty\"];\n\n// ---------------------------------------------------------------------------\n// Parsers (pure) \u2014 what git said\n// ---------------------------------------------------------------------------\n\nexport interface WorktreeEntry {\n path: string;\n sha: string;\n detached: boolean;\n}\n\n/** Newest ref plus the total ref count, from one sorted ref walk. */\nexport function parseRefs(raw: string | undefined): { source?: ActivitySource; count: number } {\n if (raw === undefined) return { count: 0 };\n const lines = raw.split(\"\\n\").filter((line) => line.trim() !== \"\");\n if (!lines.length) return { count: 0 };\n\n const [stamp, name] = lines[0]!.split(\"\\t\");\n const unix = Number(stamp);\n if (!Number.isFinite(unix) || unix <= 0) return { count: lines.length };\n return { source: { kind: \"ref\", label: name ?? \"(unnamed ref)\", unix }, count: lines.length };\n}\n\nexport function parseWorktrees(raw: string | undefined): WorktreeEntry[] {\n if (raw === undefined) return [];\n const entries: WorktreeEntry[] = [];\n let current: { path?: string; sha?: string; detached: boolean } = { detached: false };\n const flush = () => {\n if (current.path && current.sha) entries.push({ path: current.path, sha: current.sha, detached: current.detached });\n current = { detached: false };\n };\n for (const line of raw.split(\"\\n\")) {\n if (line.startsWith(\"worktree \")) {\n flush();\n current.path = line.slice(\"worktree \".length);\n } else if (line.startsWith(\"HEAD \")) {\n current.sha = line.slice(\"HEAD \".length).trim();\n } else if (line === \"detached\") {\n current.detached = true;\n }\n }\n flush();\n return entries;\n}\n\n/** Pick the newest worktree HEAD out of a `git show -s --format=%ct %H` batch. */\nexport function parseWorktreeStamps(raw: string | undefined, entries: readonly WorktreeEntry[]): ActivitySource | undefined {\n if (raw === undefined) return undefined;\n let best: ActivitySource | undefined;\n for (const line of raw.split(\"\\n\")) {\n const [stamp, sha] = line.trim().split(\" \");\n const unix = Number(stamp);\n if (!Number.isFinite(unix) || unix <= 0 || !sha) continue;\n if (best && unix <= best.unix) continue;\n const owner = entries.find((entry) => entry.sha === sha);\n const name = owner ? basenameOf(owner.path) : sha.slice(0, 7);\n best = { kind: \"worktree\", label: owner?.detached ? `${name} (detached)` : name, unix };\n }\n return best;\n}\n\n/**\n * Paths from `git status --porcelain -z`. Every entry is \"XY <path>\"; renames\n * and copies are followed by a bare origin path with no status prefix, which\n * is consumed rather than treated as a second change.\n */\nexport function parseStatusPaths(raw: string | undefined): string[] {\n if (raw === undefined) return [];\n const parts = raw.split(\"\\0\").filter((part) => part !== \"\");\n const paths: string[] = [];\n for (let index = 0; index < parts.length; index++) {\n const entry = parts[index]!;\n if (entry.length < 4 || entry[2] !== \" \") continue;\n paths.push(entry.slice(3));\n if (entry[0] === \"R\" || entry[0] === \"C\") index += 1;\n }\n return paths;\n}\n\nfunction basenameOf(path: string): string {\n const parts = path.split(\"/\").filter(Boolean);\n return parts[parts.length - 1] ?? path;\n}\n\n/**\n * Newest mtime among uncommitted paths. This is what makes \"active\" true while\n * you are mid-edit and have not committed yet \u2014 the case a commit-only signal\n * misses entirely.\n */\nexport function uncommittedSource(repo: string, paths: readonly string[]): ActivitySource | undefined {\n if (!paths.length) return undefined;\n let newest = 0;\n for (const path of paths.slice(0, MAX_DIRTY_STATS)) {\n try {\n const mtime = Math.floor(statSync(join(repo, path)).mtimeMs / 1000);\n if (mtime > newest) newest = mtime;\n } catch {\n // Deleted (or unreadable) paths have no mtime to contribute.\n }\n }\n if (newest <= 0) return undefined;\n const label = paths.length === 1 ? \"1 uncommitted file\" : `${paths.length} uncommitted files`;\n return { kind: \"uncommitted\", label, unix: newest };\n}\n\n// ---------------------------------------------------------------------------\n// Assembly (pure)\n// ---------------------------------------------------------------------------\n\nconst NO_ACTIVITY: RepoActivity = {\n updated: null,\n updatedUnix: null,\n relative: \"never\",\n compact: \"\u2014\",\n active: false,\n source: null,\n scanned: { refs: 0, worktrees: 0, dirtyFiles: 0 },\n};\n\n/** Empty activity, for a path that is not a git repo at all. */\nexport function emptyActivity(): RepoActivity {\n return { ...NO_ACTIVITY, scanned: { refs: 0, worktrees: 0, dirtyFiles: 0 } };\n}\n\n/**\n * Ties break toward immediacy (uncommitted > worktree > ref): when a commit\n * and a working-tree edit share a second, the edit is the later event.\n */\nexport function assembleActivity(\n candidates: readonly (ActivitySource | undefined)[],\n scanned: RepoActivity[\"scanned\"],\n now?: Date,\n): RepoActivity {\n let winner: ActivitySource | null = null;\n for (const candidate of candidates) {\n if (!candidate) continue;\n if (!winner || candidate.unix >= winner.unix) winner = candidate;\n }\n if (!winner) return { ...NO_ACTIVITY, scanned };\n\n const nowUnix = Math.floor((now?.getTime() ?? Date.now()) / 1000);\n const delta = nowUnix - winner.unix;\n return {\n updated: new Date(winner.unix * 1000).toISOString(),\n updatedUnix: winner.unix,\n relative: formatRelativeAge(delta),\n compact: formatCompactAge(delta),\n active: delta < ACTIVE_WINDOW_SECONDS,\n source: winner,\n scanned,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Drivers\n// ---------------------------------------------------------------------------\n\n/**\n * Newest work across every branch, worktree, and uncommitted change.\n *\n * There is deliberately no \"fast, less accurate\" variant. The three probes\n * together measure ~8ms of git, and the ~5ms a ref-only shortcut would save\n * comes at the cost of missing detached worktrees entirely \u2014 a repo whose work\n * happens in a detached worktree would report months of staleness. That is the\n * exact coverage this function exists to provide, so it is not optional.\n */\nexport function computeRepoActivity(repo: string, options: ActivityOptions = {}): RepoActivity {\n if (!isGitRepo(repo)) return emptyActivity();\n\n const refs = parseRefs(git(repo, REF_ARGS));\n const worktrees = parseWorktrees(git(repo, WORKTREE_ARGS));\n const shas = [...new Set(worktrees.map((entry) => entry.sha))];\n const worktreeSource = shas.length\n ? parseWorktreeStamps(git(repo, [\"show\", \"-s\", \"--format=%ct %H\", ...shas]), worktrees)\n : undefined;\n const paths = parseStatusPaths(git(repo, STATUS_ARGS));\n\n return assembleActivity(\n [refs.source, worktreeSource, uncommittedSource(repo, paths)],\n { refs: refs.count, worktrees: worktrees.length, dirtyFiles: paths.length },\n options.now,\n );\n}\n\n/** Async twin of `computeRepoActivity`, for concurrent registry scans. */\nexport async function computeRepoActivityAsync(repo: string, options: ActivityOptions = {}): Promise<RepoActivity> {\n if (trimmed(await gitAsync(repo, [\"rev-parse\", \"--is-inside-work-tree\"])) !== \"true\") return emptyActivity();\n\n const [refRaw, worktreeRaw, statusRaw] = await Promise.all([\n gitAsync(repo, REF_ARGS),\n gitAsync(repo, WORKTREE_ARGS),\n gitAsync(repo, STATUS_ARGS),\n ]);\n\n const refs = parseRefs(refRaw);\n const worktrees = parseWorktrees(worktreeRaw);\n const shas = [...new Set(worktrees.map((entry) => entry.sha))];\n const worktreeSource = shas.length\n ? parseWorktreeStamps(await gitAsync(repo, [\"show\", \"-s\", \"--format=%ct %H\", ...shas]), worktrees)\n : undefined;\n const paths = parseStatusPaths(statusRaw);\n\n return assembleActivity(\n [refs.source, worktreeSource, uncommittedSource(repo, paths)],\n { refs: refs.count, worktrees: worktrees.length, dirtyFiles: paths.length },\n options.now,\n );\n}\n\n/**\n * Activity for many repos at once, keyed by the path passed in.\n *\n * Scanning the live registry serially costs ~450ms across 27 projects, which\n * is a visible stall on `project list`; a bounded pool brings that down to\n * roughly the cost of the slowest repo.\n */\nexport async function computeRepoActivityBatch(\n repos: readonly string[],\n options: ActivityOptions & { concurrency?: number } = {},\n): Promise<Map<string, RepoActivity>> {\n const results = new Map<string, RepoActivity>();\n const unique = [...new Set(repos)];\n const limit = Math.max(1, options.concurrency ?? 8);\n let cursor = 0;\n\n const worker = async (): Promise<void> => {\n while (cursor < unique.length) {\n const repo = unique[cursor++]!;\n try {\n results.set(repo, await computeRepoActivityAsync(repo, options));\n } catch {\n results.set(repo, emptyActivity());\n }\n }\n };\n\n await Promise.all(Array.from({ length: Math.min(limit, unique.length) }, worker));\n return results;\n}\n", "// Ticket-provider URL derivation \u2014 the one place a board or work-item URL is\n// constructed.\n//\n// `src/project/index.ts` has long declared the rule (\"board URLs are derived\n// from provider + workspace + board_id at runtime; the manifest stores only\n// stable identity\") without anywhere actually performing it, so every caller\n// that wanted a URL either hand-assembled one or persisted a stale\n// `board_url`. This module is that missing function.\n//\n// Deliberately dependency-free \u2014 node builtins only, no imports from\n// `./index`. `src/prompt.ts` is a separate size-critical bundle that runs on\n// every shell prompt, and pulling the registry (or the parity rule set behind\n// it) into that bundle would cost more than the whole feature is worth.\n\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\n\nexport const DEFAULT_PLANE_BASE = \"https://plane.delo.sh\";\nexport const DEFAULT_PLANE_WORKSPACE = \"33god\";\n\n/** The `.project.json` `ticket_provider` block, as far as URL building cares. */\nexport interface TicketProviderFacts {\n type?: string;\n workspace?: string;\n identifier?: string;\n board_id?: string;\n}\n\nexport interface BoardUrlOptions {\n /** Explicit work item: `71`, `PJAN-71`, or `pjan-71`. Wins over `branch`. */\n ref?: string;\n /** Branch name to mine for a ticket reference when `ref` is absent. */\n branch?: string;\n env?: NodeJS.ProcessEnv;\n home?: string;\n}\n\n/**\n * Where the Hermes template keeps `[plane] base`.\n *\n * NOTE: twin of `resolveTemplateConfigPath` in\n * `src/commands/hermes/EnsureTemplateConfig.ts`. That module cannot be\n * imported here without dragging the command layer into the prompt bundle.\n * `tests/pjan-72-regressions.mjs` carries a drift tripwire on the pair.\n */\nexport function resolveTemplateConfigPath(env: NodeJS.ProcessEnv = process.env, home = homedir()): string {\n const fromEnv = env.HERMES_TEMPLATE_CONFIG;\n if (fromEnv && fromEnv.trim()) return fromEnv.trim();\n const xdg = env.XDG_CONFIG_HOME?.trim();\n const base = xdg && xdg.length ? xdg : join(home, \".config\");\n return join(base, \"hermes-agent-template\", \"config.toml\");\n}\n\n/**\n * Read `key` from `[section]` of a small TOML file.\n *\n * Scoped to exactly what this module needs \u2014 two string scalars out of a\n * generated config. Not a TOML parser, and deliberately not pretending to be\n * one: anything it cannot confidently read comes back undefined and the caller\n * falls through to a default.\n */\nexport function readTomlScalar(text: string, section: string, key: string): string | undefined {\n let inSection = false;\n for (const raw of text.split(\"\\n\")) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) continue;\n if (line.startsWith(\"[\")) {\n inSection = line === `[${section}]`;\n continue;\n }\n if (!inSection) continue;\n const eq = line.indexOf(\"=\");\n if (eq === -1) continue;\n if (line.slice(0, eq).trim() !== key) continue;\n const value = line.slice(eq + 1).trim();\n const quoted = /^\"([^\"]*)\"|^'([^']*)'/.exec(value);\n if (quoted) return quoted[1] ?? quoted[2];\n const bare = (value.split(\"#\")[0] ?? \"\").trim();\n return bare || undefined;\n }\n return undefined;\n}\n\nfunction readTemplateConfig(env: NodeJS.ProcessEnv, home: string): string | undefined {\n try {\n const path = resolveTemplateConfigPath(env, home);\n return existsSync(path) ? readFileSync(path, \"utf8\") : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Plane instance base URL.\n *\n * Precedence mirrors the shell adapter at\n * `agents/hermes/pm/.scripts/providers/plane.sh:22` \u2014 env override first, then\n * the generated template config, then the fleet default \u2014 so the CLI and the\n * PM agent can never disagree about which Plane they are talking to.\n */\nexport function planeBase(env: NodeJS.ProcessEnv = process.env, home = homedir()): string {\n const fromEnv = env.PLANE_BASE?.trim();\n if (fromEnv) return fromEnv.replace(/\\/+$/, \"\");\n const config = readTemplateConfig(env, home);\n const fromConfig = config ? readTomlScalar(config, \"plane\", \"base\")?.trim() : undefined;\n if (fromConfig) return fromConfig.replace(/\\/+$/, \"\");\n return DEFAULT_PLANE_BASE;\n}\n\n/**\n * Workspace slug for a board binding: manifest, then generated template\n * config, then the fleet default.\n *\n * Exported for `boardQuery.ts`, which needs the identical chain to build API\n * URLs. A second copy of this fallback order is exactly how the CLI and the PM\n * agent would end up querying two different workspaces.\n */\nexport function planeWorkspace(provider: TicketProviderFacts, env: NodeJS.ProcessEnv = process.env, home: string = homedir()): string | undefined {\n const fromManifest = provider.workspace?.trim();\n if (fromManifest) return fromManifest;\n const config = readTemplateConfig(env, home);\n const fromConfig = config ? readTomlScalar(config, \"plane\", \"workspace\")?.trim() : undefined;\n return fromConfig || DEFAULT_PLANE_WORKSPACE;\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Pull a ticket reference out of a branch name.\n *\n * `fix/PJAN-67-mcp-fail-closed` \u2192 `PJAN-67`. CLAUDE.md already requires branch\n * names to carry a ticket reference, so this is the common path rather than a\n * clever edge case.\n *\n * The `\\b` before the identifier is what keeps `XPJAN-3` from reading as\n * `PJAN-3`, and requiring a literal `-` after it keeps `PJANX-3` out too.\n */\nexport function extractTicketRef(branch: string | undefined, identifier: string | undefined): string | undefined {\n if (!branch || !identifier) return undefined;\n const ident = identifier.trim();\n if (!ident) return undefined;\n const match = new RegExp(`\\\\b${escapeRegExp(ident)}-(\\\\d+)\\\\b`, \"i\").exec(branch);\n return match ? `${ident.toUpperCase()}-${match[1]}` : undefined;\n}\n\n/**\n * Normalize user input into a work-item reference.\n *\n * A bare number is completed with this project's identifier. A fully-qualified\n * reference is accepted as-is even when its prefix belongs to another\n * project \u2014 Plane's browse route is workspace-scoped, not project-scoped, so\n * `board DECK-21` resolves correctly from inside the pjangler repo.\n */\nexport function normalizeTicketRef(input: string | undefined, identifier: string | undefined): string | undefined {\n const value = input?.trim();\n if (!value) return undefined;\n if (/^\\d+$/.test(value)) {\n const ident = identifier?.trim();\n return ident ? `${ident.toUpperCase()}-${value}` : undefined;\n }\n const qualified = /^([A-Za-z][A-Za-z0-9]*)-(\\d+)$/.exec(value);\n if (!qualified) return undefined;\n return `${qualified[1]!.toUpperCase()}-${qualified[2]!}`;\n}\n\n/** Explicit ref beats branch inference beats nothing. */\nexport function resolveTicketRef(provider: TicketProviderFacts, options: BoardUrlOptions): string | undefined {\n return (\n normalizeTicketRef(options.ref, provider.identifier) ??\n extractTicketRef(options.branch, provider.identifier)\n );\n}\n\n/**\n * Board URL, or the URL of one work item on it.\n *\n * Route shapes were read off the live instance's router manifest rather than\n * its documentation:\n * `:workspaceSlug/browse/:workItem`\n * `:workspaceSlug/projects/:projectId/issues`\n *\n * Returns undefined rather than guessing when there is no board to point at.\n */\nexport function boardUrl(provider: TicketProviderFacts | undefined, options: BoardUrlOptions = {}): string | undefined {\n if (!provider) return undefined;\n const env = options.env ?? process.env;\n const home = options.home ?? homedir();\n const type = (provider.type || \"plane\").trim().toLowerCase();\n const boardId = provider.board_id?.trim();\n if (!boardId) return undefined;\n\n if (type === \"trello\") {\n // Trello addresses cards by an opaque short id, not by `<IDENT>-<n>`, so a\n // ticket reference cannot be resolved to a card without an API round trip.\n // Opening the board is the honest answer; silently ignoring the ref would\n // not be.\n return `https://trello.com/b/${boardId}`;\n }\n if (type !== \"plane\") return undefined;\n\n const workspace = planeWorkspace(provider, env, home);\n if (!workspace) return undefined;\n const base = planeBase(env, home);\n const ref = resolveTicketRef(provider, options);\n return ref\n ? `${base}/${workspace}/browse/${ref}`\n : `${base}/${workspace}/projects/${boardId}/issues`;\n}\n\n/** Nearest ancestor holding a `.project.json`, starting at `from`. */\nexport function findProjectRoot(from: string): string | undefined {\n let dir = resolve(from);\n for (;;) {\n if (existsSync(join(dir, \".project.json\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\n/** The `ticket_provider` block, or undefined when absent or unreadable. */\nexport function readTicketProvider(root: string): TicketProviderFacts | undefined {\n try {\n const manifest = JSON.parse(readFileSync(join(root, \".project.json\"), \"utf8\")) as Record<string, unknown>;\n const provider = manifest.ticket_provider;\n if (!provider || typeof provider !== \"object\") return undefined;\n return provider as TicketProviderFacts;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Current branch, read straight out of `.git/HEAD`.\n *\n * No subprocess: this runs on the shell-prompt path, where spawning git would\n * cost more than everything else here combined. Handles the `.git`-as-a-file\n * form used by worktrees and submodules. A detached HEAD has no branch, so\n * there is no reference to mine and undefined is the truthful answer.\n */\nexport function currentBranch(from: string): string | undefined {\n try {\n let dir = resolve(from);\n for (;;) {\n const dotgit = join(dir, \".git\");\n if (existsSync(dotgit)) {\n let gitDir = dotgit;\n if (statSync(dotgit).isFile()) {\n const pointer = /^gitdir:\\s*(.+)$/m.exec(readFileSync(dotgit, \"utf8\"));\n if (!pointer) return undefined;\n const target = pointer[1]!.trim();\n gitDir = isAbsolute(target) ? target : resolve(dir, target);\n }\n const head = readFileSync(join(gitDir, \"HEAD\"), \"utf8\").trim();\n const ref = /^ref:\\s*refs\\/heads\\/(.+)$/.exec(head);\n return ref ? ref[1]!.trim() : undefined;\n }\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n } catch {\n return undefined;\n }\n}\n\n/**\n * Full resolution from a working directory: the URL this project's prompt\n * segment points at.\n */\nexport function resolveBoardUrl(cwd: string, ref?: string, env: NodeJS.ProcessEnv = process.env): string | undefined {\n const root = findProjectRoot(cwd);\n if (!root) return undefined;\n const provider = readTicketProvider(root);\n if (!provider) return undefined;\n return boardUrl(provider, { ref, branch: currentBranch(root), env });\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["readFileSync", "join", "statSync", "join", "readFileSync", "join"]
|
|
7
|
-
}
|