zz_

app/cli/commands/ask-claim.ts

zzmq.dev@gmail.com/zz / app / cli / commands / ask-claim.ts @ 5e5720fb Download
Ladder Summary
  • file The file implements the `zz ask claim` CLI command, which lets a human contributor claim an existing Ask in a repository, creates a new work Lane for that Ask, and prints the Lane ID for subsequent submission. It exports the `AskClaimOptions` interface and the async `askClaim(slug, askId, opts)` function that resolves the user’s identity, calls remote tools to verify the Ask, claim it, and propose a new Lane.
// `zz ask claim <owner/repo> <ask-id>`: the human-contributor
// counterpart to agent/scriptedWork.ts's own claim step
// (runContributorWorkAsk's `ask_claim` call) -- claims an Ask by hand,
// then opens a new Lane for the work ("my workstream", in the demo's
// own words), directly in the same repo (no sharding/forking -- that's
// the remote-autonomous-agent path's own thing, not this one, since a
// human doing this by hand clones and commits straight into the repo
// they were granted access to). Prints the new Lane's id, since
// `zz repo submit` needs it next.
import { resolveIdentity, releaseIdentity } from "../lib/identity.js";
import { setActiveToken } from "../lib/api.js";
import { callRemoteTool } from "../lib/remoteTools.js";
import type { AskEntry, Lane } from "../../engine/types.js";

export interface AskClaimOptions {
  /** Which Lane the ask lives in -- defaults to "_repo", where `zz repo ask` always posts. */
  lane?: string;
  /** --as <name>: claim as a managed agent instead of yourself -- see WorkOptions.as (work.ts) / lib/identity.ts for the full resolution order. */
  as?: string;
}

export async function askClaim(slug: string, askId: string, opts: AskClaimOptions): Promise<void> {
  const identity = await resolveIdentity(opts.as);
  setActiveToken(identity.token);

  let cleanedUp = false;
  const cleanup = async (): Promise<void> => {
    if (cleanedUp) return;
    cleanedUp = true;
    await releaseIdentity(identity);
  };
  process.on("SIGINT", () => void cleanup().then(() => process.exit(0)));
  process.on("SIGTERM", () => void cleanup().then(() => process.exit(0)));

  try {
    const laneId = opts.lane ?? "_repo";
    const lane = (await callRemoteTool(slug, "lane_get", { lane_id: laneId })) as Lane | null;
    if (!lane) throw new Error(`no such lane "${laneId}" in ${slug}`);
    const theAsk = lane.history.find((e): e is AskEntry => e.type === "ASK" && e.id === askId);
    if (!theAsk) throw new Error(`no such ask "${askId}" in ${slug}/${laneId}`);
    const description = (theAsk.payload as { description?: string } | undefined)?.description ?? JSON.stringify(theAsk.payload);

    await callRemoteTool(slug, "ask_claim", { lane_id: laneId, ask_id: askId, actor: identity.username });

    const workLane = (await callRemoteTool(slug, "lane_propose", {
      name: description.slice(0, 60),
      objective: description,
      scope: ["**"],
      actor: identity.username,
      origin_lane: laneId,
    })) as Lane;

    console.log(`Claimed ${askId} and opened Lane ${workLane.id} for it.`);
    console.log(`Next: do the work, \`git add\` it, then \`zz repo submit ${slug} ${workLane.id} ${askId}\`.`);
  } finally {
    await cleanup();
  }
}