CtrlK
BlogDocsLog inGet started
Tessl Logo
PluginPublic

tessl/factory-setup

Set up, understand, and change a Tessl factory on a repository: pick automations that fit how you work, such as shipping tickets as pull requests, answering reviews and fixing checks on those pull requests, code review, and scheduled jobs; prepare the repository for agents; write issues for the factory to build; then see what the factory has done, find out why a run failed or never started, and change or extend it.

Quality

91%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Medium

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

run.mjsskills/prepare-repo/scripts/lib/

// Shared helpers for the evidence collector: running a command without
// throwing, bounding text, and mapping work over a list with a fixed number
// of calls in flight.

import { spawn } from 'node:child_process';
import {
  mkdtempSync,
  openSync,
  closeSync,
  readFileSync,
  rmSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

const DEFAULT_TIMEOUT_MS = 60_000;
const MAX_STDERR = 64 * 1024;

/**
 * Run a command and resolve to `{ ok, stdout, stderr, missing, error }`.
 * Never rejects: a missing binary, a non-zero exit, or a timeout is
 * `ok: false`.
 *
 * Stdout goes to a temporary file rather than a pipe: a CLI that exits
 * straight after one large write to a pipe (the Tessl CLI does) loses the
 * part the pipe had not drained yet, which leaves truncated JSON.
 */
export function run(cmd, args, { cwd, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
  const dir = mkdtempSync(join(tmpdir(), 'prepare-repo-run-'));
  const outPath = join(dir, 'stdout');
  const outFd = openSync(outPath, 'w');
  return new Promise((resolvePromise) => {
    let stderr = '';
    let settled = false;
    const child = spawn(cmd, args, {
      cwd,
      env: process.env,
      stdio: ['ignore', outFd, 'pipe'],
    });
    const timer = setTimeout(() => child.kill('SIGKILL'), timeoutMs);
    child.stderr.on('data', (chunk) => {
      if (stderr.length < MAX_STDERR) stderr += chunk;
    });
    const finish = (result) => {
      if (settled) return;
      settled = true;
      clearTimeout(timer);
      closeSync(outFd);
      let stdout = '';
      try {
        stdout = readFileSync(outPath, 'utf8');
      } catch {
        stdout = '';
      }
      rmSync(dir, { recursive: true, force: true });
      resolvePromise({ stdout, stderr, ...result });
    };
    child.on('error', (error) =>
      finish({
        ok: false,
        missing: error.code === 'ENOENT',
        error: error.code === 'ENOENT' ? `${cmd} not found` : error.message,
      }),
    );
    child.on('close', (code, signal) => {
      if (code === 0) return finish({ ok: true, missing: false, error: null });
      finish({
        ok: false,
        missing: false,
        error: signal ? 'timed out' : errorText(code, stderr),
      });
    });
  });
}

function errorText(code, stderr) {
  const detail = String(stderr ?? '').trim() || `exited with status ${code}`;
  return clip(detail.replace(/\s+/g, ' '), 300);
}

/** Run a command whose stdout is JSON. Resolves to `{ ok, data, error }`. */
export async function runJson(cmd, args, options) {
  const result = await run(cmd, args, options);
  if (!result.ok) return { ok: false, data: null, error: result.error };
  try {
    return { ok: true, data: JSON.parse(result.stdout), error: null };
  } catch {
    return { ok: false, data: null, error: `${cmd} printed invalid JSON` };
  }
}

/** Trim and cut text to at most `max` characters, marking the cut. */
export function clip(text, max) {
  const s = String(text ?? '').trim();
  return s.length > max ? `${s.slice(0, max)}…` : s;
}

/** Map `fn` over `items` with at most `limit` calls in flight, in order. */
export async function mapLimit(items, limit, fn) {
  const results = new Array(items.length);
  let next = 0;
  async function worker() {
    while (next < items.length) {
      const index = next++;
      results[index] = await fn(items[index], index);
    }
  }
  const workers = Array.from({ length: Math.min(limit, items.length) }, worker);
  await Promise.all(workers);
  return results;
}

/** ISO date (YYYY-MM-DD) `days` before `now`. */
export function sinceDate(days, now = new Date()) {
  return new Date(now.getTime() - days * 86_400_000).toISOString().slice(0, 10);
}

/** The result of a source that did not run because a prerequisite is absent. */
export function skipped(reason) {
  return { status: 'skipped', reason };
}

/** The result of a source that tried and could not finish. */
export function failed(reason) {
  return { status: 'failed', reason };
}

README.md

tile.json