@mestreyoda/fabrica 0.2.7 → 0.2.9

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.
@@ -91,49 +91,31 @@ If you discover unrelated bugs or needed improvements, call `task_create`:
91
91
 
92
92
  ## Completing Your Task
93
93
 
94
- When you are done, submit your review using **Method A** if the tools are available, or **Method B** otherwise.
95
-
96
- ### Method A — Fabrica tools (preferred)
97
-
98
- 1. Call `review_submit` to write the review artifact to the PR:
99
- - **Approve:** `review_submit({ channelId: "<project slug>", issueId: <issue number>, result: "approve", body: "<what you checked>" })`
100
- - **Reject:** `review_submit({ channelId: "<project slug>", issueId: <issue number>, result: "reject", body: "<specific issues>" })`
101
- - Capture the returned `artifactId` and `artifactType`.
102
- 2. Then call `work_finish`:
103
- - **Approve:** `work_finish({ role: "reviewer", result: "approve", channelId: "<project slug>", summary: "<what you checked>", reviewArtifactId: <artifactId>, reviewArtifactType: "<artifactType>" })`
104
- - **Reject:** `work_finish({ role: "reviewer", result: "reject", channelId: "<project slug>", summary: "<specific issues>", reviewArtifactId: <artifactId>, reviewArtifactType: "<artifactType>" })`
105
- - **Blocked:** `work_finish({ role: "reviewer", result: "blocked", channelId: "<project slug>", summary: "<what you need>" })`
106
-
107
- > **IMPORTANT:** The `channelId` parameter accepts the project slug (e.g., "gestao-notas").
108
- > Extract it from the "Project: <name>" line in your task message. Do NOT use the numeric
109
- > channel ID — use the project slug to avoid resolution errors when channels are shared.
110
-
111
- ### Method B — GitHub CLI fallback (use only if `review_submit` / `work_finish` are unavailable)
112
-
113
- Extract from your task message:
114
- - `OWNER/REPO` from the `Repo:` line
115
- - `PR_NUMBER` from the PR URL in the diff header or the `Branch:` line
116
- - `ISSUE_NUMBER` from the `Issue:` field
117
-
118
- **Approve:**
119
- ```bash
120
- gh pr review PR_NUMBER --repo OWNER/REPO --approve -b "$(cat <<'EOF'
121
- <your full review body here>
122
- EOF
123
- )"
124
- ```
94
+ After writing your review, you **MUST** output your final decision on a dedicated line in **exactly** one of these two formats:
125
95
 
126
- **Reject (request changes):**
127
- ```bash
128
- gh pr review PR_NUMBER --repo OWNER/REPO --request-changes -b "$(cat <<'EOF'
129
- <specific issues and how to fix them>
130
- EOF
131
- )"
132
96
  ```
