@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.
Files changed (93) hide show
  1. package/README.md +127 -37
  2. package/dist/app/board.d.ts +7 -0
  3. package/dist/app/board.js +47 -0
  4. package/dist/app/board.js.map +1 -0
  5. package/dist/app/define.d.ts +27 -0
  6. package/dist/app/define.js +68 -0
  7. package/dist/app/define.js.map +1 -0
  8. package/dist/app/doctor.js +5 -4
  9. package/dist/app/doctor.js.map +1 -1
  10. package/dist/app/factory.js +10 -2
  11. package/dist/app/factory.js.map +1 -1
  12. package/dist/app/run.js +1 -1
  13. package/dist/app/run.js.map +1 -1
  14. package/dist/app/workflows/implement.d.ts +8 -0
  15. package/dist/app/workflows/implement.js +268 -0
  16. package/dist/app/workflows/implement.js.map +1 -0
  17. package/dist/app/workflows/issue-save.d.ts +2 -0
  18. package/dist/app/workflows/issue-save.js +35 -0
  19. package/dist/app/workflows/issue-save.js.map +1 -0
  20. package/dist/cli/application.js +1 -1
  21. package/dist/cli/application.js.map +1 -1
  22. package/dist/cli/help.d.ts +1 -1
  23. package/dist/cli/help.js +5 -3
  24. package/dist/cli/help.js.map +1 -1
  25. package/dist/cli/init.d.ts +2 -2
  26. package/dist/cli/init.js +4 -4
  27. package/dist/cli/init.js.map +1 -1
  28. package/dist/cli/reporter.js +42 -15
  29. package/dist/cli/reporter.js.map +1 -1
  30. package/dist/cli/ui.d.ts +5 -2
  31. package/dist/cli/ui.js +20 -7
  32. package/dist/cli/ui.js.map +1 -1
  33. package/dist/domain/board.d.ts +65 -0
  34. package/dist/domain/board.js +95 -0
  35. package/dist/domain/board.js.map +1 -0
  36. package/dist/domain/definition.d.ts +15 -0
  37. package/dist/domain/definition.js +28 -0
  38. package/dist/domain/definition.js.map +1 -0
  39. package/dist/domain/delivery.d.ts +9 -0
  40. package/dist/domain/delivery.js +10 -0
  41. package/dist/domain/delivery.js.map +1 -0
  42. package/dist/domain/issue.d.ts +11 -0
  43. package/dist/domain/issue.js +22 -0
  44. package/dist/domain/issue.js.map +1 -0
  45. package/dist/domain/parsed.d.ts +8 -0
  46. package/dist/domain/parsed.js +2 -0
  47. package/dist/domain/parsed.js.map +1 -0
  48. package/dist/domain/plan.d.ts +24 -0
  49. package/dist/domain/plan.js +49 -0
  50. package/dist/domain/plan.js.map +1 -0
  51. package/dist/domain/pull-request.d.ts +17 -0
  52. package/dist/domain/pull-request.js +32 -0
  53. package/dist/domain/pull-request.js.map +1 -0
  54. package/dist/domain/review.d.ts +36 -0
  55. package/dist/domain/review.js +45 -0
  56. package/dist/domain/review.js.map +1 -0
  57. package/dist/domain/spec.d.ts +18 -0
  58. package/dist/domain/spec.js +118 -0
  59. package/dist/domain/spec.js.map +1 -0
  60. package/dist/domain/verification.d.ts +16 -0
  61. package/dist/domain/verification.js +11 -0
  62. package/dist/domain/verification.js.map +1 -0
  63. package/dist/index.d.ts +8 -4
  64. package/dist/index.js +5 -3
  65. package/dist/index.js.map +1 -1
  66. package/dist/system/checks.d.ts +32 -0
  67. package/dist/system/checks.js +49 -0
  68. package/dist/system/checks.js.map +1 -0
  69. package/dist/system/factory.d.ts +7 -5
  70. package/dist/system/factory.js +19 -19
  71. package/dist/system/factory.js.map +1 -1
  72. package/dist/system/git.d.ts +25 -0
  73. package/dist/system/git.js +73 -0
  74. package/dist/system/git.js.map +1 -0
  75. package/dist/system/github.d.ts +8 -0
  76. package/dist/system/github.js +52 -0
  77. package/dist/system/github.js.map +1 -0
  78. package/dist/system/issues.d.ts +24 -0
  79. package/dist/system/issues.js +66 -0
  80. package/dist/system/issues.js.map +1 -0
  81. package/dist/system/lock.d.ts +8 -0
  82. package/dist/system/lock.js +35 -0
  83. package/dist/system/lock.js.map +1 -0
  84. package/dist/system/templates.d.ts +2 -1
  85. package/dist/system/templates.js +69 -3
  86. package/dist/system/templates.js.map +1 -1
  87. package/package.json +6 -4
  88. package/prompts/implement/implement.md +21 -0
  89. package/prompts/implement/plan.md +20 -0
  90. package/prompts/implement/review.md +17 -0
  91. package/templates/init/factory.ts +37 -2
  92. package/templates/init/workflows/ask/index.ts +2 -1
  93. package/templates/skills/issue/SKILL.md +82 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/cli/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,qHAAqH;AACrH,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,OAAgB,EAAE,IAAa;IACtD,MAAM,SAAS,GAAG,MAAM,cAAc,CACpC;QACE,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;QAC5B,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC;KACxD,EACD,IAAI,CACL,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,eAAe,SAAS,CAAC,GAAG,gCAAgC,CAC7D,CAAC;IACF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAC7B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;IACxB,OAAO,CAAC,CAAC;AACX,CAAC"}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/cli/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAc,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,EAAE,CACtB,OAAgB,EAChB,IAAa,EACb,SAAkB;IAElB,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC;QAClC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,SAAS;QAChB,KAAK;QACL,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC;KACxD,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAC/B;QACE,KAAK,EAAE,SAAS;QAChB,KAAK;QACL,wFAAwF;QACxF,KAAK,EAAE,GAAmB,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC;QAC1D,IAAI;KACL,EACD,IAAI,CACL,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,eAAe,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,GAAG,qCAAqC,CAChF,CAAC;IACF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAC7B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACnB,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { type RunState, type WorkflowStep } from "@clemrig/flow";
