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.
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
// Open Tessl findings for the project this directory is linked to.
import { clip, failed, run, runJson, skipped } from '../lib/run.mjs';
const PAGE_SIZE = 100;
const TEXT_CHARS = 600;
/** The linked project id from `tessl project repair --json`, or a reason. */
export async function linkedProject(root) {
// Without flags, `project repair` only diagnoses the link; it changes nothing.
const result = await run('tessl', ['project', 'repair', '--json'], {
cwd: root,
});
if (result.missing) return { reason: 'tessl is not installed' };
let parsed;
try {
parsed = JSON.parse(result.stdout);
} catch {
return {
reason: `could not read the project link: ${result.error ?? 'invalid JSON'}`,
};
}
const id = parsed.linkedProject?.id;
if (!id)
return {
reason:
parsed.message ?? 'this directory is not linked to a Tessl project',
};
return { id, name: parsed.linkedProject.name ?? null };
}
function summarize(finding) {
const a = finding.attributes ?? {};
const evidence = (a.observations ?? []).flatMap((o) => o.evidence ?? []);
return {
id: finding.id,
title: a.title,
analysis: clip(a.analysis, TEXT_CHARS),
recommendation: clip(a.recommendation, TEXT_CHARS),
evidence: evidence.slice(0, 10),
createdAt: a.createdAt,
};
}
/** Open findings, newest first, up to `maxFindings`, reading every page. */
export async function collectFindings(ctx) {
const project = await linkedProject(ctx.root);
if (!project.id) return skipped(project.reason);
const items = [];
let after = null;
let more = true;
while (more && items.length < ctx.limits.maxFindings) {
const args = [
'finding',
'list',
'--project',
project.id,
'--state',
'open',
'--json',
'--limit',
String(Math.min(PAGE_SIZE, ctx.limits.maxFindings)),
];
if (after) args.push('--after', after);
const page = await runJson('tessl', args, { cwd: ctx.root });
if (!page.ok) return failed(`tessl finding list failed: ${page.error}`);
items.push(...(page.data.data ?? []).map(summarize));
const next = page.data.links?.next;
after = next ? new URL(next).searchParams.get('page[after]') : null;
more = Boolean(after);
}
return {
status: 'ok',
project,
truncated: more || items.length > ctx.limits.maxFindings,
items: items.slice(0, ctx.limits.maxFindings),
};
}