97
+ Review result: APPROVE
98
+ ```
99
+ ```
100
+ Review result: REJECT
101
+ ```
102
+
103
+ The Fabrica orchestrator reads your session output and advances the pipeline automatically based on this line. **You do not need to call any tool or run any CLI command.** Just output the line above and your work is done.
104
+
105
+ - Output `Review result: APPROVE` if all quality gates pass and the code is ready to proceed.
106
+ - Output `Review result: REJECT` if any blocking issue was found that the developer must fix.
133
107
 
134
- After submitting via `gh pr review`, the Fabrica heartbeat will detect the PR review state and advance the pipeline automatically. **Do NOT manually edit issue labels.**
108
+ > **IMPORTANT:** The decision line must appear in your response text, not inside a code block. It is case-insensitive but must follow the `Review result: APPROVE/REJECT` format exactly.
109
+
110
+ ### Optional: submit PR comment (best-effort)
111
+
112
+ If `gh` is available and the PR author is not the same GitHub account you are logged in as, you may optionally leave a PR comment for visibility:
113
+
114
+ ```
115
+ gh pr comment <PR_NUMBER> --repo <OWNER/REPO> --body "<summary of your findings>"
116
+ ```
135
117
 
136
- **Never call `task_comment` for review findings.** Your review must be posted on the PR itself.
118
+ This is informational only the orchestrator does not require it to advance the pipeline.
137
119
 
138
120
  ## Tools You Should NOT Use
139
121
 
package/dist/index.js CHANGED
@@ -111330,8 +111330,8 @@ import fsSync from "node:fs";
111330
111330
  import path5 from "node:path";
111331
111331
  import { fileURLToPath as fileURLToPath3 } from "node:url";
111332
111332
  function getCurrentVersion() {
111333
- if ("0.2.7") {
111334
- return "0.2.7";
111333
+ if ("0.2.9") {
111334
+ return "0.2.9";
111335
111335
  }
111336
111336
  try {
111337
111337
  const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
@@ -139562,6 +139562,36 @@ function registerReactiveDispatchHooks(api, ctx) {
139562
139562
 
139563
139563
  // lib/dispatch/subagent-lifecycle-hook.ts
139564
139564
  init_workflow();
139565
+ init_labels();
139566
+ async function parseReviewerSessionResult(ctx, sessionKey) {
139567
+ try {
139568
+ const messagesResult = await ctx.runtime.subagent?.getSessionMessages?.({ sessionKey });
139569
+ if (!messagesResult) return null;
139570
+ const messages = Array.isArray(messagesResult) ? messagesResult : Array.isArray(messagesResult?.messages) ? messagesResult.messages : [];
139571
+ const assistantTexts = messages.filter((m2) => m2.role === "assistant").map((m2) => {
139572
+ const raw = m2.content;
139573
+ if (typeof raw === "string") return raw;
139574
+ if (Array.isArray(raw)) {
139575
+ return raw.find((b) => b.type === "text")?.text ?? "";
139576
+ }
139577
+ return "";
139578
+ }).filter(Boolean).reverse();
139579
+ for (const text of assistantTexts) {
139580
+ const lower2 = text.toLowerCase();
139581
+ if (/review\s+result\s*:\s*approve/i.test(text)) return "approve";
139582
+ if (/review\s+result\s*:\s*reject/i.test(text)) return "reject";
139583
+ if (/\*\*approved\*\*/i.test(text)) return "approve";
139584
+ if (/\*\*rejected\*\*/i.test(text)) return "reject";
139585
+ const approveMatch = lower2.match(/\b(approve|approved|lgtm|looks good)\b/);
139586
+ const rejectMatch = lower2.match(/\b(reject|rejected|request[s]?\s+changes?|changes\s+requested)\b/);
139587
+ if (approveMatch && !rejectMatch) return "approve";
139588
+ if (rejectMatch && !approveMatch) return "reject";
139589
+ }
139590
+ return null;
139591
+ } catch {
139592
+ return null;
139593
+ }
139594
+ }
139565
139595
  function registerSubagentLifecycleHook(api, ctx) {
139566
139596
  const workspaceDir = resolveWorkspaceDir(ctx.config);
139567
139597
  if (!workspaceDir) return;
@@ -139625,7 +139655,42 @@ function registerSubagentLifecycleHook(api, ctx) {
139625
139655
  runCommand: ctx.runCommand
139626
139656
  });
139627
139657
  const issue2 = await provider.getIssue(Number(issueId));
139628
- if (issue2.labels.includes(activeLabel)) {
139658
+ if (!issue2.labels.includes(activeLabel)) {
139659
+ } else if (role === "reviewer") {
139660
+ const reviewResult = await parseReviewerSessionResult(ctx, sessionKey);
139661
+ if (reviewResult === "approve" || reviewResult === "reject") {
139662
+ const reviewingState = findStateByLabel(config2.workflow, activeLabel);
139663
+ const eventKey = reviewResult === "approve" ? WorkflowEvent.APPROVED : WorkflowEvent.CHANGES_REQUESTED;
139664
+ const transition2 = reviewingState?.on?.[eventKey];
139665
+ const targetKey = typeof transition2 === "string" ? transition2 : transition2?.target;
139666
+ const targetState = targetKey ? config2.workflow.states[targetKey] : void 0;
139667
+ if (targetState) {
139668
+ await resilientLabelTransition(provider, Number(issueId), activeLabel, targetState.label);
139669
+ await log(workspaceDir, "reviewer_session_transition", {
139670
+ sessionKey,
139671
+ project: projectName,
139672
+ issueId,
139673
+ result: reviewResult,
139674
+ from: activeLabel,
139675
+ to: targetState.label
139676
+ }).catch(() => {
139677
+ });
139678
+ ctx.logger.info(
139679
+ `reviewer_session_transition: issue #${issueId} \u2192 ${targetState.label} (result=${reviewResult})`
139680
+ );
139681
+ } else {
139682
+ await provider.transitionLabel(Number(issueId), activeLabel, revertLabel);
139683
+ }
139684
+ } else {
139685
+ await provider.transitionLabel(Number(issueId), activeLabel, revertLabel);
139686
+ await log(workspaceDir, "reviewer_session_no_result", {
139687
+ sessionKey,
139688
+ project: projectName,
139689
+ issueId
139690
+ }).catch(() => {
139691
+ });
139692
+ }
139693
+ } else {
139629
139694
  await provider.transitionLabel(Number(issueId), activeLabel, revertLabel);
139630
139695
  }
139631
139696
  } catch {