2
+ /** The columns, in the order an issue goes through them. */
3
+ export declare const columns: readonly ["Backlog", "Specifying", "Executing", "Blocked", "To Approve", "Done"];
4
+ export type ColumnName = (typeof columns)[number];
5
+ /** What the repository says of an issue. */
6
+ export interface IssueFacts {
7
+ slug: string;
8
+ /** From `issue.md`; the slug when it has no readable title. */
9
+ title: string;
10
+ hasSpec: boolean;
11
+ /** Its `plan.md` is on the main branch. */
12
+ delivered: boolean;
13
+ /** When one of its files last changed, as an ISO date. */
14
+ changedAt: string;
15
+ }
16
+ /** How far a run went through one step of its workflow. */
17
+ export interface StepProgress {
18
+ name: string;
19
+ state: "done" | "current" | "failed" | "pending";
20
+ }
21
+ /** The last run of a workflow shown for an issue. */
22
+ export interface RunProgress {
23
+ id: string;
24
+ workflow: string;
25
+ status: RunState["status"];
26
+ /** The stage it is running, or the one it ended on. */
27
+ stage: string;
28
+ steps: StepProgress[];
29
+ durationMs: number | null;
30
+ /** Unknown (`null`) until an agent call announces a cost. */
31
+ costUsd: number | null;
32
+ /** Some agent call announced no cost. */
33
+ partial: boolean;
34
+ }
35
+ /** An issue on the board. */
36
+ export interface IssueCard {
37
+ slug: string;
38
+ title: string;
39
+ /** The newest pull request its deliveries recorded. */
40
+ pullRequest?: string;
41
+ /** The last run of each shown workflow, newest first. */
42
+ runs: RunProgress[];
43
+ }
44
+ /** The issues in their columns, and what may leave the board incomplete. */
45
+ export interface IssueBoard {
46
+ columns: {
47
+ name: ColumnName;
48
+ cards: IssueCard[];
49
+ }[];
50
+ problems: string[];
51
+ }
52
+ /** A workflow shown on the cards: one that takes an issue and declares its steps. */
53
+ export interface ShownWorkflow {
54
+ name: string;
55
+ steps: WorkflowStep[];
56
+ }
57
+ /** No status is stored: the column follows from the issue's files, the main branch and its last delivery. */
58
+ export declare function columnOf(issue: IssueFacts, lastDelivery: Pick<RunState, "status"> | undefined): ColumnName;
59
+ /** How far a run went through its workflow's steps; a correction brings its step back. */
60
+ export declare function stepsOf(steps: WorkflowStep[], run: Pick<RunState, "stage" | "status">): StepProgress[];
61
+ /**
62
+ * The Kanban: each issue in its column, the most recently active first, with
63
+ * the last run of each shown workflow. `runs` come in creation order.
64
+ */
65
+ export declare function boardOf(issues: IssueFacts[], runs: RunState[], shown: ShownWorkflow[], problems: string[]): IssueBoard;
@@ -0,0 +1,95 @@
1
+ import { summarizeRun } from "@clemrig/flow";
2
+ import { DELIVERY, pullRequestOf } from "./delivery.js";
3
+ /** The columns, in the order an issue goes through them. */
4
+ export const columns = [
5
+ "Backlog",
6
+ "Specifying",
7
+ "Executing",
8
+ "Blocked",
9
+ "To Approve",
10
+ "Done",
11
+ ];
12
+ /** No status is stored: the column follows from the issue's files, the main branch and its last delivery. */
13
+ export function columnOf(issue, lastDelivery) {
14
+ if (issue.delivered)
15
+ return "Done";
16
+ if (lastDelivery?.status === "running")
17
+ return "Executing";
18
+ if (lastDelivery?.status === "failed")
19
+ return "Blocked";
20
+ if (lastDelivery)
21
+ return "To Approve";
22
+ return issue.hasSpec ? "Specifying" : "Backlog";
23
+ }
24
+ /** How far a run went through its workflow's steps; a correction brings its step back. */
25
+ export function stepsOf(steps, run) {
26
+ const at = steps.findIndex(({ stages }) => stages.includes(run.stage));
27
+ return steps.map(({ name }, index) => ({
28
+ name,
29
+ state: run.status === "completed" || index < at
30
+ ? "done"
31
+ : index > at || at < 0
32
+ ? "pending"
33
+ : run.status === "running"
34
+ ? "current"
35
+ : "failed",
36
+ }));
37
+ }
38
+ function progressOf(run, steps) {
39
+ const { durationMs, costUsd, partial } = summarizeRun(run);
40
+ return {
41
+ id: run.id,
42
+ workflow: run.workflow,
43
+ status: run.status,
44
+ stage: run.stage,
45
+ steps: stepsOf(steps, run),
46
+ durationMs,
47
+ costUsd,
48
+ partial,
49
+ };
50
+ }
51
+ /**
52
+ * The Kanban: each issue in its column, the most recently active first, with
53
+ * the last run of each shown workflow. `runs` come in creation order.
54
+ */
55
+ export function boardOf(issues, runs, shown, problems) {
56
+ const placed = issues.map((issue) => {
57
+ const own = runs.filter(({ input }) => input.issue === issue.slug);
58
+ const last = (workflow) => own.findLast((run) => run.workflow === workflow);
59
+ // The last delivery sets the column. A new delivery updates the same pull
60
+ // request: the newest one recorded stays shown.
61
+ const delivery = last(DELIVERY);
62
+ const pullRequest = own.map(pullRequestOf).findLast(Boolean);
63
+ const cardRuns = shown
64
+ .flatMap(({ name, steps }) => {
65
+ const run = last(name);
66
+ return run ? [{ run, steps }] : [];
67
+ })
68
+ .sort((a, b) => b.run.createdAt.localeCompare(a.run.createdAt))
69
+ .map(({ run, steps }) => progressOf(run, steps));
70
+ const activity = [issue.changedAt, ...own.map((run) => run.updatedAt)]
71
+ .sort()
72
+ .at(-1);
73
+ return {
74
+ column: columnOf(issue, delivery),
75
+ activity: activity ?? issue.changedAt,
76
+ card: {
77
+ slug: issue.slug,
78
+ title: issue.title,
79
+ ...(pullRequest ? { pullRequest } : {}),
80
+ runs: cardRuns,
81
+ },
82
+ };
83
+ });
84
+ return {
85
+ columns: columns.map((name) => ({
86
+ name,
87
+ cards: placed
88
+ .filter(({ column }) => column === name)
89
+ .sort((a, b) => b.activity.localeCompare(a.activity))
90
+ .map(({ card }) => card),
91
+ })),
92
+ problems,
93
+ };
94
+ }
95
+ //# sourceMappingURL=board.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board.js","sourceRoot":"","sources":["../../src/domain/board.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { type AgentProfile, type Workflow } from "@clemrig/flow";
2
+ import type { VerifyCommand } from "./verification.js";
3
+ /** What a repository decides: its checks, its budget per run, its agents and its own workflows. */
4
+ export interface FactoryDefinition {
5
+ verify: VerifyCommand[];
6
+ maxCostUsd: number;
7
+ profiles: {
8
+ plan: AgentProfile;
9
+ implement: AgentProfile;
10
+ review: AgentProfile;
11
+ };
12
+ /** The repository's own workflows, beside the factory's. */
13
+ workflows?: Workflow[];
14
+ }
15
+ export declare function definitionProblems(value: unknown): string[];
@@ -0,0 +1,28 @@
1
+ import { z } from "@clemrig/flow";
2
+ // Profiles are validated by defineWorkflow: each declares the tools its agent may use.
3
+ const profile = z.looseObject({});
4
+ const definition = z.strictObject({
5
+ verify: z
6
+ .array(z.strictObject({
7
+ name: z.string().min(1),
8
+ /** The executable, then its arguments; run without a shell. */
9
+ command: z.tuple([z.string().min(1)], z.string()),
10
+ timeoutMs: z.number().int().positive(),
11
+ }))
12
+ .min(1),
13
+ maxCostUsd: z.number().positive(),
14
+ profiles: z.strictObject({
15
+ plan: profile,
16
+ implement: profile,
17
+ review: profile,
18
+ }),
19
+ // Each workflow is checked where the factory loads them.
20
+ workflows: z.array(z.unknown()).optional(),
21
+ });
22
+ export function definitionProblems(value) {
23
+ const parsed = definition.safeParse(value);
24
+ return parsed.success
25
+ ? []
26
+ : parsed.error.issues.map(({ path, message }) => `${path.join(".")}: ${message}`);
27
+ }
28
+ //# sourceMappingURL=definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.js","sourceRoot":"","sources":["../../src/domain/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,CAAC,EAAE,MAAM,eAAe,CAAC;AAgBpE,uFAAuF;AACvF,MAAM,OAAO,GAAG,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;AAElC,MAAM,UAAU,GAAG,CAAC,CAAC,YAAY,CAAC;IAChC,MAAM,EAAE,CAAC;SACN,KAAK,CACJ,CAAC,CAAC,YAAY,CAAC;QACb,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACvB,+DAA+D;QAC/D,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;QACjD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;KACvC,CAAC,CACH;SACA,GAAG,CAAC,CAAC,CAAC;IACT,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,QAAQ,EAAE,CAAC,CAAC,YAAY,CAAC;QACvB,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,OAAO;QAClB,MAAM,EAAE,OAAO;KAChB,CAAC;IACF,yDAAyD;IACzD,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC3C,CAAC,CAAC;AAEH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC3C,OAAO,MAAM,CAAC,OAAO;QACnB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CACrB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CACvD,CAAC;AACR,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { RunState } from "@clemrig/flow";
2
+ /** The workflow that delivers an issue: its last run sets the issue's column. */
3
+ export declare const DELIVERY = "implement";
4
+ /** What a delivery records once it opened its pull request. */
5
+ export interface Delivered {
6
+ pullRequest: string;
7
+ }
8
+ /** The pull request a delivery run recorded, if it got that far. */
9
+ export declare function pullRequestOf(run: Pick<RunState, "workflow" | "data">): string | undefined;
@@ -0,0 +1,10 @@
1
+ /** The workflow that delivers an issue: its last run sets the issue's column. */
2
+ export const DELIVERY = "implement";
3
+ /** The pull request a delivery run recorded, if it got that far. */
4
+ export function pullRequestOf(run) {
5
+ if (run.workflow !== DELIVERY)
6
+ return undefined;
7
+ const { pullRequest } = run.data;
8
+ return typeof pullRequest === "string" ? pullRequest : undefined;
9
+ }
10
+ //# sourceMappingURL=delivery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delivery.js","sourceRoot":"","sources":["../../src/domain/delivery.ts"],"names":[],"mappings":"AAEA,iFAAiF;AACjF,MAAM,CAAC,MAAM,QAAQ,GAAG,WAAW,CAAC;AAOpC,oEAAoE;AACpE,MAAM,UAAU,aAAa,CAC3B,GAAwC;IAExC,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,IAA0B,CAAC;IACvD,OAAO,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;AACnE,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Parsed } from "./parsed.js";
2
+ /** An issue's identity: its folder, branch and run input. */
3
+ export declare function isSlug(value: string): boolean;
4
+ export interface Issue {
5
+ title: string;
6
+ description: string;
7
+ }
8
+ /** The `# ` title on the first line of issue.md; empty without one. */
9
+ export declare function issueTitle(text: string): string;
10
+ /** Reads issue.md: a `# ` title, then a free description. */
11
+ export declare function parseIssue(text: string): Parsed<Issue>;
@@ -0,0 +1,22 @@
1
+ /** An issue's identity: its folder, branch and run input. */
2
+ export function isSlug(value) {
3
+ return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);
4
+ }
5
+ /** The `# ` title on the first line of issue.md; empty without one. */
6
+ export function issueTitle(text) {
7
+ const [first = ""] = text.trim().split(/\r?\n/);
8
+ return /^# (.*)$/.exec(first)?.[1]?.trim() ?? "";
9
+ }
10
+ /** Reads issue.md: a `# ` title, then a free description. */
11
+ export function parseIssue(text) {
12
+ const title = issueTitle(text);
13
+ const description = text.trim().split(/\r?\n/).slice(1).join("\n").trim();
14
+ const problems = [
15
+ ...(title ? [] : ["The first line must be a title: # <Title>"]),
16
+ ...(description ? [] : ["The description is empty"]),
17
+ ];
18
+ return problems.length > 0
19
+ ? { ok: false, problems }
20
+ : { ok: true, value: { title, description } };
21
+ }
22
+ //# sourceMappingURL=issue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue.js","sourceRoot":"","sources":["../../src/domain/issue.ts"],"names":[],"mappings":"AAEA,6DAA6D;AAC7D,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,OAAO,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC;AAOD,uEAAuE;AACvE,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAChD,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1E,MAAM,QAAQ,GAAG;QACf,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,2CAA2C,CAAC,CAAC;QAC/D,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC;KACrD,CAAC;IACF,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC;QACxB,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE;QACzB,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** A parsed document, or every problem found in it. */
2
+ export type Parsed<T> = {
3
+ ok: true;
4
+ value: T;
5
+ } | {
6
+ ok: false;
7
+ problems: string[];
8
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parsed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsed.js","sourceRoot":"","sources":["../../src/domain/parsed.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ import { z } from "@clemrig/flow";
2
+ import { type Criterion } from "./spec.js";
3
+ /** The planning agent's response. */
4
+ export declare const planOutput: z.ZodObject<{
5
+ summary: z.ZodString;
6
+ commit: z.ZodString;
7
+ steps: z.ZodArray<z.ZodObject<{
8
+ title: z.ZodString;
9
+ description: z.ZodString;
10
+ criteria: z.ZodArray<z.ZodString>;
11
+ files: z.ZodArray<z.ZodString>;
12
+ tests: z.ZodString;
13
+ }, z.core.$strict>>;
14
+ risks: z.ZodArray<z.ZodString>;
15
+ }, z.core.$strict>;
16
+ export type Plan = z.output<typeof planOutput>;
17
+ /** Every criterion of the spec is covered by at least one step. */
18
+ export declare function planProblem(plan: Plan, criteria: Criterion[]): string | undefined;
19
+ export declare function renderPlan(plan: Plan, source: {
20
+ title: string;
21
+ specPath: string;
22
+ baseCommit: string;
23
+ }): string;
24
+ export declare function planCommitMessage(slug: string): string;
@@ -0,0 +1,49 @@
1
+ import { z } from "@clemrig/flow";
2
+ import { citationProblem } from "./spec.js";
3
+ const text = z.string().trim().min(1);
4
+ /** The planning agent's response. */
5
+ export const planOutput = z.strictObject({
6
+ summary: text,
7
+ /** Subject of the implementation's commit, also the PR title. */
8
+ commit: z
9
+ .string()
10
+ .max(72)
11
+ // One line: it is the commit's subject and the PR's title.
12
+ .regex(/^(feat|fix|refactor|perf|test|docs|chore)(\([a-z0-9-]+\))?: \S[^\r\n]*$/),
13
+ steps: z
14
+ .array(z.strictObject({
15
+ title: text,
16
+ description: text,
17
+ criteria: z.array(text).min(1),
18
+ files: z.array(text).min(1),
19
+ tests: text,
20
+ }))
21
+ .min(1),
22
+ risks: z.array(text),
23
+ });
24
+ /** Every criterion of the spec is covered by at least one step. */
25
+ export function planProblem(plan, criteria) {
26
+ return citationProblem(plan.steps.flatMap((step) => step.criteria), criteria);
27
+ }
28
+ export function renderPlan(plan, source) {
29
+ const steps = plan.steps.map((step, index) => [
30
+ `### ${index + 1}. ${step.title}`,
31
+ `Criteria: ${step.criteria.join(", ")}\nFiles: ${step.files.map((file) => `\`${file}\``).join(", ")}`,
32
+ step.description,
33
+ `Tests: ${step.tests}`,
34
+ ].join("\n\n"));
35
+ const risks = plan.risks.length > 0
36
+ ? plan.risks.map((risk) => `- ${risk}`).join("\n")
37
+ : "None.";
38
+ return `${[
39
+ `# Plan: ${source.title}`,
40
+ `Spec: \`${source.specPath}\` at ${source.baseCommit.slice(0, 7)}`,
41
+ `## Summary\n\n${plan.summary}`,
42
+ `## Steps\n\n${steps.join("\n\n")}`,
43
+ `## Risks\n\n${risks}`,
44
+ ].join("\n\n")}\n`;
45
+ }
46
+ export function planCommitMessage(slug) {
47
+ return `docs: plan ${slug}`;
48
+ }
49
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/domain/plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAkB,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5D,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEtC,qCAAqC;AACrC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,YAAY,CAAC;IACvC,OAAO,EAAE,IAAI;IACb,iEAAiE;IACjE,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,EAAE,CAAC;QACR,2DAA2D;SAC1D,KAAK,CACJ,yEAAyE,CAC1E;IACH,KAAK,EAAE,CAAC;SACL,KAAK,CACJ,CAAC,CAAC,YAAY,CAAC;QACb,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9B,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3B,KAAK,EAAE,IAAI;KACZ,CAAC,CACH;SACA,GAAG,CAAC,CAAC,CAAC;IACT,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;CACrB,CAAC,CAAC;AAGH,mEAAmE;AACnE,MAAM,UAAU,WAAW,CACzB,IAAU,EACV,QAAqB;IAErB,OAAO,eAAe,CACpB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAC3C,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,IAAU,EACV,MAA+D;IAE/D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC3C;QACE,OAAO,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE;QACjC,aAAa,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACrG,IAAI,CAAC,WAAW;QAChB,UAAU,IAAI,CAAC,KAAK,EAAE;KACvB,CAAC,IAAI,CAAC,MAAM,CAAC,CACf,CAAC;IACF,MAAM,KAAK,GACT,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACnB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAClD,CAAC,CAAC,OAAO,CAAC;IACd,OAAO,GAAG;QACR,WAAW,MAAM,CAAC,KAAK,EAAE;QACzB,WAAW,MAAM,CAAC,QAAQ,SAAS,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;QAClE,iBAAiB,IAAI,CAAC,OAAO,EAAE;QAC/B,eAAe,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QACnC,eAAe,KAAK,EAAE;KACvB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,cAAc,IAAI,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Implementation, Review } from "./review.js";
2
+ import type { Criterion } from "./spec.js";
3
+ import { type CheckResult } from "./verification.js";
4
+ export interface PullRequestFacts {
5
+ criteria: Criterion[];
6
+ implementation: Implementation;
7
+ review: Review;
8
+ checks: CheckResult[];
9
+ /** Corrections the review asked for before approving. */
10
+ reviewRounds: number;
11
+ specPath: string;
12
+ planPath: string;
13
+ baseCommit: string;
14
+ runId: string;
15
+ }
16
+ /** Everything needed to approve the change without opening the run. */
17
+ export declare function pullRequestBody(facts: PullRequestFacts): string;
@@ -0,0 +1,32 @@
1
+ import { seconds } from "./verification.js";
2
+ /** Agent text inside a table cell: one line, no column separator. */
3
+ function cell(text) {
4
+ return text.replace(/\s*\n\s*/g, " ").replaceAll("|", "\\|");
5
+ }
6
+ /** Everything needed to approve the change without opening the run. */
7
+ export function pullRequestBody(facts) {
8
+ const { implementation, review } = facts;
9
+ const criteria = facts.criteria.map(({ id, text }) => {
10
+ const judged = review.criteria.find((criterion) => criterion.id === id);
11
+ const status = judged?.status === "met" ? "Met" : "Not met";
12
+ return `| ${cell(`${id}: ${text}`)} | ${status} | ${cell(judged?.evidence ?? "")} |`;
13
+ });
14
+ const checks = facts.checks.map(({ name, command, durationMs }) => `- ${name}: \`${command.join(" ")}\` passed in ${seconds(durationMs)}`);
15
+ const rounds = facts.reviewRounds === 0
16
+ ? "Approved at the first review."
17
+ : `Approved after ${facts.reviewRounds} round${facts.reviewRounds === 1 ? "" : "s"} of changes.`;
18
+ const minor = review.findings
19
+ .filter(({ severity }) => severity === "minor")
20
+ .map(({ file, summary }) => `- ${file ? `\`${file}\`: ` : ""}${summary}`);
21
+ const deviations = implementation.deviations.map((deviation) => `- ${deviation}`);
22
+ return `${[
23
+ `## Summary\n\n${implementation.summary}`,
24
+ `## Acceptance criteria\n\n| Criterion | Review | Evidence |\n|---|---|---|\n${criteria.join("\n")}`,
25
+ `## Verification\n\n${checks.join("\n")}`,
26
+ `## Review\n\n${rounds}\n\n${minor.length > 0 ? `Minor findings:\n${minor.join("\n")}` : "No minor findings."}`,
27
+ `## Deviations from plan\n\n${deviations.length > 0 ? deviations.join("\n") : "None."}`,
28
+ "---",
29
+ `Spec: \`${facts.specPath}\` at ${facts.baseCommit.slice(0, 7)} · Plan: \`${facts.planPath}\` · Run: ${facts.runId}`,
30
+ ].join("\n\n")}\n`;
31
+ }
32
+ //# sourceMappingURL=pull-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull-request.js","sourceRoot":"","sources":["../../src/domain/pull-request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { z } from "@clemrig/flow";
2
+ import { type Criterion } from "./spec.js";
3
+ /** The implementing agent's response. */
4
+ export declare const implementationOutput: z.ZodObject<{
5
+ summary: z.ZodString;
6
+ criteria: z.ZodArray<z.ZodObject<{
7
+ id: z.ZodString;
8
+ evidence: z.ZodString;
9
+ }, z.core.$strict>>;
10
+ deviations: z.ZodArray<z.ZodString>;
11
+ }, z.core.$strict>;
12
+ export type Implementation = z.output<typeof implementationOutput>;
13
+ export declare function implementationProblem(implementation: Implementation, criteria: Criterion[]): string | undefined;
14
+ /** The reviewer's response: its approval follows from these facts alone. */
15
+ export declare const reviewOutput: z.ZodObject<{
16
+ criteria: z.ZodArray<z.ZodObject<{
17
+ id: z.ZodString;
18
+ status: z.ZodEnum<{
19
+ met: "met";
20
+ not_met: "not_met";
21
+ }>;
22
+ evidence: z.ZodString;
23
+ }, z.core.$strict>>;
24
+ findings: z.ZodArray<z.ZodObject<{
25
+ severity: z.ZodEnum<{
26
+ blocking: "blocking";
27
+ minor: "minor";
28
+ }>;
29
+ file: z.ZodOptional<z.ZodString>;
30
+ summary: z.ZodString;
31
+ }, z.core.$strict>>;
32
+ }, z.core.$strict>;
33
+ export type Review = z.output<typeof reviewOutput>;
34
+ export declare function reviewProblem(review: Review, criteria: Criterion[]): string | undefined;
35
+ /** The correction sent to the implementing agent, or nothing when every criterion is met and nothing blocks. */
36
+ export declare function reviewChanges(review: Review): string | undefined;
@@ -0,0 +1,45 @@
1
+ import { z } from "@clemrig/flow";
2
+ import { citationProblem } from "./spec.js";
3
+ const text = z.string().trim().min(1);
4
+ /** The implementing agent's response. */
5
+ export const implementationOutput = z.strictObject({
6
+ summary: text,
7
+ criteria: z.array(z.strictObject({ id: text, evidence: text })),
8
+ /** Where the work departs from the plan, and why. */
9
+ deviations: z.array(text),
10
+ });
11
+ export function implementationProblem(implementation, criteria) {
12
+ return citationProblem(implementation.criteria.map(({ id }) => id), criteria, true);
13
+ }
14
+ /** The reviewer's response: its approval follows from these facts alone. */
15
+ export const reviewOutput = z.strictObject({
16
+ criteria: z.array(z.strictObject({
17
+ id: text,
18
+ status: z.enum(["met", "not_met"]),
19
+ evidence: text,
20
+ })),
21
+ findings: z.array(z.strictObject({
22
+ /** Blocking: an unmet criterion, a defect or a regression; minor: an improvement not required. */
23
+ severity: z.enum(["blocking", "minor"]),
24
+ file: text.optional(),
25
+ summary: text,
26
+ })),
27
+ });
28
+ export function reviewProblem(review, criteria) {
29
+ return citationProblem(review.criteria.map(({ id }) => id), criteria, true);
30
+ }
31
+ /** The correction sent to the implementing agent, or nothing when every criterion is met and nothing blocks. */
32
+ export function reviewChanges(review) {
33
+ const changes = [
34
+ ...review.criteria
35
+ .filter(({ status }) => status === "not_met")
36
+ .map(({ id, evidence }) => `- ${id} is not met: ${evidence}`),
37
+ ...review.findings
38
+ .filter(({ severity }) => severity === "blocking")
39
+ .map(({ file, summary }) => `- Blocking${file ? ` (${file})` : ""}: ${summary}`),
40
+ ];
41
+ return changes.length > 0
42
+ ? ["The review requires changes:", ...changes].join("\n")
43
+ : undefined;
44
+ }
45
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/domain/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAkB,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5D,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEtC,yCAAyC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,YAAY,CAAC;IACjD,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,qDAAqD;IACrD,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;CAC1B,CAAC,CAAC;AAGH,MAAM,UAAU,qBAAqB,CACnC,cAA8B,EAC9B,QAAqB;IAErB,OAAO,eAAe,CACpB,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAC3C,QAAQ,EACR,IAAI,CACL,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,KAAK,CACf,CAAC,CAAC,YAAY,CAAC;QACb,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAClC,QAAQ,EAAE,IAAI;KACf,CAAC,CACH;IACD,QAAQ,EAAE,CAAC,CAAC,KAAK,CACf,CAAC,CAAC,YAAY,CAAC;QACb,kGAAkG;QAClG,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACvC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;QACrB,OAAO,EAAE,IAAI;KACd,CAAC,CACH;CACF,CAAC,CAAC;AAGH,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,QAAqB;IAErB,OAAO,eAAe,CACpB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EACnC,QAAQ,EACR,IAAI,CACL,CAAC;AACJ,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,OAAO,GAAG;QACd,GAAG,MAAM,CAAC,QAAQ;aACf,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC;aAC5C,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,gBAAgB,QAAQ,EAAE,CAAC;QAC/D,GAAG,MAAM,CAAC,QAAQ;aACf,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,KAAK,UAAU,CAAC;aACjD,GAAG,CACF,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CACpB,aAAa,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,EAAE,CACtD;KACJ,CAAC;IACF,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC;QACvB,CAAC,CAAC,CAAC,8BAA8B,EAAE,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACzD,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { Parsed } from "./parsed.js";
2
+ /** An acceptance criterion: an externally observable fact, cited by the plan, the review and the PR. */
3
+ export interface Criterion {
4
+ id: string;
5
+ text: string;
6
+ }
7
+ export interface Spec {
8
+ title: string;
9
+ objective: string;
10
+ scope: string;
11
+ outOfScope: string;
12
+ criteria: Criterion[];
13
+ constraints: string;
14
+ }
15
+ /** Reads spec.md: a `# ` title, then exactly the five sections, in order. */
16
+ export declare function parseSpec(text: string): Parsed<Spec>;
17
+ /** What is wrong with the criteria a response cites: all of the spec's, no other, and each once if required. */
18
+ export declare function citationProblem(cited: string[], criteria: Criterion[], once?: boolean): string | undefined;