zz_

app/cli/lib/repo.ts

zzmq.dev@gmail.com/zz / app / cli / lib / repo.ts @ 69f3dd4b Download
Ladder Summary
  • file The module supplies a small CLI‑side repository manager that parses a “owner/repo” slug, maps it to a deterministic location under the user’s `~/.zz` directory, and can clone or fast‑forward the repository using the native `git` command line; it also offers helpers to detect whether a repository has already been “onboarded” (i.e. contains the ladder anchor note) and to infer a GitHub slug from the current working directory. **Exported symbols** - `ResolvedRepo` (interface) – describes the resolved paths for a repo. - `parseSlug(slug: string)` – validates and splits a “owner/repo” string. - `resolveRepo(slug: string)` – returns a `ResolvedRepo` with `repoDir` and `stateDir` under `~/.zz`. - `ensureCloned(repo: ResolvedRepo, remoteUrl?: string)` – clones the repo if missing (or re‑clones in remote mode). - `syncClone(repo: ResolvedRepo)` – fetches and hard‑resets the local clone to `origin/main`. - `isLocallyOnboarded(repo: ResolvedRepo): Promise<boolean>` – checks for the ladder anchor note in the repo. - `inferGitHubSlugFromDir(dir?: string): string | null` – walks up to the nearest git repository and extracts a GitHub “owner/repo” slug from its `origin` remote.
 // Shared repo resolution for CLI commands: parses "owner/repo", clones it
 // locally if it isn't already (real `git clone`, not isomorphic-git --
 // the CLI runs on a human's own machine, not in a Worker, so there's no
 // reason not to use the fastest tool available for this one step; only the
 // Ladder's own reads/writes go through the Workers-compatible engine).
 import { execFileSync } from "node:child_process";
 import { existsSync } from "node:fs";
 import { dirname, join } from "node:path";
 import { fileURLToPath } from "node:url";
 
 const here = dirname(fileURLToPath(import.meta.url));
 const appRoot = join(here, "..", "..");
 
 export interface ResolvedRepo {
  slug: string; // "owner/repo"
  name: string; // "repo"
  repoDir: string; // real clone
  stateDir: string; // our own .zz/ state alongside it
 }
 
 export function parseSlug(slug: string): { owner: string; name: string } {
  if (typeof slug !== "string" || !slug) {
  throw new Error(`expected a "repo" argument shaped "owner/repo", got ${JSON.stringify(slug)} -- every tool call that touches a repo needs one, even if you already passed it on an earlier call.`);
  }
  const parts = slug.split("/");
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
  throw new Error(`expected "owner/repo", got "${slug}"`);
  }
  return { owner: parts[0], name: parts[1] };
 }
 
 export function resolveRepo(slug: string): ResolvedRepo {
  const { name } = parseSlug(slug);
  return {
  slug,
  name,
  repoDir: join(appRoot, "demo-repos", name),
  stateDir: join(appRoot, "state", name),
  };
 }
 
 export function ensureCloned(repo: ResolvedRepo): void {
  if (existsSync(repo.repoDir)) return;
  console.error(`cloning ${repo.slug}...`);
  execFileSync("git", ["clone", "--quiet", `https://github.com/${repo.slug}.git`, repo.repoDir], { stdio: "inherit" });
 }