zz_

app/cli/zz.ts

zzmq.dev@gmail.com/zz / app / cli / zz.ts @ b4dbd262 Download
Ladder Summary
  • file This file is the main entry point for the **zz** command‑line tool: it loads optional environment variables, registers a local backend, implements custom `--version`/`--simple` handling, and uses **commander** to define the top‑level program and a suite of sub‑commands (e.g., `ladder build`, `ladder dump`, `ladder replay‑commits`, `ladder edit`, `ladder push`, as well as other commands imported elsewhere). All of the imported functions such as `ladderBuild`, `ladderDump`, `ladderReplayCommits`, `ladderEdit`, `ladderPush`, `work`, `login`, etc., are wired into these commands, but the file itself does not export any functions or classes. **Exports:** *none* (the script only runs as a CLI).
 #!/usr/bin/env node
 import { Command } from "commander";
 import { ladderBuild } from "./commands/ladder-build.js";
 import { ladderDump, type DumpFormat } from "./commands/ladder-dump.js";
 import { ladderReplayCommits } from "./commands/ladder-replay-commits.js";
 import { laneLog, type LaneLogFormat } from "./commands/lane-log.js";
+import { work } from "./commands/work.js";
 
 const program = new Command();
 program.name("zz").description("zz: a coordination layer for agentic open source").version("0.1.0");
 
 const ladder = program.command("ladder").description("build and read a repo's Ladder");
 
 ladder
  .command("build <repo>")
  .description("onboard a real GitHub repo (owner/repo) into a Ladder, from its real git history and issue/PR/release data")
  .option("--force", "rebuild the file tree and overwrite any existing asks/releases/contributors instead of leaving them alone")
  .action(async (repo: string, opts: { force?: boolean }) => {
  await ladderBuild(repo, { force: opts.force });
  });
 
 ladder
  .command("dump <repo>")
  .description("materialize an onboarded repo's Ladder for reading")
  .option("--format <format>", "text, json, or html", "text")
  .option("--out <path>", "write to a file instead of stdout")
  .action(async (repo: string, opts: { format: string; out?: string }) => {
  const format = opts.format as DumpFormat;
  if (format !== "text" && format !== "json" && format !== "html") {
  console.error(`unknown format "${opts.format}"; expected text, json, or html`);
  process.exitCode = 1;
  return;
  }
  await ladderDump(repo, { format, out: opts.out });
  });
 
 ladder
  .command("replay-commits <repo>")
  .description("walk the commit log from the root commit forward, one Lane per commit, building changeset/filechange/linechange rungs")
  .option("--limit <n>", "only replay the first N not-yet-onboarded commits", (v) => parseInt(v, 10))
  .option("--force", "re-replay commits that already have a changeset rung")
  .option("--max-files <n>", "commits touching more files than this get a mechanical summary instead of per-file/per-hunk analysis (default: 25)", (v) => parseInt(v, 10))
  .action(async (repo: string, opts: { limit?: number; force?: boolean; maxFiles?: number }) => {
  await ladderReplayCommits(repo, { limit: opts.limit, force: opts.force, maxFiles: opts.maxFiles });
  });
 
 const lane = program.command("lane").description("inspect the Lane/ASK/ANSWER protocol trail");
 
 lane
  .command("log <repo>")
  .description("chronological log of every ASK/ANSWER across every Lane for a repo")
  .option("--format <format>", "text or json", "text")
  .option("--lane <id>", "show only one lane")
  .option("--out <path>", "write to a file instead of stdout")
  .action(async (repo: string, opts: { format: string; lane?: string; out?: string }) => {
  const format = opts.format as LaneLogFormat;
  if (format !== "text" && format !== "json") {
  console.error(`unknown format "${opts.format}"; expected text or json`);
  process.exitCode = 1;
  return;
  }
  await laneLog(repo, { format, lane: opts.lane, out: opts.out });
  });
 
+program
+ .command("work <repo>")
+ .description("run a general agent loop against the zz-git/zz-ladder-lanes tools, working one prose-described Ask")
+ .option("--task <description>", "what to ask the agent to do (default: ingest the whole repo)")
+ .option("--max-turns <n>", "stop after this many tool-calling turns (default: 40)", (v) => parseInt(v, 10))
+ .action(async (repo: string, opts: { task?: string; maxTurns?: number }) => {
+ await work(repo, { task: opts.task, maxTurns: opts.maxTurns });
+ });
+
 program.parseAsync(process.argv);