@jwillert/forgeflow 0.1.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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -0
  3. package/dist/cli/index.d.ts +2 -0
  4. package/dist/cli/index.js +119 -0
  5. package/dist/cli/index.js.map +1 -0
  6. package/dist/cli/init.d.ts +11 -0
  7. package/dist/cli/init.js +198 -0
  8. package/dist/cli/init.js.map +1 -0
  9. package/dist/cli/lock.d.ts +4 -0
  10. package/dist/cli/lock.js +55 -0
  11. package/dist/cli/lock.js.map +1 -0
  12. package/dist/cli/templates/Dockerfile +38 -0
  13. package/dist/cli/templates/build-image.sh +11 -0
  14. package/dist/core/capabilities.d.ts +75 -0
  15. package/dist/core/capabilities.js +2 -0
  16. package/dist/core/capabilities.js.map +1 -0
  17. package/dist/core/config.d.ts +38 -0
  18. package/dist/core/config.js +25 -0
  19. package/dist/core/config.js.map +1 -0
  20. package/dist/core/engine.d.ts +44 -0
  21. package/dist/core/engine.js +225 -0
  22. package/dist/core/engine.js.map +1 -0
  23. package/dist/core/state.d.ts +42 -0
  24. package/dist/core/state.js +8 -0
  25. package/dist/core/state.js.map +1 -0
  26. package/dist/core/types.d.ts +141 -0
  27. package/dist/core/types.js +2 -0
  28. package/dist/core/types.js.map +1 -0
  29. package/dist/core/workflow.d.ts +72 -0
  30. package/dist/core/workflow.js +38 -0
  31. package/dist/core/workflow.js.map +1 -0
  32. package/dist/index.d.ts +13 -0
  33. package/dist/index.js +11 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/process/index.d.ts +14 -0
  36. package/dist/process/index.js +46 -0
  37. package/dist/process/index.js.map +1 -0
  38. package/dist/providers/github/index.d.ts +81 -0
  39. package/dist/providers/github/index.js +271 -0
  40. package/dist/providers/github/index.js.map +1 -0
  41. package/dist/providers/gitlab/index.d.ts +79 -0
  42. package/dist/providers/gitlab/index.js +263 -0
  43. package/dist/providers/gitlab/index.js.map +1 -0
  44. package/dist/state/sqlite/index.d.ts +2 -0
  45. package/dist/state/sqlite/index.js +138 -0
  46. package/dist/state/sqlite/index.js.map +1 -0
  47. package/dist/workflows/implement.d.ts +14 -0
  48. package/dist/workflows/implement.js +154 -0
  49. package/dist/workflows/implement.js.map +1 -0
  50. package/dist/workflows/index.d.ts +9 -0
  51. package/dist/workflows/index.js +5 -0
  52. package/dist/workflows/index.js.map +1 -0
  53. package/dist/workflows/prompts/implement.md +43 -0
  54. package/dist/workflows/prompts/review/extraction.md +21 -0
  55. package/dist/workflows/prompts/review/prompt.md +49 -0
  56. package/dist/workflows/prompts/update-branch/extraction.md +13 -0
  57. package/dist/workflows/prompts/update-branch/prompt.md +41 -0
  58. package/dist/workflows/review.d.ts +14 -0
  59. package/dist/workflows/review.js +134 -0
  60. package/dist/workflows/review.js.map +1 -0
  61. package/dist/workflows/shared/git.d.ts +17 -0
  62. package/dist/workflows/shared/git.js +28 -0
  63. package/dist/workflows/shared/git.js.map +1 -0
  64. package/dist/workflows/shared/lifecycle.d.ts +21 -0
  65. package/dist/workflows/shared/lifecycle.js +29 -0
  66. package/dist/workflows/shared/lifecycle.js.map +1 -0
  67. package/dist/workflows/shared/review-output.d.ts +44 -0
  68. package/dist/workflows/shared/review-output.js +100 -0
  69. package/dist/workflows/shared/review-output.js.map +1 -0
  70. package/dist/workflows/shared/sandcastle.d.ts +26 -0
  71. package/dist/workflows/shared/sandcastle.js +112 -0
  72. package/dist/workflows/shared/sandcastle.js.map +1 -0
  73. package/dist/workflows/shared/schema.d.ts +4 -0
  74. package/dist/workflows/shared/schema.js +17 -0
  75. package/dist/workflows/shared/schema.js.map +1 -0
  76. package/dist/workflows/update-branch.d.ts +12 -0
  77. package/dist/workflows/update-branch.js +148 -0
  78. package/dist/workflows/update-branch.js.map +1 -0
  79. package/package.json +46 -0
