app/cli/lib/watchedRepoResolution.ts
zzmq.dev@gmail.com/zz / app / cli / lib / watchedRepoResolution.ts
@ 2a73adc2
Download
Ladder Summary
- file The module implements the logic for determining which GitHub repository slugs the current **zz** session should consider “watched,” combining explicit `--repos` arguments, a logged‑in server‑side list, the user’s `~/.zz/config.json` fallback, and finally a local discovery of running zz processes and the current directory. It prints onboarding guidance when no local repos are found and never performs any cloning itself. **Exported members** - `resolveWatchedRepoSlugs` – async function that returns an array of repo slugs or `null` (the “nothing to show” case) based on the priority rules described above.
// Shared "which repos am I watching" resolution -- originally `zz
// top`'s own logic, extracted so `zz work --assist`'s ask picker (which
// needs this same answer, but must never eagerly clone anything the way
// `zz work`'s normal body does) can reuse it verbatim instead of
// drifting out of sync with `zz top`'s own behavior.
import { execFileSync } from "node:child_process";
import { resolveRepo, isLocallyOnboarded, inferGitHubSlugFromCwd } from "./repo.js";
import { configuredRepos } from "./config.js";
import { apiFetch } from "./api.js";
import { listRunningRepos } from "./processRegistry.js";
import type { BackendMode } from "./backendMode.js";
/** No explicit config/--repos, local mode: union whatever other local `zz work`/`zz review` processes are watching with the current directory, if it's an onboarded GitHub-backed repo. Prints onboarding guidance and returns null (meaning "nothing to show, caller should exit/show empty") rather than ever pretending there's something to watch. */
async function discoverLocalRepos(): Promise<string[] | null> {
const running = listRunningRepos();
const cwdSlug = inferGitHubSlugFromCwd();
const cwdRepo = cwdSlug ? resolveRepo(cwdSlug) : null;
const cwdOnboarded = cwdRepo ? await isLocallyOnboarded(cwdRepo) : false;
const repos = cwdOnboarded && cwdSlug ? [...new Set([...running, cwdSlug])] : running;
if (repos.length > 0) return repos;
if (cwdSlug && !cwdOnboarded) {
console.log(`Nothing to show yet. This directory looks like it's "${cwdSlug}" on GitHub, but it isn't onboarded into zz locally. Run:\n\n zz ladder build ${cwdSlug}\n\nthen try again.`);
} else if (cwdRepo === null && cwdSlugLooksLikeGitRepo()) {
console.log(`Nothing to show yet. This directory is a git repo, but its origin isn't a GitHub remote -- \`zz ladder build\` (zz's only onboarding path right now) needs one. Run \`zz work --local <owner/repo>\`/\`zz review --local <owner/repo>\` directly on a repo you've already onboarded, or use Explore (local mode browses the filesystem) from a terminal where one is nearby.`);
} else {
console.log("Nothing to show yet -- no other local zz process is running, and this directory isn't a git repo. Run `zz work`/`zz review` on a repo, or `zz ladder build owner/repo` to onboard one from GitHub, then try again.");
}
return null;
}
// A plain git-repo check (no GitHub remote needed) just to pick the
// right wording above -- inferGitHubSlugFromCwd() returning null is
// ambiguous between "not a git repo" and "git repo, no GitHub
// remote," and those two deserve different guidance.
function cwdSlugLooksLikeGitRepo(): boolean {
try {
execFileSync("git", ["rev-parse", "--show-toplevel"], { stdio: "ignore" });
return true;
} catch {
return false;
}
}
/**
* Priority: --repos (explicit, always wins) > the server's own
* per-identity list, if logged in (never mixed with a local config file
* that isn't scoped per-identity -- confirmed live: logging in as a
* second account still showed the first account's repos, because
* ~/.zz/config.json was being checked unconditionally before login
* state at all) > ~/.zz/config.json (the fallback for the two cases
* with no server-side list to use instead: remote but not logged in, or
* local mode) > local discovery > empty.
*
* Never clones or otherwise touches disk beyond resolving paths --
* callers that need an actual working tree (zz work's normal body, an
* assist session once an ask is picked) do that themselves, deliberately
* separate from "what am I watching" so browsing/picking never triggers
* it as a side effect.
*
* Returns null only for the local-mode "truly nothing to watch" case
* (discoverLocalRepos already printed onboarding guidance) -- callers
* that would otherwise render a pointless empty view should treat that
* as "print nothing more, exit," same as `zz top` always has; an empty
* array (not logged in remotely, no config) is a normal state worth
* still rendering a view for.
*/
export async function resolveWatchedRepoSlugs(mode: BackendMode, options: { repos?: string[] }): Promise<string[] | null> {
if (options.repos && options.repos.length > 0) return options.repos;
if (mode.useRemote && mode.identity) {
return (await apiFetch<{ repos: { slug: string }[] }>("GET", "/api/me/repos")).repos.map((r) => r.slug);
}
const configured = configuredRepos();
if (configured) return configured;
if (mode.useRemote) return []; // not logged in, no config -- empty dashboard
return discoverLocalRepos();
}