@clemrig/factory 0.2.1 → 0.4.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 +127 -37
- package/dist/app/board.d.ts +7 -0
- package/dist/app/board.js +47 -0
- package/dist/app/board.js.map +1 -0
- package/dist/app/define.d.ts +27 -0
- package/dist/app/define.js +68 -0
- package/dist/app/define.js.map +1 -0
- package/dist/app/doctor.js +5 -4
- package/dist/app/doctor.js.map +1 -1
- package/dist/app/factory.js +10 -2
- package/dist/app/factory.js.map +1 -1
- package/dist/app/run.js +1 -1
- package/dist/app/run.js.map +1 -1
- package/dist/app/workflows/implement.d.ts +8 -0
- package/dist/app/workflows/implement.js +268 -0
- package/dist/app/workflows/implement.js.map +1 -0
- package/dist/app/workflows/issue-save.d.ts +2 -0
- package/dist/app/workflows/issue-save.js +35 -0
- package/dist/app/workflows/issue-save.js.map +1 -0
- package/dist/cli/application.js +1 -1
- package/dist/cli/application.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +5 -3
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/init.d.ts +2 -2
- package/dist/cli/init.js +4 -4
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/reporter.js +42 -15
- package/dist/cli/reporter.js.map +1 -1
- package/dist/cli/ui.d.ts +5 -2
- package/dist/cli/ui.js +20 -7
- package/dist/cli/ui.js.map +1 -1
- package/dist/domain/board.d.ts +65 -0
- package/dist/domain/board.js +95 -0
- package/dist/domain/board.js.map +1 -0
- package/dist/domain/definition.d.ts +15 -0
- package/dist/domain/definition.js +28 -0
- package/dist/domain/definition.js.map +1 -0
- package/dist/domain/delivery.d.ts +9 -0
- package/dist/domain/delivery.js +10 -0
- package/dist/domain/delivery.js.map +1 -0
- package/dist/domain/issue.d.ts +11 -0
- package/dist/domain/issue.js +22 -0
- package/dist/domain/issue.js.map +1 -0
- package/dist/domain/parsed.d.ts +8 -0
- package/dist/domain/parsed.js +2 -0
- package/dist/domain/parsed.js.map +1 -0
- package/dist/domain/plan.d.ts +24 -0
- package/dist/domain/plan.js +49 -0
- package/dist/domain/plan.js.map +1 -0
- package/dist/domain/pull-request.d.ts +17 -0
- package/dist/domain/pull-request.js +32 -0
- package/dist/domain/pull-request.js.map +1 -0
- package/dist/domain/review.d.ts +36 -0
- package/dist/domain/review.js +45 -0
- package/dist/domain/review.js.map +1 -0
- package/dist/domain/spec.d.ts +18 -0
- package/dist/domain/spec.js +118 -0
- package/dist/domain/spec.js.map +1 -0
- package/dist/domain/verification.d.ts +16 -0
- package/dist/domain/verification.js +11 -0
- package/dist/domain/verification.js.map +1 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/system/checks.d.ts +32 -0
- package/dist/system/checks.js +49 -0
- package/dist/system/checks.js.map +1 -0
- package/dist/system/factory.d.ts +7 -5
- package/dist/system/factory.js +19 -19
- package/dist/system/factory.js.map +1 -1
- package/dist/system/git.d.ts +25 -0
- package/dist/system/git.js +73 -0
- package/dist/system/git.js.map +1 -0
- package/dist/system/github.d.ts +8 -0
- package/dist/system/github.js +52 -0
- package/dist/system/github.js.map +1 -0
- package/dist/system/issues.d.ts +24 -0
- package/dist/system/issues.js +66 -0
- package/dist/system/issues.js.map +1 -0
- package/dist/system/lock.d.ts +8 -0
- package/dist/system/lock.js +35 -0
- package/dist/system/lock.js.map +1 -0
- package/dist/system/templates.d.ts +2 -1
- package/dist/system/templates.js +69 -3
- package/dist/system/templates.js.map +1 -1
- package/package.json +6 -4
- package/prompts/implement/implement.md +21 -0
- package/prompts/implement/plan.md +20 -0
- package/prompts/implement/review.md +17 -0
- package/templates/init/factory.ts +37 -2
- package/templates/init/workflows/ask/index.ts +2 -1
- package/templates/skills/issue/SKILL.md +82 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const sectionHeadings = [
|
|
2
|
+
"Objective",
|
|
3
|
+
"Scope",
|
|
4
|
+
"Out of scope",
|
|
5
|
+
"Acceptance criteria",
|
|
6
|
+
"Constraints",
|
|
7
|
+
];
|
|
8
|
+
/** Reads spec.md: a `# ` title, then exactly the five sections, in order. */
|
|
9
|
+
export function parseSpec(text) {
|
|
10
|
+
const [first = "", ...lines] = text.trim().split(/\r?\n/);
|
|
11
|
+
const title = /^# (.*)$/.exec(first)?.[1]?.trim() ?? "";
|
|
12
|
+
const preamble = [];
|
|
13
|
+
const headings = [];
|
|
14
|
+
const sections = new Map();
|
|
15
|
+
let current = preamble;
|
|
16
|
+
for (const line of lines) {
|
|
17
|
+
const heading = /^## (.*)$/.exec(line)?.[1]?.trim();
|
|
18
|
+
if (heading === undefined)
|
|
19
|
+
current.push(line);
|
|
20
|
+
else {
|
|
21
|
+
current = [];
|
|
22
|
+
headings.push(heading);
|
|
23
|
+
sections.set(heading, current);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const body = (heading) => (sections.get(heading) ?? []).join("\n").trim();
|
|
27
|
+
const { criteria, problems: criteriaProblems } = parseCriteria(sections.get("Acceptance criteria") ?? []);
|
|
28
|
+
const problems = [
|
|
29
|
+
...(title ? [] : ["The first line must be a title: # <Title>"]),
|
|
30
|
+
...(preamble.join("").trim()
|
|
31
|
+
? ["Unexpected text before the first section"]
|
|
32
|
+
: []),
|
|
33
|
+
...sectionProblems(headings),
|
|
34
|
+
...sectionHeadings
|
|
35
|
+
.filter((heading) => sections.has(heading) && !body(heading))
|
|
36
|
+
.map((heading) => `Empty section: ## ${heading}`),
|
|
37
|
+
...criteriaProblems,
|
|
38
|
+
];
|
|
39
|
+
if (problems.length > 0)
|
|
40
|
+
return { ok: false, problems };
|
|
41
|
+
return {
|
|
42
|
+
ok: true,
|
|
43
|
+
value: {
|
|
44
|
+
title,
|
|
45
|
+
objective: body("Objective"),
|
|
46
|
+
scope: body("Scope"),
|
|
47
|
+
outOfScope: body("Out of scope"),
|
|
48
|
+
criteria,
|
|
49
|
+
constraints: body("Constraints"),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Exactly the expected headings, each once, in order. */
|
|
54
|
+
function sectionProblems(headings) {
|
|
55
|
+
const unknown = headings.filter((heading) => !sectionHeadings.includes(heading));
|
|
56
|
+
const duplicate = sectionHeadings.filter((heading) => headings.indexOf(heading) !== headings.lastIndexOf(heading));
|
|
57
|
+
const missing = sectionHeadings.filter((heading) => !headings.includes(heading));
|
|
58
|
+
const problems = [
|
|
59
|
+
...[...new Set(unknown)].map((heading) => `Unknown section: ## ${heading}`),
|
|
60
|
+
...duplicate.map((heading) => `Duplicate section: ## ${heading}`),
|
|
61
|
+
...missing.map((heading) => `Missing section: ## ${heading}`),
|
|
62
|
+
];
|
|
63
|
+
if (problems.length === 0 &&
|
|
64
|
+
headings.join("\n") !== sectionHeadings.join("\n"))
|
|
65
|
+
problems.push(`Sections must appear in this order: ${sectionHeadings.join(", ")}`);
|
|
66
|
+
return problems;
|
|
67
|
+
}
|
|
68
|
+
/** `- AC<n>: <text>` items numbered from 1; an indented line continues the item above it. */
|
|
69
|
+
function parseCriteria(lines) {
|
|
70
|
+
const criteria = [];
|
|
71
|
+
const problems = [];
|
|
72
|
+
let open;
|
|
73
|
+
const close = () => {
|
|
74
|
+
if (open && !open.text)
|
|
75
|
+
problems.push(`Acceptance criteria: ${open.id} is empty`);
|
|
76
|
+
open = undefined;
|
|
77
|
+
};
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
if (!line.trim())
|
|
80
|
+
continue;
|
|
81
|
+
const item = /^- (AC\d+):(.*)$/.exec(line);
|
|
82
|
+
if (item) {
|
|
83
|
+
close();
|
|
84
|
+
const [, id = "", text = ""] = item;
|
|
85
|
+
const expected = `AC${criteria.length + 1}`;
|
|
86
|
+
if (id !== expected)
|
|
87
|
+
problems.push(`Acceptance criteria: expected ${expected}, found ${id}`);
|
|
88
|
+
open = { id, text: text.trim() };
|
|
89
|
+
criteria.push(open);
|
|
90
|
+
}
|
|
91
|
+
else if (open && /^\s/.test(line))
|
|
92
|
+
open.text = `${open.text} ${line.trim()}`.trim();
|
|
93
|
+
else {
|
|
94
|
+
close();
|
|
95
|
+
problems.push(`Acceptance criteria: unexpected line "${line.trim()}" (write "- AC<n>: <criterion>")`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
close();
|
|
99
|
+
return { criteria, problems };
|
|
100
|
+
}
|
|
101
|
+
/** What is wrong with the criteria a response cites: all of the spec's, no other, and each once if required. */
|
|
102
|
+
export function citationProblem(cited, criteria, once = false) {
|
|
103
|
+
const ids = criteria.map((criterion) => criterion.id);
|
|
104
|
+
const missing = ids.filter((id) => !cited.includes(id));
|
|
105
|
+
const unknown = [...new Set(cited.filter((id) => !ids.includes(id)))];
|
|
106
|
+
const repeated = once
|
|
107
|
+
? [...new Set(cited.filter((id, index) => cited.indexOf(id) !== index))]
|
|
108
|
+
: [];
|
|
109
|
+
const problems = [
|
|
110
|
+
...(missing.length > 0 ? [`missing ${missing.join(", ")}`] : []),
|
|
111
|
+
...(unknown.length > 0 ? [`unknown ${unknown.join(", ")}`] : []),
|
|
112
|
+
...(repeated.length > 0 ? [`repeated ${repeated.join(", ")}`] : []),
|
|
113
|
+
];
|
|
114
|
+
return problems.length > 0
|
|
115
|
+
? `Acceptance criteria: ${problems.join("; ")}`
|
|
116
|
+
: undefined;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/domain/spec.ts"],"names":[],"mappings":"AAiBA,MAAM,eAAe,GAAG;IACtB,WAAW;IACX,OAAO;IACP,cAAc;IACd,qBAAqB;IACrB,aAAa;CACd,CAAC;AAEF,6EAA6E;AAC7E,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,CAAC,KAAK,GAAG,EAAE,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC7C,IAAI,OAAO,GAAG,QAAQ,CAAC;IACvB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACpD,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACzC,CAAC;YACJ,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,EAAE,CAC/B,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,aAAa,CAC5D,QAAQ,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAC1C,CAAC;IACF,MAAM,QAAQ,GAAG;QACf,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,2CAA2C,CAAC,CAAC;QAC/D,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE;YAC1B,CAAC,CAAC,CAAC,0CAA0C,CAAC;YAC9C,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,eAAe,CAAC,QAAQ,CAAC;QAC5B,GAAG,eAAe;aACf,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;aAC5D,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,qBAAqB,OAAO,EAAE,CAAC;QACnD,GAAG,gBAAgB;KACpB,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACxD,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK,EAAE;YACL,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC;YAC5B,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC;YACpB,UAAU,EAAE,IAAI,CAAC,cAAc,CAAC;YAChC,QAAQ;YACR,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC;SACjC;KACF,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,SAAS,eAAe,CAAC,QAAkB;IACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAC7B,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAChD,CAAC;IACF,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,CACtC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,CACzE,CAAC;IACF,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CACpC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CACzC,CAAC;IACF,MAAM,QAAQ,GAAG;QACf,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAuB,OAAO,EAAE,CAAC;QAC3E,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,yBAAyB,OAAO,EAAE,CAAC;QACjE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAuB,OAAO,EAAE,CAAC;KAC9D,CAAC;IACF,IACE,QAAQ,CAAC,MAAM,KAAK,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAElD,QAAQ,CAAC,IAAI,CACX,uCAAuC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpE,CAAC;IACJ,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6FAA6F;AAC7F,SAAS,aAAa,CAAC,KAAe;IAIpC,MAAM,QAAQ,GAAgB,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,IAA2B,CAAC;IAChC,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI;YACpB,QAAQ,CAAC,IAAI,CAAC,wBAAwB,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC;QAC5D,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAC3B,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,EAAE,CAAC;YACR,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;YACpC,MAAM,QAAQ,GAAG,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,IAAI,EAAE,KAAK,QAAQ;gBACjB,QAAQ,CAAC,IAAI,CAAC,iCAAiC,QAAQ,WAAW,EAAE,EAAE,CAAC,CAAC;YAC1E,IAAI,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACjC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YACjC,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;aAC9C,CAAC;YACJ,KAAK,EAAE,CAAC;YACR,QAAQ,CAAC,IAAI,CACX,yCAAyC,IAAI,CAAC,IAAI,EAAE,kCAAkC,CACvF,CAAC;QACJ,CAAC;IACH,CAAC;IACD,KAAK,EAAE,CAAC;IACR,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,eAAe,CAC7B,KAAe,EACf,QAAqB,EACrB,IAAI,GAAG,KAAK;IAEZ,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,IAAI;QACnB,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC;QACxE,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,QAAQ,GAAG;QACf,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC;IACF,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC;QACxB,CAAC,CAAC,wBAAwB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC/C,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** A command of the repository that must succeed, as its definition declares it. */
|
|
2
|
+
export interface VerifyCommand {
|
|
3
|
+
name: string;
|
|
4
|
+
command: string[];
|
|
5
|
+
timeoutMs: number;
|
|
6
|
+
}
|
|
7
|
+
/** A check that passed, as the pull request reports it. */
|
|
8
|
+
export interface CheckResult {
|
|
9
|
+
name: string;
|
|
10
|
+
command: string[];
|
|
11
|
+
durationMs: number;
|
|
12
|
+
}
|
|
13
|
+
/** The correction sent to the implementing agent: the failed check, its cause and the end of its output. */
|
|
14
|
+
export declare function failureFeedback(check: VerifyCommand, cause: string, output: string): string;
|
|
15
|
+
/** A duration as the terminal and the pull request show it. */
|
|
16
|
+
export declare function seconds(durationMs: number): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The correction sent to the implementing agent: the failed check, its cause and the end of its output. */
|
|
2
|
+
export function failureFeedback(check, cause, output) {
|
|
3
|
+
const header = `Verification failed: ${check.name} (\`${check.command.join(" ")}\`): ${cause}.`;
|
|
4
|
+
const tail = output.trim().split("\n").slice(-100).join("\n").slice(-8000);
|
|
5
|
+
return tail ? `${header} End of its output:\n${tail}` : header;
|
|
6
|
+
}
|
|
7
|
+
/** A duration as the terminal and the pull request show it. */
|
|
8
|
+
export function seconds(durationMs) {
|
|
9
|
+
return `${Math.round(durationMs / 1000)} s`;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=verification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../../src/domain/verification.ts"],"names":[],"mappings":"AAcA,4GAA4G;AAC5G,MAAM,UAAU,eAAe,CAC7B,KAAoB,EACpB,KAAa,EACb,MAAc;IAEd,MAAM,MAAM,GAAG,wBAAwB,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC;IAChG,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,wBAAwB,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;AACjE,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,OAAO,CAAC,UAAkB;IACxC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;AAC9C,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public API
|
|
3
|
-
*
|
|
4
|
-
* factory and the engine share
|
|
2
|
+
* Public API of a repository's `.factory/`: `defineFactory`, which adapts the
|
|
3
|
+
* factory's workflows to the repository, and the engine's API to write its own
|
|
4
|
+
* workflows, `z` included, so that workflows, the factory and the engine share
|
|
5
|
+
* one Zod instance.
|
|
5
6
|
*/
|
|
6
|
-
export { type AgentProfile, agent, type Check, code, defineWorkflow, END, gate, type Log, node, ProcessInterruptedError, pick, type RunState, retry, runProcess, type Stage, type StageContext, type StageResult, schemaGate, step, type WorkflowDefinition, z, } from "@clemrig/flow";
|
|
7
|
+
export { type AgentProfile, agent, type Check, code, defineWorkflow, END, gate, type Log, node, ProcessInterruptedError, pick, type RunState, retry, runProcess, type Stage, type StageContext, type StageResult, schemaGate, step, type WorkflowDefinition, type WorkflowStep, z, } from "@clemrig/flow";
|
|
8
|
+
export { type DefinedFactory, defineFactory } from "./app/define.js";
|
|
9
|
+
export type { FactoryDefinition } from "./domain/definition.js";
|
|
10
|
+
export type { VerifyCommand } from "./domain/verification.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public API
|
|
3
|
-
*
|
|
4
|
-
* factory and the engine share
|
|
2
|
+
* Public API of a repository's `.factory/`: `defineFactory`, which adapts the
|
|
3
|
+
* factory's workflows to the repository, and the engine's API to write its own
|
|
4
|
+
* workflows, `z` included, so that workflows, the factory and the engine share
|
|
5
|
+
* one Zod instance.
|
|
5
6
|
*/
|
|
6
7
|
export { agent, code, defineWorkflow, END, gate, node, ProcessInterruptedError, pick, retry, runProcess, schemaGate, step, z, } from "@clemrig/flow";
|
|
8
|
+
export { defineFactory } from "./app/define.js";
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,EAEL,IAAI,EACJ,cAAc,EACd,GAAG,EACH,IAAI,EAEJ,IAAI,EACJ,uBAAuB,EACvB,IAAI,EAEJ,KAAK,EACL,UAAU,EAIV,UAAU,EACV,IAAI,EAGJ,CAAC,GACF,MAAM,eAAe,CAAC;AACvB,OAAO,EAAuB,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CheckResult, VerifyCommand } from "../domain/verification.js";
|
|
2
|
+
export type CheckOutcome = {
|
|
3
|
+
ok: true;
|
|
4
|
+
durationMs: number;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
cause: string;
|
|
8
|
+
output: string;
|
|
9
|
+
};
|
|
10
|
+
/** Runs one of the repository's checks; a timeout or a command that cannot start fails it, Ctrl-C stops the run. */
|
|
11
|
+
export declare function runCheck(dir: string, check: VerifyCommand): Promise<CheckOutcome>;
|
|
12
|
+
export type ChecksOutcome = {
|
|
13
|
+
ok: true;
|
|
14
|
+
passed: CheckResult[];
|
|
15
|
+
} | {
|
|
16
|
+
ok: false;
|
|
17
|
+
passed: CheckResult[];
|
|
18
|
+
check: VerifyCommand;
|
|
19
|
+
cause: string;
|
|
20
|
+
output: string;
|
|
21
|
+
};
|
|
22
|
+
/** A check that starts, or passes with its duration. */
|
|
23
|
+
export type CheckProgress = {
|
|
24
|
+
check: VerifyCommand;
|
|
25
|
+
status: "started";
|
|
26
|
+
} | {
|
|
27
|
+
check: VerifyCommand;
|
|
28
|
+
status: "passed";
|
|
29
|
+
durationMs: number;
|
|
30
|
+
};
|
|
31
|
+
/** Runs the checks in order, stopping at the first failure. */
|
|
32
|
+
export declare function runChecks(dir: string, checks: VerifyCommand[], progress?: (event: CheckProgress) => void): Promise<ChecksOutcome>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ProcessInterruptedError, ProcessTimeoutError, runProcess, } from "@clemrig/flow";
|
|
2
|
+
/** Runs one of the repository's checks; a timeout or a command that cannot start fails it, Ctrl-C stops the run. */
|
|
3
|
+
export async function runCheck(dir, check) {
|
|
4
|
+
const started = Date.now();
|
|
5
|
+
try {
|
|
6
|
+
const result = await runProcess(check.command, dir, check.timeoutMs);
|
|
7
|
+
if (result.exitCode === 0)
|
|
8
|
+
return { ok: true, durationMs: Date.now() - started };
|
|
9
|
+
return {
|
|
10
|
+
ok: false,
|
|
11
|
+
cause: `exit code ${result.exitCode}`,
|
|
12
|
+
output: result.stdout + result.stderr,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error instanceof ProcessInterruptedError)
|
|
17
|
+
throw error;
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
21
|
+
// What a command wrote before its timeout explains it.
|
|
22
|
+
output: error instanceof ProcessTimeoutError ? error.stdout + error.stderr : "",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Runs the checks in order, stopping at the first failure. */
|
|
27
|
+
export async function runChecks(dir, checks, progress) {
|
|
28
|
+
const passed = [];
|
|
29
|
+
for (const check of checks) {
|
|
30
|
+
progress?.({ check, status: "started" });
|
|
31
|
+
const outcome = await runCheck(dir, check);
|
|
32
|
+
if (!outcome.ok)
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
passed,
|
|
36
|
+
check,
|
|
37
|
+
cause: outcome.cause,
|
|
38
|
+
output: outcome.output,
|
|
39
|
+
};
|
|
40
|
+
progress?.({ check, status: "passed", durationMs: outcome.durationMs });
|
|
41
|
+
passed.push({
|
|
42
|
+
name: check.name,
|
|
43
|
+
command: check.command,
|
|
44
|
+
durationMs: outcome.durationMs,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return { ok: true, passed };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/system/checks.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,UAAU,GACX,MAAM,eAAe,CAAC;AAOvB,oHAAoH;AACpH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAW,EACX,KAAoB;IAEpB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACrE,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC;YACvB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;QACxD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,aAAa,MAAM,CAAC,QAAQ,EAAE;YACrC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM;SACtC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,uBAAuB;YAAE,MAAM,KAAK,CAAC;QAC1D,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;YAC7D,uDAAuD;YACvD,MAAM,EACJ,KAAK,YAAY,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;SAC1E,CAAC;IACJ,CAAC;AACH,CAAC;AAiBD,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,MAAuB,EACvB,QAAyC;IAEzC,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,CAAC,EAAE;YACb,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM;gBACN,KAAK;gBACL,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC;QACJ,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,UAAU,EAAE,OAAO,CAAC,UAAU;SAC/B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC"}
|
package/dist/system/factory.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Workflow } from "@clemrig/flow";
|
|
2
2
|
/** A repository's `.factory/`: its workflows, where they work and where runs are kept. */
|
|
3
3
|
export interface Factory {
|
|
4
4
|
/** The repository: the directory that contains `.factory/`. */
|
|
@@ -6,11 +6,13 @@ export interface Factory {
|
|
|
6
6
|
factoryDir: string;
|
|
7
7
|
runsDir: string;
|
|
8
8
|
workflows: Map<string, Workflow>;
|
|
9
|
+
/** The factory's own workflows, whose prompts come with this package. */
|
|
10
|
+
packaged: Set<string>;
|
|
9
11
|
}
|
|
10
12
|
/** A factory without workflows, to read runs when `factory.ts` cannot load. */
|
|
11
13
|
export declare function emptyFactory(factoryDir: string): Factory;
|
|
12
|
-
/** Imports `.factory/factory.ts
|
|
13
|
-
export declare function
|
|
14
|
+
/** Imports `.factory/factory.ts` and returns its default export, unchecked. */
|
|
15
|
+
export declare function importFactory(factoryDir: string): Promise<unknown>;
|
|
14
16
|
export declare function workflowOf(factory: Factory, name: string): Workflow;
|
|
15
|
-
/** Reads
|
|
16
|
-
export declare function loadPrompts(
|
|
17
|
+
/** Reads `<name>.md` for each agent stage of the workflow, at every run. */
|
|
18
|
+
export declare function loadPrompts(factory: Factory, workflow: Workflow): Promise<Record<string, string>>;
|
package/dist/system/factory.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
4
|
-
import { isWorkflow } from "@clemrig/flow";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
4
|
/** A factory without workflows, to read runs when `factory.ts` cannot load. */
|
|
6
5
|
export function emptyFactory(factoryDir) {
|
|
7
6
|
return {
|
|
@@ -9,23 +8,13 @@ export function emptyFactory(factoryDir) {
|
|
|
9
8
|
factoryDir,
|
|
10
9
|
runsDir: join(factoryDir, "runs"),
|
|
11
10
|
workflows: new Map(),
|
|
11
|
+
packaged: new Set(),
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
-
/** Imports `.factory/factory.ts
|
|
15
|
-
export async function
|
|
14
|
+
/** Imports `.factory/factory.ts` and returns its default export, unchecked. */
|
|
15
|
+
export async function importFactory(factoryDir) {
|
|
16
16
|
const module = (await import(pathToFileURL(join(factoryDir, "factory.ts")).href));
|
|
17
|
-
|
|
18
|
-
if (!Array.isArray(exported))
|
|
19
|
-
throw new Error("factory.ts must export default an array of defineWorkflow(…) results");
|
|
20
|
-
const workflows = new Map();
|
|
21
|
-
for (const workflow of exported) {
|
|
22
|
-
if (!isWorkflow(workflow))
|
|
23
|
-
throw new Error("factory.ts must export default an array of defineWorkflow(…) results");
|
|
24
|
-
if (workflows.has(workflow.name))
|
|
25
|
-
throw new Error(`Duplicate workflow ${workflow.name} in factory.ts`);
|
|
26
|
-
workflows.set(workflow.name, workflow);
|
|
27
|
-
}
|
|
28
|
-
return { ...emptyFactory(factoryDir), workflows };
|
|
17
|
+
return module.default;
|
|
29
18
|
}
|
|
30
19
|
export function workflowOf(factory, name) {
|
|
31
20
|
const workflow = factory.workflows.get(name);
|
|
@@ -33,14 +22,25 @@ export function workflowOf(factory, name) {
|
|
|
33
22
|
throw new Error(`Unknown workflow ${name}; workflows: ${[...factory.workflows.keys()].join(", ") || "none"}`);
|
|
34
23
|
return workflow;
|
|
35
24
|
}
|
|
36
|
-
/**
|
|
37
|
-
|
|
25
|
+
/** Both `src/system/` and `dist/system/` sit two levels below the package root. */
|
|
26
|
+
const packageRoot = fileURLToPath(new URL("../../", import.meta.url));
|
|
27
|
+
/**
|
|
28
|
+
* Where a workflow's prompts are: `prompts/<workflow>/` of this package for the
|
|
29
|
+
* factory's own, `.factory/workflows/<workflow>/prompts/` for the repository's.
|
|
30
|
+
*/
|
|
31
|
+
function promptsDir(factory, workflow) {
|
|
32
|
+
return factory.packaged.has(workflow.name)
|
|
33
|
+
? join(packageRoot, "prompts", workflow.name)
|
|
34
|
+
: join(factory.factoryDir, "workflows", workflow.name, "prompts");
|
|
35
|
+
}
|
|
36
|
+
/** Reads `<name>.md` for each agent stage of the workflow, at every run. */
|
|
37
|
+
export async function loadPrompts(factory, workflow) {
|
|
38
38
|
const prompts = {};
|
|
39
39
|
const problems = [];
|
|
40
40
|
for (const { prompt: name } of Object.values(workflow.agents)) {
|
|
41
41
|
if (Object.hasOwn(prompts, name))
|
|
42
42
|
continue;
|
|
43
|
-
const file = join(
|
|
43
|
+
const file = join(promptsDir(factory, workflow), `${name}.md`);
|
|
44
44
|
let text;
|
|
45
45
|
try {
|
|
46
46
|
text = await readFile(file, "utf8");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/system/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/system/factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** A branch's full reference: a tag of the same name never answers for it. */
|
|
2
|
+
export declare function branchRef(branch: string): string;
|
|
3
|
+
/** The full reference of `origin`'s copy of a branch. */
|
|
4
|
+
export declare function remoteRef(branch: string): string;
|
|
5
|
+
/** The branch `origin/HEAD` points to, read from its full reference: a tag never shortens it. */
|
|
6
|
+
export declare function defaultBranch(dir: string): Promise<string>;
|
|
7
|
+
/** The repository's Git directory, where Git keeps what belongs to this working tree. */
|
|
8
|
+
export declare function gitDir(dir: string): Promise<string>;
|
|
9
|
+
/** The files under `path` in a branch or commit, relative to the repository. */
|
|
10
|
+
export declare function filesIn(dir: string, ref: string, path: string): Promise<string[]>;
|
|
11
|
+
export declare function currentBranch(dir: string): Promise<string>;
|
|
12
|
+
/** Modified, staged or untracked files, in the whole tree or under a path. */
|
|
13
|
+
export declare function hasChanges(dir: string, path?: string): Promise<boolean>;
|
|
14
|
+
export declare function fetchBranch(dir: string, branch: string): Promise<void>;
|
|
15
|
+
export declare function commitOf(dir: string, ref: string): Promise<string>;
|
|
16
|
+
/** Switches to a branch; `from` creates it at that commit, replacing a branch of that name. */
|
|
17
|
+
export declare function switchTo(dir: string, branch: string, from?: string): Promise<void>;
|
|
18
|
+
/** Commits the given paths, or every change; returns the new commit. */
|
|
19
|
+
export declare function commit(dir: string, message: string, paths?: string[]): Promise<string>;
|
|
20
|
+
/** Every change since a commit, new files included: stages them all, then diffs the index. */
|
|
21
|
+
export declare function stagedDiff(dir: string, since: string): Promise<{
|
|
22
|
+
text: string;
|
|
23
|
+
truncated: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
export declare function forcePush(dir: string, branch: string): Promise<void>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { runProcess } from "@clemrig/flow";
|
|
2
|
+
/** Runs git in the repository; a failure carries git's own message. */
|
|
3
|
+
async function git(dir, args, timeoutMs = 60_000) {
|
|
4
|
+
const result = await runProcess(["git", ...args], dir, timeoutMs);
|
|
5
|
+
if (result.exitCode !== 0)
|
|
6
|
+
throw new Error(`git ${args.join(" ")} failed: ${result.stderr.trim() || result.stdout.trim()}`);
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
async function output(dir, args) {
|
|
10
|
+
return (await git(dir, args)).stdout.trim();
|
|
11
|
+
}
|
|
12
|
+
/** A branch's full reference: a tag of the same name never answers for it. */
|
|
13
|
+
export function branchRef(branch) {
|
|
14
|
+
return `refs/heads/${branch}`;
|
|
15
|
+
}
|
|
16
|
+
/** The full reference of `origin`'s copy of a branch. */
|
|
17
|
+
export function remoteRef(branch) {
|
|
18
|
+
return `refs/remotes/origin/${branch}`;
|
|
19
|
+
}
|
|
20
|
+
/** The branch `origin/HEAD` points to, read from its full reference: a tag never shortens it. */
|
|
21
|
+
export async function defaultBranch(dir) {
|
|
22
|
+
return (await output(dir, ["symbolic-ref", "refs/remotes/origin/HEAD"])).replace(/^refs\/remotes\/origin\//, "");
|
|
23
|
+
}
|
|
24
|
+
/** The repository's Git directory, where Git keeps what belongs to this working tree. */
|
|
25
|
+
export function gitDir(dir) {
|
|
26
|
+
return output(dir, ["rev-parse", "--absolute-git-dir"]);
|
|
27
|
+
}
|
|
28
|
+
/** The files under `path` in a branch or commit, relative to the repository. */
|
|
29
|
+
export async function filesIn(dir, ref, path) {
|
|
30
|
+
return (await output(dir, ["ls-tree", "-r", "--name-only", ref, "--", path]))
|
|
31
|
+
.split("\n")
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
}
|
|
34
|
+
export function currentBranch(dir) {
|
|
35
|
+
return output(dir, ["branch", "--show-current"]);
|
|
36
|
+
}
|
|
37
|
+
/** Modified, staged or untracked files, in the whole tree or under a path. */
|
|
38
|
+
export async function hasChanges(dir, path) {
|
|
39
|
+
return ((await output(dir, [
|
|
40
|
+
"status",
|
|
41
|
+
"--porcelain",
|
|
42
|
+
// Every untracked file, whatever status.showUntrackedFiles says.
|
|
43
|
+
"--untracked-files=all",
|
|
44
|
+
...(path ? ["--", path] : []),
|
|
45
|
+
])) !== "");
|
|
46
|
+
}
|
|
47
|
+
export async function fetchBranch(dir, branch) {
|
|
48
|
+
await git(dir, ["fetch", "origin", `+${branchRef(branch)}:${remoteRef(branch)}`], 120_000);
|
|
49
|
+
}
|
|
50
|
+
export function commitOf(dir, ref) {
|
|
51
|
+
return output(dir, ["rev-parse", ref]);
|
|
52
|
+
}
|
|
53
|
+
/** Switches to a branch; `from` creates it at that commit, replacing a branch of that name. */
|
|
54
|
+
export async function switchTo(dir, branch, from) {
|
|
55
|
+
await git(dir, ["switch", ...(from ? ["-C", branch, from] : [branch])]);
|
|
56
|
+
}
|
|
57
|
+
/** Commits the given paths, or every change; returns the new commit. */
|
|
58
|
+
export async function commit(dir, message, paths) {
|
|
59
|
+
await git(dir, ["add", ...(paths ? ["--", ...paths] : ["-A"])]);
|
|
60
|
+
await git(dir, ["commit", "-m", message, ...(paths ? ["--", ...paths] : [])]);
|
|
61
|
+
return commitOf(dir, "HEAD");
|
|
62
|
+
}
|
|
63
|
+
/** Every change since a commit, new files included: stages them all, then diffs the index. */
|
|
64
|
+
export async function stagedDiff(dir, since) {
|
|
65
|
+
await git(dir, ["add", "-A"]);
|
|
66
|
+
const result = await git(dir, ["diff", "--cached", since]);
|
|
67
|
+
return { text: result.stdout, truncated: result.stdoutTruncated };
|
|
68
|
+
}
|
|
69
|
+
export async function forcePush(dir, branch) {
|
|
70
|
+
const ref = branchRef(branch);
|
|
71
|
+
await git(dir, ["push", "--force", "-u", "origin", `${ref}:${ref}`], 120_000);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/system/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function isAuthenticated(dir: string): Promise<boolean>;
|
|
2
|
+
/** Updates the branch's open pull request, or opens one; returns its URL. */
|
|
3
|
+
export declare function openPullRequest(dir: string, pullRequest: {
|
|
4
|
+
base: string;
|
|
5
|
+
head: string;
|
|
6
|
+
title: string;
|
|
7
|
+
body: string;
|
|
8
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { runProcess } from "@clemrig/flow";
|
|
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, 60_000, undefined, stdin);
|
|
5
|
+
if (result.exitCode !== 0)
|
|
6
|
+
throw new Error(`gh ${args.slice(0, 2).join(" ")} failed: ${result.stderr.trim()}`);
|
|
7
|
+
return result.stdout.trim();
|
|
8
|
+
}
|
|
9
|
+
export async function isAuthenticated(dir) {
|
|
10
|
+
const result = await runProcess(["gh", "auth", "status"], dir, 60_000);
|
|
11
|
+
return result.exitCode === 0;
|
|
12
|
+
}
|
|
13
|
+
/** Updates the branch's open pull request, or opens one; returns its URL. */
|
|
14
|
+
export async function openPullRequest(dir, pullRequest) {
|
|
15
|
+
const { base, head, title, body } = pullRequest;
|
|
16
|
+
const open = JSON.parse(await gh(dir, [
|
|
17
|
+
"pr",
|
|
18
|
+
"list",
|
|
19
|
+
"--head",
|
|
20
|
+
head,
|
|
21
|
+
"--state",
|
|
22
|
+
"open",
|
|
23
|
+
"--json",
|
|
24
|
+
"number,url",
|
|
25
|
+
]));
|
|
26
|
+
const existing = open[0];
|
|
27
|
+
if (existing) {
|
|
28
|
+
await gh(dir, [
|
|
29
|
+
"pr",
|
|
30
|
+
"edit",
|
|
31
|
+
String(existing.number),
|
|
32
|
+
"--title",
|
|
33
|
+
title,
|
|
34
|
+
"--body-file",
|
|
35
|
+
"-",
|
|
36
|
+
], body);
|
|
37
|
+
return existing.url;
|
|
38
|
+
}
|
|
39
|
+
return gh(dir, [
|
|
40
|
+
"pr",
|
|
41
|
+
"create",
|
|
42
|
+
"--base",
|
|
43
|
+
base,
|
|
44
|
+
"--head",
|
|
45
|
+
head,
|
|
46
|
+
"--title",
|
|
47
|
+
title,
|
|
48
|
+
"--body-file",
|
|
49
|
+
"-",
|
|
50
|
+
], body);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +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;IAEd,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,EACf,GAAG,EACH,MAAM,EACN,SAAS,EACT,KAAK,CACN,CAAC;IACF,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CACnE,CAAC;IACJ,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW;IAC/C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IACvE,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,WAAwE;IAExE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CACrB,MAAM,EAAE,CAAC,GAAG,EAAE;QACZ,IAAI;QACJ,MAAM;QACN,QAAQ;QACR,IAAI;QACJ,SAAS;QACT,MAAM;QACN,QAAQ;QACR,YAAY;KACb,CAAC,CACkC,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,EAAE,CACN,GAAG,EACH;YACE,IAAI;YACJ,MAAM;YACN,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACvB,SAAS;YACT,KAAK;YACL,aAAa;YACb,GAAG;SACJ,EACD,IAAI,CACL,CAAC;QACF,OAAO,QAAQ,CAAC,GAAG,CAAC;IACtB,CAAC;IACD,OAAO,EAAE,CACP,GAAG,EACH;QACE,IAAI;QACJ,QAAQ;QACR,QAAQ;QACR,IAAI;QACJ,QAAQ;QACR,IAAI;QACJ,SAAS;QACT,KAAK;QACL,aAAa;QACb,GAAG;KACJ,EACD,IAAI,CACL,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type ArtifactKind = "issue" | "spec" | "plan";
|
|
2
|
+
/** Where the issues are, relative to the repository. */
|
|
3
|
+
export declare const issuesPath: string;
|
|
4
|
+
/** `.factory/issues/<slug>/<kind>.md`, relative to the repository: the only place that names these paths. */
|
|
5
|
+
export declare function artifactPath(slug: string, kind: ArtifactKind): string;
|
|
6
|
+
/** An issue's folder as it is in the working tree. */
|
|
7
|
+
export interface IssueFolder {
|
|
8
|
+
slug: string;
|
|
9
|
+
/** Its `issue.md`, when it has one. */
|
|
10
|
+
issue?: string;
|
|
11
|
+
/** Why its `issue.md` cannot be read. */
|
|
12
|
+
unreadable?: string;
|
|
13
|
+
hasSpec: boolean;
|
|
14
|
+
/** When one of its files last changed, as an ISO date. */
|
|
15
|
+
changedAt: string;
|
|
16
|
+
}
|
|
17
|
+
/** Every folder of `.factory/issues/`; none before the first issue. */
|
|
18
|
+
export declare function listIssueFolders(repository: string): Promise<IssueFolder[]>;
|
|
19
|
+
export declare function writeArtifact(repository: string, slug: string, kind: ArtifactKind, text: string): Promise<string>;
|
|
20
|
+
export declare function hasArtifact(repository: string, slug: string, kind: ArtifactKind): Promise<boolean>;
|
|
21
|
+
/** The artifact's text, or nothing when the issue has none. */
|
|
22
|
+
export declare function readArtifact(repository: string, slug: string, kind: ArtifactKind): Promise<string | undefined>;
|
|
23
|
+
/** A draft handed over by an agent, wherever it wrote it. */
|
|
24
|
+
export declare function readDraft(path: string): Promise<string>;
|