@@ -0,0 +1,154 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ import { dirname, join } from "node:path";
54
+ import { fileURLToPath } from "node:url";
55
+ import { defineWorkflow, Match, labelAdded } from "../core/workflow.js";
56
+ import { checkoutBaseBranch, commitsAhead, pushBranch } from "./shared/git.js";
57
+ import { createPodmanSandbox, piAgent } from "./shared/sandcastle.js";
58
+ import { runWithAgentLifecycle } from "./shared/lifecycle.js";
59
+ const here = dirname(fileURLToPath(import.meta.url));
60
+ const defaultPromptFile = join(here, "prompts/implement.md");
61
+ export function createImplementWorkflow(options = {}) {
62
+ const triggerLabel = options.triggerLabel ?? "agent:implement";
63
+ const reviewLabel = options.reviewLabel ?? "agent:review";
64
+ const promptFile = options.promptFile ?? defaultPromptFile;
65
+ return defineWorkflow(options.id ?? "implement", {
66
+ match: async ({ event, workReader }) => {
67
+ if (!labelAdded(event, triggerLabel))
68
+ return Match.ignore();
69
+ if (event.workTarget.kind !== "issue")
70
+ return Match.ignore();
71
+ const blockers = await workReader.listBlockingIssues(event.workTarget).catch(() => []);
72
+ const openBlockerIds = [];
73
+ for (const blocker of blockers) {
74
+ const snapshot = await workReader.getTarget(blocker).catch(() => undefined);
75
+ if (snapshot?.state !== "closed")
76
+ openBlockerIds.push(blocker.id);
77
+ }
78
+ if (openBlockerIds.length > 0)
79
+ return Match.defer(`Blocked by #${openBlockerIds.join(", #")}`);
80
+ return Match.accept();
81
+ },
82
+ run: async ({ command, workReader, workTracker, codeHost, codeReviewHost, commandState }) => runWithAgentLifecycle({ workTracker, target: command.workTarget, labels: { trigger: triggerLabel, blocked: options.blockedLabel, inProgress: options.inProgressLabel } }, async () => {
83
+ const env_1 = { stack: [], error: void 0, hasError: false };
84
+ try {
85
+ const issueNumber = command.workTarget.id;
86
+ const checkout = await codeHost.resolveCheckout(command.codeTarget, command);
87
+ await checkoutBaseBranch({ baseRef: checkout.baseRef });
88
+ const issueSnapshot = await workReader.getTarget(command.workTarget).catch(() => undefined);
89
+ const issueComments = await codeReviewHost.listGeneralComments(command.workTarget).catch(() => []);
90
+ const issueContext = issueSnapshot
91
+ ? [`# ${issueSnapshot.title}\n\n${issueSnapshot.body ?? ""}`, ...issueComments.map(c => `---\n${c.author ?? "unknown"}: ${c.body}`)].join("\n\n")
92
+ : command.body ?? `Issue #${issueNumber}: ${command.title}`;
93
+ const sandbox = __addDisposableResource(env_1, await createPodmanSandbox({
94
+ ...options,
95
+ branch: checkout.workBranch,
96
+ preflightCommand: options.preflightCommand,
97
+ }), true);
98
+ const result = await sandbox.run({
99
+ name: `implement-#${issueNumber}`,
100
+ agent: piAgent(options),
101
+ promptFile,
102
+ promptArgs: {
103
+ ISSUE_NUMBER: issueNumber,
104
+ ISSUE_TITLE: command.title,
105
+ ISSUE_CONTEXT: issueContext,
106
+ BRANCH: checkout.workBranch,
107
+ BASE_REF: checkout.baseRef,
108
+ },
109
+ idleTimeoutSeconds: options.idleTimeoutSeconds ?? 900,
110
+ });
111
+ const count = await commitsAhead({ baseRef: checkout.baseRef, cwd: sandbox.worktreePath });
112
+ if ((!Number.isFinite(count) || count === 0) && result.commits.length === 0) {
113
+ throw new Error("Agent finished but no commits were made on the branch.");
114
+ }
115
+ await pushBranch({ branch: checkout.workBranch, cwd: sandbox.worktreePath });
116
+ const existing = await commandState.getLinkedChangeRequest();
117
+ const changeRequest = await codeHost.openOrUpdateChangeRequest({
118
+ existing,
119
+ sourceBranch: {
120
+ provider: command.codeTarget.provider,
121
+ repo: command.codeTarget.repo,
122
+ name: checkout.workBranch,
123
+ },
124
+ targetBranch: checkout.baseRef,
125
+ title: `Fix #${issueNumber}: ${command.title}`.slice(0, 256),
126
+ body: `Closes #${issueNumber}\n\nImplemented by the Forgeflow external workflow.`,
127
+ });
128
+ await commandState.linkChangeRequest(changeRequest);
129
+ const reviewTarget = {
130
+ provider: command.workTarget.provider,
131
+ repo: command.workTarget.repo,
132
+ project: command.workTarget.project,
133
+ kind: changeRequest.kind,
134
+ id: changeRequest.id,
135
+ url: changeRequest.url,
136
+ };
137
+ await workTracker.addLabel(reviewTarget, reviewLabel).catch(() => undefined);
138
+ await workTracker.addComment(command.workTarget, `Implementation completed.\n\nChange request: ${changeRequest.url ?? changeRequest.id}`);
139
+ return { summary: `Implementation completed: ${changeRequest.url ?? changeRequest.id}` };
140
+ }
141
+ catch (e_1) {
142
+ env_1.error = e_1;
143
+ env_1.hasError = true;
144
+ }
145
+ finally {
146
+ const result_1 = __disposeResources(env_1);
147
+ if (result_1)
148
+ await result_1;
149
+ }
150
+ }),
151
+ });
152
+ }
153
+ export const implement = createImplementWorkflow();
154
+ //# sourceMappingURL=implement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"implement.js","sourceRoot":"","sources":["../../src/workflows/implement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export { createImplementWorkflow, implement } from "./implement.js";
2
+ export type { ImplementWorkflowOptions } from "./implement.js";
3
+ export { createReviewWorkflow, review } from "./review.js";
4
+ export type { ReviewWorkflowOptions } from "./review.js";
5
+ export { createUpdateBranchWorkflow, updateBranch } from "./update-branch.js";
6
+ export type { UpdateBranchWorkflowOptions } from "./update-branch.js";
7
+ export type { SandboxDefaults, AgentThinking } from "./shared/sandcastle.js";
8
+ export { runWithAgentLifecycle } from "./shared/lifecycle.js";
9
+ export type { AgentLifecycleLabels } from "./shared/lifecycle.js";
@@ -0,0 +1,5 @@
1
+ export { createImplementWorkflow, implement } from "./implement.js";
2
+ export { createReviewWorkflow, review } from "./review.js";
3
+ export { createUpdateBranchWorkflow, updateBranch } from "./update-branch.js";
4
+ export { runWithAgentLifecycle } from "./shared/lifecycle.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workflows/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAEnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAE1D,OAAO,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAG7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,43 @@
1
+ # TASK
2
+
3
+ Implement issue #{{ISSUE_NUMBER}}: {{ISSUE_TITLE}}
4
+
5
+ You are on branch `{{BRANCH}}`, already created from `{{BASE_REF}}`.
6
+
7
+ # ISSUE
8
+
9
+ {{ISSUE_CONTEXT}}
10
+
11
+ # CONTEXT
12
+
13
+ Read the project's domain and architecture docs before changing code, if present:
14
+
15
+ - `CONTEXT.md`, `CLAUDE.md`, or `README.md`
16
+ - `docs/adr/` or similar architecture-decision records
17
+ - Any coding standards file the repo points to
18
+
19
+ Explore the repo and relevant tests before editing.
20
+
21
+ # EXECUTION
22
+
23
+ Where a test seam already exists, or a new one is being proposed, do red-green-refactor:
24
+
25
+ 1. RED: write a failing test
26
+ 2. GREEN: implement the smallest correct change
27
+ 3. REPEAT until the issue is done
28
+ 4. REFACTOR
29
+
30
+ Do not improvise new test seams, such as extracting out a function so that it can be tested in isolation. This creates spaghetti tests.
31
+
32
+ Run this project's standard build/test command before committing (check `package.json` scripts, a `Makefile`, `CONTRIBUTING.md`, or `CLAUDE.md` for what that is). Use focused tests where relevant.
33
+
34
+ # COMMIT
35
+
36
+ Make one or more commits on `{{BRANCH}}` with conventional commit messages.
37
+
38
+ Do not push the branch.
39
+ Do not close the issue.
40
+ Do not edit labels.
41
+ Do not create or edit PRs.
42
+
43
+ When complete, output `<promise>COMPLETE</promise>`.
@@ -0,0 +1,21 @@
1
+ Emit a single `<output>` block as the last thing in your response.
2
+
3
+ Do not change files.
4
+ Do not run commands.
5
+ Do not include text outside the `<output>` block.
6
+ Do not wrap the `<output>` block in a markdown code fence (no ``` before or after it) — the JSON goes directly between the tags.
7
+
8
+ <output>
9
+ {
10
+ "summary": "1-3 paragraphs explaining your review, including what you changed or why it was already clean.",
11
+ "inlineComments": [
12
+ { "path": "relative/file.ts", "line": 123, "body": "Markdown comment" }
13
+ ],
14
+ "replies": [
15
+ { "threadId": "the \"id\" of a thread from review_threads in PR_COMMENTS_JSON", "body": "Markdown reply" }
16
+ ]
17
+ }
18
+ </output>
19
+
20
+ Use empty arrays when there are no inline comments or replies.
21
+ Even if there is nothing to change or add, still emit the full JSON object above with an empty summary-appropriate message — never reply with plain prose instead of the `<output>` block.
@@ -0,0 +1,49 @@
1
+ # TASK
2
+
3
+ Review PR #{{PR_NUMBER}} on branch `{{BRANCH}}`.
4
+
5
+ PR title: {{PR_TITLE}}
6
+ Linked issue: #{{ISSUE_NUMBER}} {{ISSUE_TITLE}}
7
+
8
+ You are an expert code reviewer. Your job is not just to comment. Actively improve the branch when a concrete improvement is warranted, then explain what you changed.
9
+
10
+ # LINKED ISSUE
11
+
12
+ {{LINKED_ISSUE}}
13
+
14
+ # DIFF TO MAIN
15
+
16
+ ```diff
17
+ {{DIFF_TO_MAIN}}
18
+ ```
19
+
20
+ # PR COMMENTS
21
+
22
+ ```json
23
+ {{PR_COMMENTS_JSON}}
24
+ ```
25
+
26
+ # REVIEW PROCESS
27
+
28
+ 1. Read the diff carefully.
29
+ 2. Verify the PR satisfies the linked issue.
30
+ 3. Stress-test edge cases and add tests where useful.
31
+ 4. Improve clarity, maintainability, and consistency while preserving behavior.
32
+ 5. Respond to unresolved human review threads when useful:
33
+ - Address: change code and reply
34
+ - Decline: do not change code, reply with why
35
+ - Defer: no reply, only for stale/context-only comments
36
+
37
+ Read this project's domain and architecture docs if present (`CONTEXT.md`, `CLAUDE.md`, `docs/adr/`, or similar).
38
+
39
+ Run this project's standard build/test command before committing if you make changes (check `package.json` scripts, a `Makefile`, `CONTRIBUTING.md`, or `CLAUDE.md` for what that is). Use focused tests where relevant.
40
+
41
+ If you make changes, commit them as a single conventional commit.
42
+ If the code is already clean and there is nothing to answer, make no commit.
43
+
44
+ Do not push.
45
+ Do not edit labels.
46
+ Do not mark review threads resolved.
47
+ Do not create GitHub comments yourself.
48
+
49
+ When complete, output `<promise>COMPLETE</promise>`.
@@ -0,0 +1,13 @@
1
+ # EMIT STRUCTURED OUTPUT
2
+
3
+ You have resolved the conflicts and committed the merge. **Do not make any further changes** — only report what you did.
4
+
5
+ Emit a single block as the last thing in your response:
6
+
7
+ <output>
8
+ {
9
+ "comment": "Markdown body posted as a PR comment. Free-form prose. Describe which conflicts existed, how you resolved each, and flag any uncertainty or remaining problems (e.g. build failures, ambiguous intent). Reference commit SHAs or file paths where useful."
10
+ }
11
+ </output>
12
+
13
+ The comment is the only safety net for the human author. Write it like you're handing the branch back to them and want them to be able to spot any bad call you made in 30 seconds.
@@ -0,0 +1,41 @@
1
+ # TASK
2
+
3
+ PR #{{PR_NUMBER}} (branch `{{BRANCH}}`) has merge conflicts against its base `{{BASE_REF}}`. A `git merge origin/{{BASE_REF}} --no-edit` has already been attempted and left the working tree in a conflicted state. Your job is to resolve every conflict, finish the merge, and write a PR comment describing what you did.
4
+
5
+ # CONTEXT
6
+
7
+ Read this project's domain and architecture docs if present (`CONTEXT.md`, `CLAUDE.md`, `docs/adr/`, or similar) before resolving anything substantive.
8
+
9
+ <pr-view>
10
+
11
+ !`gh pr view {{PR_NUMBER}}`
12
+
13
+ </pr-view>
14
+
15
+ <merge-status>
16
+
17
+ !`git status`
18
+
19
+ </merge-status>
20
+
21
+ <conflicting-files>
22
+
23
+ !`git diff --name-only --diff-filter=U`
24
+
25
+ </conflicting-files>
26
+
27
+ # RESOLUTION POLICY
28
+
29
+ Always resolve. Do not abort the merge. Do not leave the branch in a half-finished state.
30
+
31
+ For each conflicting hunk:
32
+
33
+ 1. **Investigate intent on both sides** before choosing a resolution. Use `git log -p --follow -- <path>` on both `origin/{{BASE_REF}}` and `{{BRANCH}}` to see how each side reached this state. Read the commit messages. If a commit references an issue, pull it with `gh issue view <n>`.
34
+ 2. **Pick the resolution that preserves both intents** wherever possible. Where the intents are incompatible, pick the one that best matches the PR's stated goal (in `<pr-view>` above) and note the trade-off in your comment.
35
+ 3. **Do not invent new behaviour.** Your job is reconciliation, not feature work. If a sensible resolution requires writing new logic that wasn't on either side, that's a signal to flag uncertainty rather than to be creative.
36
+
37
+ After resolving, run this project's standard build/test command (check `package.json` scripts, a `Makefile`, `CONTRIBUTING.md`, or `CLAUDE.md` for what that is) — the repo-wide check is strongest; a narrower/focused check is fine if the resolution is localized. If something is broken, fix what you can; if you can't, push what you have and flag it clearly in the comment.
38
+
39
+ # COMMIT
40
+
41
+ Stage everything and finish the merge with a single commit. Conventional-commit style, e.g. `chore: merge origin/{{BASE_REF}} into {{BRANCH}}`. The wrapper will push whatever you commit.
@@ -0,0 +1,14 @@
1
+ import type { Workflow } from "../core/workflow.js";
2
+ import { type SandboxDefaults } from "./shared/sandcastle.js";
3
+ export interface ReviewWorkflowOptions extends SandboxDefaults {
4
+ readonly id?: string;
5
+ readonly triggerLabel?: string;
6
+ readonly blockedLabel?: string;
7
+ readonly inProgressLabel?: string;
8
+ readonly promptFile?: string;
9
+ readonly extractionPrompt?: string;
10
+ readonly preflightCommand?: string;
11
+ readonly outputDir?: string;
12
+ }
13
+ export declare function createReviewWorkflow(options?: ReviewWorkflowOptions): Workflow;
14
+ export declare const review: Workflow;
@@ -0,0 +1,134 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ import { dirname, join } from "node:path";
54
+ import { fileURLToPath } from "node:url";
55
+ import { defineWorkflow, Match, isChangeRequestKind, labelAdded } from "../core/workflow.js";
56
+ import { git, pushBranch } from "./shared/git.js";
57
+ import { createPodmanSandbox, runSandboxWithExtraction } from "./shared/sandcastle.js";
58
+ import { fetchChangeRequestContext, filterInlineComments, filterReplies, validateReviewOutput } from "./shared/review-output.js";
59
+ import { runWithAgentLifecycle } from "./shared/lifecycle.js";
60
+ const here = dirname(fileURLToPath(import.meta.url));
61
+ const defaultPromptFile = join(here, "prompts/review/prompt.md");
62
+ const defaultExtractionPrompt = await import("node:fs").then(fs => fs.readFileSync(join(here, "prompts/review/extraction.md"), "utf8"));
63
+ export function createReviewWorkflow(options = {}) {
64
+ const triggerLabel = options.triggerLabel ?? "agent:review";
65
+ const promptFile = options.promptFile ?? defaultPromptFile;
66
+ const extractionPrompt = options.extractionPrompt ?? defaultExtractionPrompt;
67
+ return defineWorkflow(options.id ?? "review", {
68
+ match: ({ event }) => {
69
+ if (!labelAdded(event, triggerLabel))
70
+ return Match.ignore();
71
+ if (!isChangeRequestKind(event.workTarget.kind))
72
+ return Match.ignore();
73
+ return Match.accept();
74
+ },
75
+ run: async ({ command, workReader, workTracker, codeReviewHost }) => runWithAgentLifecycle({ workTracker, target: command.workTarget, labels: { trigger: triggerLabel, blocked: options.blockedLabel, inProgress: options.inProgressLabel } }, async () => {
76
+ const env_1 = { stack: [], error: void 0, hasError: false };
77
+ try {
78
+ const target = command.workTarget;
79
+ const outputDir = options.outputDir ?? process.env.FORGEFLOW_OUTPUT_DIR ?? "/tmp/forgeflow-output";
80
+ const details = await codeReviewHost.getChangeRequestDetails(target);
81
+ if (details.isCrossRepository)
82
+ throw new Error("Refused to run review on a cross-repository change request.");
83
+ const sandbox = __addDisposableResource(env_1, await createPodmanSandbox({
84
+ ...options,
85
+ branch: details.sourceBranch,
86
+ preflightCommand: options.preflightCommand,
87
+ }), true);
88
+ await git(["fetch", "origin", details.targetBranch], { cwd: sandbox.worktreePath });
89
+ await git(["reset", "--hard", details.sourceSha], { cwd: sandbox.worktreePath });
90
+ const context = await fetchChangeRequestContext({ target, workReader, codeReviewHost, cwd: sandbox.worktreePath });
91
+ const result = await runSandboxWithExtraction({
92
+ ...options,
93
+ sandbox,
94
+ name: `review-${target.kind}-${target.id}`,
95
+ promptFile,
96
+ extractionPrompt,
97
+ validate: validateReviewOutput,
98
+ promptArgs: {
99
+ PR_NUMBER: target.id,
100
+ BRANCH: details.sourceBranch,
101
+ PR_TITLE: context.prTitle,
102
+ ISSUE_NUMBER: context.issueNumber || "(none)",
103
+ ISSUE_TITLE: context.issueTitle || "(no linked issue)",
104
+ LINKED_ISSUE: context.linkedIssue,
105
+ DIFF_TO_MAIN: context.diff,
106
+ PR_COMMENTS_JSON: context.prCommentsJson,
107
+ },
108
+ });
109
+ const validInlineComments = filterInlineComments(result.output.inlineComments, context.diffLines);
110
+ const validReplies = filterReplies(result.output.replies, context.validReplyIds);
111
+ const headSha = await git(["rev-parse", "HEAD"], { cwd: sandbox.worktreePath });
112
+ await pushBranch({ branch: details.sourceBranch, cwd: sandbox.worktreePath, forceWithLease: `refs/heads/${details.sourceBranch}:${details.sourceSha}` });
113
+ await codeReviewHost.postReview(target, { commitSha: headSha, summary: result.output.summary, inlineComments: validInlineComments });
114
+ for (const reply of validReplies) {
115
+ await codeReviewHost.replyToThread(target, { threadId: reply.threadId, body: reply.body });
116
+ }
117
+ await codeReviewHost.markReady(target).catch(() => undefined);
118
+ await workTracker.addComment(target, `\`${triggerLabel}\` completed.`);
119
+ return { summary: result.output.summary, outputs: { commits: result.commits.length, inlineComments: validInlineComments.length, replies: validReplies.length, outputDir } };
120
+ }
121
+ catch (e_1) {
122
+ env_1.error = e_1;
123
+ env_1.hasError = true;
124
+ }
125
+ finally {
126
+ const result_1 = __disposeResources(env_1);
127
+ if (result_1)
128
+ await result_1;
129
+ }
130
+ }),
131
+ });
132
+ }
133
+ export const review = createReviewWorkflow();
134
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/workflows/review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export declare function git(args: string[], options?: {
2
+ cwd?: string;
3
+ }): Promise<string>;
4
+ export declare function configureBotGit(cwd?: string): Promise<void>;
5
+ export declare function checkoutBaseBranch(input: {
6
+ baseRef: string;
7
+ cwd?: string;
8
+ }): Promise<void>;
9
+ export declare function commitsAhead(input: {
10
+ baseRef: string;
11
+ cwd?: string;
12
+ }): Promise<number>;
13
+ export declare function pushBranch(input: {
14
+ branch: string;
15
+ cwd?: string;
16
+ forceWithLease?: string;
17
+ }): Promise<void>;
@@ -0,0 +1,28 @@
1
+ import { runProcessOrThrow } from "../../process/index.js";
2
+ export async function git(args, options = {}) {
3
+ const result = await runProcessOrThrow({ command: "git", args, cwd: options.cwd });
4
+ return result.stdout.trim();
5
+ }
6
+ export async function configureBotGit(cwd) {
7
+ await git(["config", "user.name", "forgeflow[bot]"], { cwd });
8
+ await git(["config", "user.email", "forgeflow[bot]@users.noreply.github.com"], { cwd });
9
+ }
10
+ export async function checkoutBaseBranch(input) {
11
+ await git(["fetch", "origin", `${input.baseRef}:${input.baseRef}`], { cwd: input.cwd }).catch(() => git(["fetch", "origin", input.baseRef], { cwd: input.cwd }));
12
+ await git(["checkout", input.baseRef], { cwd: input.cwd });
13
+ await git(["pull", "--ff-only", "origin", input.baseRef], { cwd: input.cwd });
14
+ await configureBotGit(input.cwd);
15
+ }
16
+ export async function commitsAhead(input) {
17
+ const count = await git(["rev-list", "--count", `${input.baseRef}..HEAD`], { cwd: input.cwd });
18
+ return Number(count.trim());
19
+ }
20
+ export async function pushBranch(input) {
21
+ if (input.forceWithLease) {
22
+ await git(["push", `--force-with-lease=${input.forceWithLease}`, "origin", input.branch], { cwd: input.cwd });
23
+ }
24
+ else {
25
+ await git(["push", "--force", "origin", input.branch], { cwd: input.cwd });
26
+ }
27
+ }
28
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/workflows/shared/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE1D,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,UAA4B,EAAE;IACtE,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAClF,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAY;IAChD,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,gBAAgB,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAA;IAC7D,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,yCAAyC,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAA;AACzF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAwC;IAC/E,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IAChK,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;IAC1D,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;IAC7E,MAAM,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAwC;IACzE,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,OAAO,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;IAC9F,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAgE;IAC/F,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QACzB,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,sBAAsB,KAAK,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;IAC/G,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;IAC5E,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { WorkTracker } from "../../core/capabilities.js";
2
+ import type { WorkTargetRef } from "../../core/types.js";
3
+ import type { WorkflowRunResult } from "../../core/workflow.js";
4
+ export interface AgentLifecycleLabels {
5
+ readonly trigger: string;
6
+ readonly blocked?: string;
7
+ readonly inProgress?: string;
8
+ }
9
+ /**
10
+ * Wraps a workflow body with the label/comment dance every agent workflow needs:
11
+ * clear the trigger + blocked labels, mark in-progress, run the body, and on
12
+ * failure add the blocked label back with a comment explaining how to retry.
13
+ * The body should simply throw on failure — this is the only place that turns
14
+ * an error into the blocked label + comment, so workflows can't double-report
15
+ * the same failure by also handling it themselves.
16
+ */
17
+ export declare function runWithAgentLifecycle(input: {
18
+ workTracker: WorkTracker;
19
+ target: WorkTargetRef;
20
+ labels: AgentLifecycleLabels;
21
+ }, body: () => Promise<WorkflowRunResult | void>): Promise<WorkflowRunResult | void>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Wraps a workflow body with the label/comment dance every agent workflow needs:
3
+ * clear the trigger + blocked labels, mark in-progress, run the body, and on
4
+ * failure add the blocked label back with a comment explaining how to retry.
5
+ * The body should simply throw on failure — this is the only place that turns
6
+ * an error into the blocked label + comment, so workflows can't double-report
7
+ * the same failure by also handling it themselves.
8
+ */
9
+ export async function runWithAgentLifecycle(input, body) {
10
+ const { workTracker, target, labels } = input;
11
+ const blockedLabel = labels.blocked ?? "agent:blocked";
12
+ const inProgressLabel = labels.inProgress ?? "agent:in-progress";
13
+ await workTracker.removeLabel(target, labels.trigger).catch(() => undefined);
14
+ await workTracker.removeLabel(target, blockedLabel).catch(() => undefined);
15
+ await workTracker.addLabel(target, inProgressLabel);
16
+ try {
17
+ return await body();
18
+ }
19
+ catch (error) {
20
+ const message = error instanceof Error ? error.message : String(error);
21
+ await workTracker.addLabel(target, blockedLabel).catch(() => undefined);
22
+ await workTracker.addComment(target, `\`${labels.trigger}\` run failed.\n\n**Reason:** ${message}\n\nRe-add \`${labels.trigger}\` or comment \`/agent retry\` to retry.`).catch(() => undefined);
23
+ throw error;
24
+ }
25
+ finally {
26
+ await workTracker.removeLabel(target, inProgressLabel).catch(() => undefined);
27
+ }
28
+ }
29
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../src/workflows/shared/lifecycle.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAAwF,EACxF,IAA6C;IAE7C,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;IAC7C,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,IAAI,eAAe,CAAA;IACtD,MAAM,eAAe,GAAG,MAAM,CAAC,UAAU,IAAI,mBAAmB,CAAA;IAEhE,MAAM,WAAW,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC5E,MAAM,WAAW,CAAC,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC1E,MAAM,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAEnD,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,EAAE,CAAA;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACtE,MAAM,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACvE,MAAM,WAAW,CAAC,UAAU,CAC1B,MAAM,EACN,KAAK,MAAM,CAAC,OAAO,iCAAiC,OAAO,gBAAgB,MAAM,CAAC,OAAO,0CAA0C,CACpI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxB,MAAM,KAAK,CAAA;IACb,CAAC;YAAS,CAAC;QACT,MAAM,WAAW,CAAC,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC/E,CAAC;AACH,CAAC"}