@clemrig/factory 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -26
- package/dist/app/define.js +1 -1
- package/dist/app/define.js.map +1 -1
- package/dist/app/init.d.ts +1 -0
- package/dist/app/init.js +1 -0
- package/dist/app/init.js.map +1 -1
- package/dist/app/run.d.ts +2 -1
- package/dist/app/run.js +16 -4
- package/dist/app/run.js.map +1 -1
- package/dist/app/workflows/implement.d.ts +1 -1
- package/dist/app/workflows/implement.js +290 -73
- package/dist/app/workflows/implement.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +4 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/init.d.ts +3 -2
- package/dist/cli/init.js +10 -3
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/run.js +1 -1
- package/dist/cli/run.js.map +1 -1
- package/dist/domain/definition.d.ts +2 -0
- package/dist/domain/definition.js +1 -0
- package/dist/domain/definition.js.map +1 -1
- package/dist/domain/description.d.ts +51 -0
- package/dist/domain/description.js +78 -0
- package/dist/domain/description.js.map +1 -0
- package/dist/domain/plan.d.ts +2 -0
- package/dist/domain/plan.js +15 -0
- package/dist/domain/plan.js.map +1 -1
- package/dist/domain/pull-request.d.ts +42 -8
- package/dist/domain/pull-request.js +287 -24
- package/dist/domain/pull-request.js.map +1 -1
- package/dist/domain/review.d.ts +9 -1
- package/dist/domain/review.js +30 -5
- package/dist/domain/review.js.map +1 -1
- package/dist/system/browser.d.ts +29 -0
- package/dist/system/browser.js +84 -0
- package/dist/system/browser.js.map +1 -0
- package/dist/system/github.d.ts +15 -0
- package/dist/system/github.js +36 -2
- package/dist/system/github.js.map +1 -1
- package/dist/system/run-files.d.ts +14 -0
- package/dist/system/run-files.js +69 -2
- package/dist/system/run-files.js.map +1 -1
- package/dist/system/templates.d.ts +3 -2
- package/dist/system/templates.js +8 -3
- package/dist/system/templates.js.map +1 -1
- package/package.json +3 -3
- package/prompts/implement/describe.md +74 -0
- package/prompts/implement/implement.md +25 -8
- package/prompts/implement/plan.md +35 -7
- package/prompts/implement/review.md +43 -16
- package/templates/init/factory.ts +23 -5
package/dist/system/github.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { runProcess } from "@clemrig/flow";
|
|
2
2
|
/** Runs gh in the repository; its output is never journaled, as it may describe the account. */
|
|
3
|
-
async function gh(dir, args, stdin) {
|
|
4
|
-
const result = await runProcess(["gh", ...args], dir,
|
|
3
|
+
async function gh(dir, args, stdin, timeoutMs = 60_000) {
|
|
4
|
+
const result = await runProcess(["gh", ...args], dir, timeoutMs, undefined, stdin);
|
|
5
5
|
if (result.exitCode !== 0)
|
|
6
6
|
throw new Error(`gh ${args.slice(0, 2).join(" ")} failed: ${result.stderr.trim()}`);
|
|
7
7
|
return result.stdout.trim();
|
|
@@ -10,6 +10,19 @@ export async function isAuthenticated(dir) {
|
|
|
10
10
|
const result = await runProcess(["gh", "auth", "status"], dir, 60_000);
|
|
11
11
|
return result.exitCode === 0;
|
|
12
12
|
}
|
|
13
|
+
/** The version of gh, such as `2.102.0`. */
|
|
14
|
+
export async function version(dir) {
|
|
15
|
+
const output = await gh(dir, ["--version"]);
|
|
16
|
+
const found = /^gh version (\d+\.\d+\.\d+)/.exec(output)?.[1];
|
|
17
|
+
if (!found)
|
|
18
|
+
throw new Error(`gh --version printed no version: ${output}`);
|
|
19
|
+
return found;
|
|
20
|
+
}
|
|
21
|
+
/** The repository's GitHub page, and whether anyone can see it. */
|
|
22
|
+
export async function repository(dir) {
|
|
23
|
+
const { url, visibility } = JSON.parse(await gh(dir, ["repo", "view", "--json", "url,visibility"]));
|
|
24
|
+
return { url, public: visibility === "PUBLIC" };
|
|
25
|
+
}
|
|
13
26
|
/** Updates the branch's open pull request, or opens one; returns its URL. */
|
|
14
27
|
export async function openPullRequest(dir, pullRequest) {
|
|
15
28
|
const { base, head, title, body } = pullRequest;
|
|
@@ -49,4 +62,25 @@ export async function openPullRequest(dir, pullRequest) {
|
|
|
49
62
|
"-",
|
|
50
63
|
], body);
|
|
51
64
|
}
|
|
65
|
+
/** Replaces the pull request's body. */
|
|
66
|
+
export async function editBody(dir, pullRequest, body) {
|
|
67
|
+
await gh(dir, ["pr", "edit", pullRequest, "--body-file", "-"], body);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Replaces the pull request's body, uploading the `images` of `folder` it
|
|
71
|
+
* names: gh, run there, points each reference at its upload. When some fail,
|
|
72
|
+
* gh still writes the body, with the others' references unchanged.
|
|
73
|
+
*/
|
|
74
|
+
export async function attachImages(folder, pullRequest, body, images) {
|
|
75
|
+
await gh(folder, [
|
|
76
|
+
"pr",
|
|
77
|
+
"edit",
|
|
78
|
+
pullRequest,
|
|
79
|
+
"--body-file",
|
|
80
|
+
"-",
|
|
81
|
+
...images.flatMap((image) => ["--attach", image]),
|
|
82
|
+
], body,
|
|
83
|
+
// Uploads take longer than the other calls.
|
|
84
|
+
600_000);
|
|
85
|
+
}
|
|
52
86
|
//# sourceMappingURL=github.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/system/github.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,gGAAgG;AAChG,KAAK,UAAU,EAAE,CACf,GAAW,EACX,IAAc,EACd,KAAc;
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/system/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,3 +8,17 @@ export type KeptCheck = CheckResult & {
|
|
|
8
8
|
export declare function keepInRun(runDir: string, path: string, text: string): Promise<string>;
|
|
9
9
|
/** Keeps what each check wrote in `checks/<n>-<name>.log` of the run's directory. */
|
|
10
10
|
export declare function keepCheckOutputs(runDir: string, passed: PassedCheck[]): Promise<KeptCheck[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Keeps what a failed check wrote in `checks/failed-<n>-<name>.log`, `n`
|
|
13
|
+
* counting the run's failures, resumptions included; returns where, absolute.
|
|
14
|
+
*/
|
|
15
|
+
export declare function keepFailedCheck(runDir: string, name: string, output: string): Promise<string>;
|
|
16
|
+
/** The run's folder where the describing agent puts its screenshots, created empty. */
|
|
17
|
+
export declare function screenshotsFolder(runDir: string): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* What is wrong with the screenshots `files` of `folder`: a folder or a file
|
|
20
|
+
* that is a link (which could lead anywhere on the machine), a file missing,
|
|
21
|
+
* not an image, too large, or already named, under another name or another
|
|
22
|
+
* case of it, which `gh` refuses.
|
|
23
|
+
*/
|
|
24
|
+
export declare function screenshotProblems(folder: string, files: string[], maxBytes: number): Promise<string[]>;
|
package/dist/system/run-files.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
1
|
+
import { lstat, mkdir, open, readdir, realpath, writeFile, } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
3
|
/** Keeps `text` in the run's directory, at `path` within it; returns where, absolute. */
|
|
4
4
|
export async function keepInRun(runDir, path, text) {
|
|
5
5
|
const file = join(runDir, path);
|
|
@@ -14,8 +14,75 @@ export function keepCheckOutputs(runDir, passed) {
|
|
|
14
14
|
outputFile: await keepInRun(runDir, `checks/${index + 1}-${safeName(check.name)}.log`, output),
|
|
15
15
|
})));
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Keeps what a failed check wrote in `checks/failed-<n>-<name>.log`, `n`
|
|
19
|
+
* counting the run's failures, resumptions included; returns where, absolute.
|
|
20
|
+
*/
|
|
21
|
+
export async function keepFailedCheck(runDir, name, output) {
|
|
22
|
+
const kept = await readdir(join(runDir, "checks")).catch(() => []);
|
|
23
|
+
const failures = kept.filter((file) => file.startsWith("failed-")).length;
|
|
24
|
+
return keepInRun(runDir, `checks/failed-${failures + 1}-${safeName(name)}.log`, output);
|
|
25
|
+
}
|
|
17
26
|
/** A name with only the characters a file name may safely hold. */
|
|
18
27
|
function safeName(name) {
|
|
19
28
|
return name.replace(/[^A-Za-z0-9._-]+/g, "-");
|
|
20
29
|
}
|
|
30
|
+
/** The run's folder where the describing agent puts its screenshots, created empty. */
|
|
31
|
+
export async function screenshotsFolder(runDir) {
|
|
32
|
+
const folder = join(runDir, "screenshots");
|
|
33
|
+
await mkdir(folder, { recursive: true });
|
|
34
|
+
return folder;
|
|
35
|
+
}
|
|
36
|
+
/** The first bytes of a PNG, JPEG, GIF or WebP image. */
|
|
37
|
+
function isImage(start) {
|
|
38
|
+
const ascii = (from, to) => start.subarray(from, to).toString("latin1");
|
|
39
|
+
return (ascii(0, 8) === "\x89PNG\r\n\x1a\n" ||
|
|
40
|
+
ascii(0, 3) === "\xff\xd8\xff" ||
|
|
41
|
+
ascii(0, 4) === "GIF8" ||
|
|
42
|
+
(ascii(0, 4) === "RIFF" && ascii(8, 12) === "WEBP"));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* What is wrong with the screenshots `files` of `folder`: a folder or a file
|
|
46
|
+
* that is a link (which could lead anywhere on the machine), a file missing,
|
|
47
|
+
* not an image, too large, or already named, under another name or another
|
|
48
|
+
* case of it, which `gh` refuses.
|
|
49
|
+
*/
|
|
50
|
+
export async function screenshotProblems(folder, files, maxBytes) {
|
|
51
|
+
const real = await realpath(folder).catch(() => undefined);
|
|
52
|
+
if (real !== join(await realpath(dirname(folder)), basename(folder)))
|
|
53
|
+
return [`screenshots: ${folder} must be the run's folder, not a link`];
|
|
54
|
+
const problems = [];
|
|
55
|
+
const named = new Map();
|
|
56
|
+
// A name given twice is the description's problem.
|
|
57
|
+
for (const file of new Set(files)) {
|
|
58
|
+
const path = join(folder, file);
|
|
59
|
+
const found = await lstat(path).catch(() => undefined);
|
|
60
|
+
if (!found?.isFile()) {
|
|
61
|
+
problems.push(`screenshots: ${file} is not a file of ${folder}`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const identity = `${found.dev}:${found.ino}`;
|
|
65
|
+
const first = named.get(identity);
|
|
66
|
+
if (first !== undefined) {
|
|
67
|
+
problems.push(`screenshots: ${file} and ${first} are the same file`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
named.set(identity, file);
|
|
71
|
+
if (found.size > maxBytes) {
|
|
72
|
+
problems.push(`screenshots: ${file} has ${found.size} bytes, over ${maxBytes}`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const handle = await open(path);
|
|
76
|
+
const start = Buffer.alloc(12);
|
|
77
|
+
try {
|
|
78
|
+
await handle.read(start, 0, 12, 0);
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
await handle.close();
|
|
82
|
+
}
|
|
83
|
+
if (!isImage(start))
|
|
84
|
+
problems.push(`screenshots: ${file} is not a PNG, JPEG, GIF or WebP image`);
|
|
85
|
+
}
|
|
86
|
+
return problems;
|
|
87
|
+
}
|
|
21
88
|
//# sourceMappingURL=run-files.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-files.js","sourceRoot":"","sources":["../../src/system/run-files.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"run-files.js","sourceRoot":"","sources":["../../src/system/run-files.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,SAAS,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAOpD,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAc,EACd,IAAY,EACZ,IAAY;IAEZ,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,gBAAgB,CAC9B,MAAc,EACd,MAAqB;IAErB,OAAO,OAAO,CAAC,GAAG,CAChB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACjD,GAAG,KAAK;QACR,UAAU,EAAE,MAAM,SAAS,CACzB,MAAM,EACN,UAAU,KAAK,GAAG,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EACjD,MAAM,CACP;KACF,CAAC,CAAC,CACJ,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,IAAY,EACZ,MAAc;IAEd,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1E,OAAO,SAAS,CACd,MAAM,EACN,iBAAiB,QAAQ,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,EACrD,MAAM,CACP,CAAC;AACJ,CAAC;AAED,mEAAmE;AACnE,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC3C,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,yDAAyD;AACzD,SAAS,OAAO,CAAC,KAAa;IAC5B,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAU,EAAE,EAAE,CACzC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC9C,OAAO,CACL,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,mBAAmB;QACnC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,cAAc;QAC9B,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM;QACtB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,IAAI,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,CACpD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,KAAe,EACf,QAAgB;IAEhB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC3D,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAClE,OAAO,CAAC,gBAAgB,MAAM,uCAAuC,CAAC,CAAC;IACzE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,mDAAmD;IACnD,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACvD,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;YACrB,QAAQ,CAAC,IAAI,CAAC,gBAAgB,IAAI,qBAAqB,MAAM,EAAE,CAAC,CAAC;YACjE,SAAS;QACX,CAAC;QACD,MAAM,QAAQ,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,QAAQ,CAAC,IAAI,CAAC,gBAAgB,IAAI,QAAQ,KAAK,oBAAoB,CAAC,CAAC;YACrE,SAAS;QACX,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,KAAK,CAAC,IAAI,GAAG,QAAQ,EAAE,CAAC;YAC1B,QAAQ,CAAC,IAAI,CACX,gBAAgB,IAAI,QAAQ,KAAK,CAAC,IAAI,gBAAgB,QAAQ,EAAE,CACjE,CAAC;YACF,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACrC,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;YACjB,QAAQ,CAAC,IAAI,CACX,gBAAgB,IAAI,wCAAwC,CAC7D,CAAC;IACN,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Creates `.factory/` in `cwd` from the package's templates, its `package.json`
|
|
3
|
-
* pinned to this factory version
|
|
4
|
-
* `.factory/` or skill is refused, with nothing
|
|
3
|
+
* pinned to this factory version and to the agents' browser command line, and
|
|
4
|
+
* the `issue` skill. An existing `.factory/` or skill is refused, with nothing
|
|
5
|
+
* written.
|
|
5
6
|
*/
|
|
6
7
|
export declare function createFromTemplates(cwd: string): Promise<{
|
|
7
8
|
factoryDir: string;
|
package/dist/system/templates.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cp, lstat, mkdir, readFile, readlink, rename, symlink, writeFile, } from "node:fs/promises";
|
|
2
2
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
import { browserPackage } from "./browser.js";
|
|
3
4
|
/** Both `src/system/` and `dist/system/` sit two levels below the package root. */
|
|
4
5
|
const root = new URL("../../", import.meta.url);
|
|
5
6
|
/** The `issue` skill: read by Pi and Codex there, by Claude Code through a folder link. */
|
|
@@ -55,8 +56,9 @@ async function skillPlaces(cwd) {
|
|
|
55
56
|
}
|
|
56
57
|
/**
|
|
57
58
|
* Creates `.factory/` in `cwd` from the package's templates, its `package.json`
|
|
58
|
-
* pinned to this factory version
|
|
59
|
-
* `.factory/` or skill is refused, with nothing
|
|
59
|
+
* pinned to this factory version and to the agents' browser command line, and
|
|
60
|
+
* the `issue` skill. An existing `.factory/` or skill is refused, with nothing
|
|
61
|
+
* written.
|
|
60
62
|
*/
|
|
61
63
|
export async function createFromTemplates(cwd) {
|
|
62
64
|
const factoryDir = join(cwd, ".factory");
|
|
@@ -80,7 +82,10 @@ export async function createFromTemplates(cwd) {
|
|
|
80
82
|
await writeFile(join(factoryDir, "package.json"), `${JSON.stringify({
|
|
81
83
|
private: true,
|
|
82
84
|
type: "module",
|
|
83
|
-
dependencies: {
|
|
85
|
+
dependencies: {
|
|
86
|
+
"@clemrig/factory": version,
|
|
87
|
+
[browserPackage.name]: browserPackage.version,
|
|
88
|
+
},
|
|
84
89
|
}, null, 2)}\n`);
|
|
85
90
|
await mkdir(dirname(skill.path), { recursive: true });
|
|
86
91
|
await cp(new URL("templates/skills/issue/", root), skill.path, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/system/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,EAAE,EACF,KAAK,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,OAAO,EACP,SAAS,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/system/templates.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clemrig/factory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Take a repository's issues from spec to pull request with agents, watch them on a local Kanban, and run the repository's own workflows",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@clemrig/factory-ui": "0.1.0",
|
|
43
|
-
"@clemrig/flow": "0.
|
|
44
|
-
"@clemrig/flow-ui": "0.
|
|
43
|
+
"@clemrig/flow": "0.7.0",
|
|
44
|
+
"@clemrig/flow-ui": "0.5.0"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
You describe an approved change for its pull request. Its reader reviews it days later and remembers nothing of
|
|
2
|
+
the issue: from the pull request alone, they must understand what the change does, how it works, how dangerous it
|
|
3
|
+
is to merge, and see it when it shows. You never change the repository. Write every text of your answer in the
|
|
4
|
+
language of the spec's content, even when the request, the code or these instructions use another; keep code,
|
|
5
|
+
paths and identifiers as they are.
|
|
6
|
+
|
|
7
|
+
Read the repository's agent instructions: `AGENTS.md` at the root and in the directories the change touches.
|
|
8
|
+
Ignore `.factory/`, except the files this request names.
|
|
9
|
+
|
|
10
|
+
The request gives the spec, the plan, the change, the checks that passed, the review that approved it and
|
|
11
|
+
`deviations`, where the implementation departed from the plan:
|
|
12
|
+
|
|
13
|
+
- `changes` lists the changed files with their added and removed lines. A small change comes with its whole diff in
|
|
14
|
+
`diff`; a large one (`large` is true) with `diffFile`, the path of its diff.
|
|
15
|
+
- `checks` gives each check's command and `outputFile`, where the run kept what it wrote.
|
|
16
|
+
|
|
17
|
+
Read the code: describe what the change does, not what anyone said it does. The pull request already shows, from
|
|
18
|
+
the run, the spec's objective, each acceptance criterion with the review's evidence, the checks, the review's
|
|
19
|
+
findings, the deviations and the plan's risks: do not repeat them. When the review still asks for changes, the
|
|
20
|
+
operator delivers the change anyway and the pull request lists them as open: weigh them in `danger`, without
|
|
21
|
+
listing them again.
|
|
22
|
+
|
|
23
|
+
Write for that reader: name things by what they do, an identifier only beside its meaning, and say what each item
|
|
24
|
+
means for the merge. Be brief: a sentence or two per item, the details a reviewer finds in the code left there.
|
|
25
|
+
Size everything to the change. Give as many items as it has, no more: if removing one loses nothing, remove it. A
|
|
26
|
+
one-line fix may have a single change, no visual and a single group of files.
|
|
27
|
+
|
|
28
|
+
Answer with:
|
|
29
|
+
|
|
30
|
+
- `changes`: each change of behavior, as a user, an API client or an operator observes it, one item each, starting
|
|
31
|
+
with a few words that name it. A change to what already existed, beyond what the spec adds, is an item of its
|
|
32
|
+
own that says so: a reviewer must not miss it.
|
|
33
|
+
- `visuals`: the views a reviewer needs to understand how it works; none when the text suffices. Each has a
|
|
34
|
+
`caption`, what it shows in a sentence, and its `content`, without code fences:
|
|
35
|
+
- `mermaid`: how components interact, or how control or data flows, as a `sequenceDiagram` or a `flowchart`
|
|
36
|
+
(`flowchart TD` beyond six nodes, which stays readable). Keep the syntax simple and quote labels that hold
|
|
37
|
+
punctuation: GitHub draws an invalid diagram as an error.
|
|
38
|
+
- `diff`: what changes in a shape that already exists, such as a call tree, a component tree, a file tree or a
|
|
39
|
+
state machine; unchanged lines start with a space, added ones with `+`, removed ones with `-`.
|
|
40
|
+
- `text`: pseudocode, a call tree or a file tree.
|
|
41
|
+
|
|
42
|
+
Pick the smallest view that makes its point, with only the calls, files, states and boundaries that matter.
|
|
43
|
+
- `danger`, what merging risks, a sentence or two per field:
|
|
44
|
+
- `door`: `two_way` when reverting the merge walks the change back; `one_way` when it does not, for instance a
|
|
45
|
+
migration that transforms or deletes data, a published contract or format that others rely on, a message
|
|
46
|
+
sent or an external system changed. `reason`: why.
|
|
47
|
+
- `blastRadius`: one word for how far a defect of this change would reach; `ramifications`: what would break,
|
|
48
|
+
and for whom.
|
|
49
|
+
- `rollback`: how to undo the change once merged: a revert, a data restore, manual steps.
|
|
50
|
+
- `afterMerge`: what the operator must do once it is merged, such as a migration to run, a setting to change or
|
|
51
|
+
a package to publish; empty if nothing.
|
|
52
|
+
- `guide`: `start`, the changed file to read first and why; `groups`, the changed files in the order to read them,
|
|
53
|
+
each group with a short `title` such as "Core logic", "Wiring", "Tests" or "Docs". Give each file once, with its
|
|
54
|
+
path as `changes` gives it; the pull request lists the files you leave out under "Other files".
|
|
55
|
+
- `screenshots`, or, without any, `noScreenshots` with the reason; leave `noScreenshots` out when there are some.
|
|
56
|
+
|
|
57
|
+
Only `visuals` hold code fences' content: write no code fence, of backticks or tildes, or HTML comment anywhere
|
|
58
|
+
else. The pull request's body must stay under 60,000 characters.
|
|
59
|
+
|
|
60
|
+
## Screenshots
|
|
61
|
+
|
|
62
|
+
When the change shows in a web interface, take screenshots of its new state: the screens and states a reviewer
|
|
63
|
+
must see. Start the application as the repository's instructions say. Use the request's `browser` when it gives
|
|
64
|
+
one, a command line (run it with `--help` first), or else the browser tools you have. Do not change data you did
|
|
65
|
+
not create: to show a state, use what the repository's instructions provide for tests, or create your own data.
|
|
66
|
+
Save each image in the folder `screenshots` names, then give its file name, starting with a letter or a digit and
|
|
67
|
+
made of letters, digits, `.`, `-` and `_`, and its caption, in a sentence: PNG, JPEG, GIF or WebP, 10 MB at most,
|
|
68
|
+
50 at most, each a file of its own. Stop everything you started, servers and browsers, before you answer.
|
|
69
|
+
|
|
70
|
+
When `repository.public` is true, anyone can see these images: show only test or seed data, never a secret, a
|
|
71
|
+
token or a person's data; when you cannot, take none and say why.
|
|
72
|
+
|
|
73
|
+
Take none when nothing visible changed, when the application does not start or when you have no browser, and give
|
|
74
|
+
the reason in `noScreenshots`.
|
|
@@ -1,21 +1,38 @@
|
|
|
1
|
-
You implement an approved plan in this repository.
|
|
1
|
+
You implement an approved plan in this repository. Write every text of your answer in the language of the spec's
|
|
2
|
+
content, even when the request, the code or these instructions use another; keep code, paths and identifiers as
|
|
3
|
+
they are.
|
|
2
4
|
|
|
3
5
|
First read the repository's agent instructions: `AGENTS.md` at the root and in each directory you touch.
|
|
4
6
|
Follow their conventions and validation guidance. Ignore `.factory/` and never change it.
|
|
5
7
|
|
|
6
|
-
The request gives the spec, approved and unchanging, and the plan.
|
|
7
|
-
the
|
|
8
|
+
The request gives the spec, approved and unchanging, and the plan. The plan is your guide, not a script: its
|
|
9
|
+
`existing` says what the code already does and must keep doing, and each step's `cases` the situations it must
|
|
10
|
+
handle and test besides its main path. When the code shows a better way than a step describes, take it and say so
|
|
11
|
+
in `deviations`. Implement the spec through the plan, tests included, and run the relevant checks yourself before
|
|
12
|
+
answering.
|
|
13
|
+
|
|
14
|
+
When the change shows in a web interface, check it in a browser before answering. Start the application as the
|
|
15
|
+
repository's instructions say. Use the request's `browser` when it gives one, a command line (run it with `--help`
|
|
16
|
+
first), or else the browser tools you have. Do not change data you did not create: when the application needs
|
|
17
|
+
data, use what the repository's instructions provide for tests, or create your own. Stop everything you started,
|
|
18
|
+
servers and browsers, before you answer.
|
|
8
19
|
|
|
9
20
|
Never commit, switch branches or push: the run commits, pushes and opens the pull request itself, and it stops
|
|
10
21
|
if you commit or switch branches.
|
|
11
22
|
|
|
12
|
-
When the request contains `feedback`, it reports what failed after your previous answer, a check or the
|
|
13
|
-
|
|
23
|
+
When the request contains `feedback`, it reports what failed after your previous answer, a check or the review.
|
|
24
|
+
First find why the change let each defect happen. When defects come from the same mechanism, such as a state
|
|
25
|
+
machine, the order locks are taken in or the way texts are built, or when a fix would add a special case for one
|
|
26
|
+
situation, change the mechanism instead, reworking that part of your change if needed: prefer the fix that leaves
|
|
27
|
+
less code and fewer special cases, a guard per case is the sign of a wrong mechanism. Fix each defect wherever the
|
|
28
|
+
change has it, not only where the feedback points, then test each place. The review
|
|
29
|
+
may add findings that do not block: fix them too when that is safe and stays within the spec, and say in your
|
|
30
|
+
summary which you left and why. Then answer again.
|
|
14
31
|
|
|
15
32
|
Answer with:
|
|
16
33
|
|
|
17
34
|
- `summary`: what you changed, in a few sentences.
|
|
18
35
|
- `criteria`: each acceptance criterion once, with the evidence that it is met (tests, files).
|
|
19
|
-
- `deviations`: where
|
|
20
|
-
|
|
21
|
-
|
|
36
|
+
- `deviations`: every place where the change departs from the plan, since the plan and not only in your last
|
|
37
|
+
answer, and why; empty if nowhere. The pull request shows them to a reader who knows neither the plan nor the
|
|
38
|
+
code: a sentence or two each, naming things by what they do, an identifier only beside its meaning.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
You plan the implementation of an approved spec in this repository. You change nothing.
|
|
1
|
+
You plan the implementation of an approved spec in this repository. You change nothing. Write every text of your
|
|
2
|
+
answer in the language of the spec's content, except `commit`, in English, even when the request, the code or
|
|
3
|
+
these instructions use another; keep code, paths and identifiers as they are.
|
|
2
4
|
|
|
3
5
|
Before planning, read the repository's agent instructions: `AGENTS.md` at the root and in each directory the
|
|
4
6
|
change will touch, and follow them. Ignore `.factory/`: the other issues there are history, not the current
|
|
@@ -8,13 +10,39 @@ The request gives the issue's slug and its spec. The spec is approved and does n
|
|
|
8
10
|
nothing more. When something is unclear, choose the interpretation most consistent with the code and record it
|
|
9
11
|
in `risks`.
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## Explore first
|
|
14
|
+
|
|
15
|
+
Before writing a step, find out how the code works where the spec changes it. For each area the spec touches,
|
|
16
|
+
such as an application, a layer or a feature, establish from the code itself, not only its documentation:
|
|
17
|
+
|
|
18
|
+
- the mechanisms involved and their states and transitions: a job's life, a queue, a cache, a shared resource;
|
|
19
|
+
- their invariants: transactions, locks and the order they are taken in, idempotence, what must stay consistent;
|
|
20
|
+
- the conventions the change must follow: texts and their plural forms, errors, API shapes, migrations;
|
|
21
|
+
- the tests and tools that prove such behavior in this repository.
|
|
22
|
+
|
|
23
|
+
When the spec touches several areas and you can launch sub-agents, give each, five at most, one area: the spec,
|
|
24
|
+
the questions above for that area, and the instruction to report facts with the file and symbol they come from,
|
|
25
|
+
to change nothing and to launch no agent itself. Check against the code the facts the plan depends on.
|
|
26
|
+
|
|
27
|
+
## The plan
|
|
28
|
+
|
|
29
|
+
The plan guides the implementation; it does not write it. Say what must be true and where, precisely enough that
|
|
30
|
+
nothing is forgotten: name a function, a file or an approach only when the code or the spec settles it, and leave
|
|
31
|
+
the other choices to the implementing agent, who reads the code as it goes and may find a better way.
|
|
32
|
+
|
|
33
|
+
Answer with:
|
|
12
34
|
|
|
13
35
|
- `summary`: the approach, in a few sentences.
|
|
14
|
-
- `
|
|
15
|
-
|
|
36
|
+
- `existing`: what the code already does that the change builds on or must keep, as the exploration found it:
|
|
37
|
+
each mechanism, invariant, convention or test tool with where it lives; empty when the change depends on
|
|
38
|
+
nothing of the kind.
|
|
39
|
+
- `steps`: each names the acceptance criteria it serves (`AC1`…), the files you expect it to create or change,
|
|
40
|
+
`cases` and the tests that prove it. Every acceptance criterion is served by at least one step.
|
|
41
|
+
- `cases`: the situations the step must handle besides its main path, each with what must happen: failures,
|
|
42
|
+
interruptions and recoveries, concurrent requests, a resource that changes or disappears meanwhile, none, one
|
|
43
|
+
or many items, permissions. Only those the step meets; empty when there are none.
|
|
16
44
|
- `commit`: the conventional commit subject of the whole change, for example
|
|
17
45
|
`feat(api): export watchlists as CSV`, at most 72 characters.
|
|
18
|
-
- `risks`: what could go wrong and what you assumed; empty if nothing.
|
|
19
|
-
|
|
20
|
-
|
|
46
|
+
- `risks`: what could go wrong and what you assumed; empty if nothing. The pull request shows them to a reader
|
|
47
|
+
who knows neither the spec nor the code: a sentence or two each, naming things by what they do, an identifier
|
|
48
|
+
only beside its meaning, and what each means for the change.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
You review a change against its approved spec, independently. You never change the repository.
|
|
1
|
+
You review a change against its approved spec, independently. You never change the repository. Write every text
|
|
2
|
+
of your answer in the language of the spec's content, even when the request, the code or these instructions use
|
|
3
|
+
another; keep code, paths and identifiers as they are.
|
|
2
4
|
|
|
3
5
|
Read the repository's agent instructions: `AGENTS.md` at the root and in the directories the change touches.
|
|
4
6
|
Ignore `.factory/`, except the files this request names: it holds other issues' history and agents' outputs, which
|
|
@@ -12,32 +14,57 @@ The request gives the spec, the change and the repository's checks, which all pa
|
|
|
12
14
|
|
|
13
15
|
Do not take anything on trust: verify each acceptance criterion in the code and the tests, reading files beyond the
|
|
14
16
|
diff when needed. Look for unmet criteria, defects, regressions, missing or weak tests, security problems and
|
|
15
|
-
departures from the repository's conventions. Follow every path the change must handle, failures, interruptions
|
|
16
|
-
and recoveries included, not only the one that succeeds
|
|
17
|
+
departures from the repository's conventions. Follow every path the change must handle, failures, interruptions,
|
|
18
|
+
concurrent requests and recoveries included, not only the one that succeeds: for each mechanism the change touches,
|
|
19
|
+
such as a state machine, a queue, a lock or a cache, go through each of its states and transitions.
|
|
20
|
+
|
|
21
|
+
When you find a defect, look for the same defect everywhere in the change: every text that shows a count, every
|
|
22
|
+
path that takes the same locks, every transition of the same mechanism. Report it once, naming each place, so that
|
|
23
|
+
it is fixed everywhere at once; when several defects come from one mechanism, report the mechanism, with the
|
|
24
|
+
defects as its occurrences: the fix belongs there, not in a guard per case.
|
|
17
25
|
|
|
18
26
|
If you can run commands, run what helps you judge: targeted tests, the scripts the change adds, small
|
|
19
|
-
reproductions.
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
reproductions. When the change shows in a web interface, check it in a browser: start the application as the
|
|
28
|
+
repository's instructions say, and use the request's `browser` when it gives one, a command line (run it with
|
|
29
|
+
`--help` first), or else the browser tools you have. Do not change data you did not create: when the application
|
|
30
|
+
needs data, use what the repository's instructions provide for tests, or create your own. Write temporary files
|
|
31
|
+
only in the system's temporary directory, and stop everything you started, servers and browsers, before you
|
|
32
|
+
answer. Never modify, stage, commit or delete anything in the repository: the run compares it before and after
|
|
33
|
+
your review, restores what changed and starts the review again.
|
|
22
34
|
|
|
23
|
-
When the change is large
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
When the change is large (`large` is true), or spans several applications or layers, and you can launch
|
|
36
|
+
sub-agents, split the review between them, five at most: each takes one part of the change, such as an
|
|
37
|
+
application, a layer or a feature, and one more takes the paths that cross the parts: failures, interruptions,
|
|
38
|
+
concurrent requests and recoveries. Give each the spec, its part, the files concerned and where to read the diff,
|
|
39
|
+
and tell it to report its findings with their evidence, to look for each defect's other occurrences, to change
|
|
40
|
+
nothing and to launch no agent itself. Check their reports, then give one answer for the whole change.
|
|
28
41
|
|
|
29
42
|
Answer with:
|
|
30
43
|
|
|
31
|
-
- `criteria`: each acceptance criterion once, `met` or `not_met`, with its evidence (file,
|
|
44
|
+
- `criteria`: each acceptance criterion once, `met` or `not_met`, with its evidence: the decisive proof (file,
|
|
45
|
+
test, behavior), without narrating how you found it. The pull request shows it to its reader.
|
|
32
46
|
- `findings`: each with its `nature`, its `file` when relevant and a `summary` that states the trigger, then the
|
|
33
|
-
consequence
|
|
47
|
+
consequence, for a reader who has not read the code: name things by what they do, an identifier only beside its
|
|
48
|
+
meaning. The nature alone decides whether the finding blocks the change:
|
|
34
49
|
- blocking: `incorrect_behavior` (the change does the wrong thing in a situation it must handle), `data_loss`,
|
|
35
50
|
`outage`, `security`, `regression` (something that worked no longer does), `spec_violation` (the change
|
|
36
51
|
contradicts the spec's scope or constraints);
|
|
37
52
|
- not blocking: `weak_test`, `maintainability`, `documentation`.
|
|
38
53
|
|
|
39
54
|
A defect whose trigger is a situation the change must handle, as the spec and the change's own paths (a
|
|
40
|
-
recovery, say) define them, is `incorrect_behavior` however rare that situation is
|
|
41
|
-
describes the defect, not the severity you would
|
|
55
|
+
recovery, say) define them, is `incorrect_behavior` however rare that situation is; so is a text the user reads
|
|
56
|
+
that is wrong, such as a missing plural. Choose the nature that describes the defect, not the severity you would
|
|
57
|
+
like it to have.
|
|
58
|
+
|
|
59
|
+
## The previous review
|
|
60
|
+
|
|
61
|
+
From the second round on, once you have answered, a request in this conversation gives `previous`, the review of
|
|
62
|
+
the change's previous version. Check each unmet criterion and finding it gave against the code as it is now: when
|
|
63
|
+
the change fixes it, put it in `resolved`, with its `summary` and the `evidence` of the fix; when it still holds,
|
|
64
|
+
keep it in `findings`, with the same nature unless the code shows that nature was wrong, which its summary then
|
|
65
|
+
says. Look at how each was fixed: a fix that adds a special case where the mechanism should change, or that makes
|
|
66
|
+
the change harder to follow without need, is a finding, `maintainability`, or the defect's own nature when the
|
|
67
|
+
mechanism still fails elsewhere. Then answer your whole review again: every criterion, and your findings with the
|
|
68
|
+
previous ones that still hold, each once.
|
|
42
69
|
|
|
43
|
-
The change is approved only when every criterion is met and no finding is blocking.
|
|
70
|
+
The change is approved only when every criterion is met and no finding is blocking.
|
|
@@ -7,10 +7,21 @@ const opus = {
|
|
|
7
7
|
model: "claude-opus-5-5",
|
|
8
8
|
thinking: "high",
|
|
9
9
|
} as const;
|
|
10
|
-
|
|
10
|
+
// Explores the code the spec changes, handing areas of a large one to
|
|
11
|
+
// sub-agents (Agent), then plans.
|
|
12
|
+
const planner = {
|
|
11
13
|
...opus,
|
|
12
|
-
tools: ["Read", "Grep", "Glob"],
|
|
13
|
-
timeoutMs:
|
|
14
|
+
tools: ["Read", "Grep", "Glob", "Agent"],
|
|
15
|
+
timeoutMs: 30 * minutes,
|
|
16
|
+
} satisfies AgentProfile;
|
|
17
|
+
// Reads the change and runs what checks it: tests, the application in a
|
|
18
|
+
// browser (Playwright CLI, installed in .factory/). The run puts back what it
|
|
19
|
+
// changes in the repository.
|
|
20
|
+
const checker = {
|
|
21
|
+
...opus,
|
|
22
|
+
tools: ["Read", "Grep", "Glob", "Bash"],
|
|
23
|
+
permissionMode: "dontAsk",
|
|
24
|
+
timeoutMs: 30 * minutes,
|
|
14
25
|
} satisfies AgentProfile;
|
|
15
26
|
|
|
16
27
|
/**
|
|
@@ -24,7 +35,7 @@ export default defineFactory({
|
|
|
24
35
|
// The budget of an implement run, in USD; --max-cost-usd changes it for one run.
|
|
25
36
|
maxCostUsd: 25,
|
|
26
37
|
profiles: {
|
|
27
|
-
plan:
|
|
38
|
+
plan: planner,
|
|
28
39
|
implement: {
|
|
29
40
|
...opus,
|
|
30
41
|
tools: ["Read", "Grep", "Glob", "Edit", "Write", "Bash"],
|
|
@@ -32,7 +43,14 @@ export default defineFactory({
|
|
|
32
43
|
permissionMode: "dontAsk",
|
|
33
44
|
timeoutMs: 60 * minutes,
|
|
34
45
|
},
|
|
35
|
-
review
|
|
46
|
+
// Splits the review of a large change between sub-agents (Agent).
|
|
47
|
+
review: {
|
|
48
|
+
...checker,
|
|
49
|
+
tools: [...checker.tools, "Agent"],
|
|
50
|
+
timeoutMs: 60 * minutes,
|
|
51
|
+
},
|
|
52
|
+
// Writes the pull request and takes its screenshots.
|
|
53
|
+
describe: checker,
|
|
36
54
|
},
|
|
37
55
|
// This repository's own workflows, run with `factory run <name>`.
|
|
38
56
|
workflows: [ask],
|