@mestreyoda/fabrica 0.2.6 → 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.
@@ -73,8 +73,7 @@ Do **not** treat the task envelope (`Repo:`, `Project:`, `Channel:`, branch hint
73
73
 
74
74
  - Read the PR diff carefully
75
75
  - Check the code against the review checklist
76
- - Call `review_submit` with your review findings so the artifact is written to the PR itself
77
- - Then call `work_finish`
76
+ - Submit your review using **one of the two methods below** (prefer `review_submit` if available)
78
77
 
79
78
  ## Conventions
80
79
 
@@ -92,21 +91,31 @@ If you discover unrelated bugs or needed improvements, call `task_create`:
92
91
 
93
92
  ## Completing Your Task
94
93
 
95
- When you are done, submit the review artifact first, then **call `work_finish` yourself** do not just announce in text.
94
+ After writing your review, you **MUST** output your final decision on a dedicated line in **exactly** one of these two formats:
96
95
 
97
- - **Approve review artifact:** `review_submit({ channelId: "<project slug from 'Project:' field in task message>", issueId: <issue number>, result: "approve", body: "<what you checked>" })`
98
- - **Reject review artifact:** `review_submit({ channelId: "<project slug from 'Project:' field in task message>", issueId: <issue number>, result: "reject", body: "<specific issues>" })`
99
- - Capture the returned `artifactId` and `artifactType` from `review_submit`.
96
+ ```
97
+ Review result: APPROVE
98
+ ```
99
+ ```
100
+ Review result: REJECT
101
+ ```
100
102
 
101
- **Never call `task_comment` for review findings.** The orchestrator may mirror your result to the issue separately, but your authoritative feedback must live on the PR via `review_submit`.
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.
102
104
 
103
- - **Approve:** `work_finish({ role: "reviewer", result: "approve", channelId: "<project slug from 'Project:' field in task message>", summary: "<what you checked>", reviewArtifactId: <artifactId>, reviewArtifactType: "<artifactType>" })`
104
- - **Reject:** `work_finish({ role: "reviewer", result: "reject", channelId: "<project slug from 'Project:' field in task message>", summary: "<specific issues>", reviewArtifactId: <artifactId>, reviewArtifactType: "<artifactType>" })`
105
- - **Blocked:** `work_finish({ role: "reviewer", result: "blocked", channelId: "<project slug from 'Project:' field in task message>", summary: "<what you need>" })`
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.
106
107
 
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.
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
+ ```
117
+
118
+ This is informational only — the orchestrator does not require it to advance the pipeline.
110
119
 
111
120
  ## Tools You Should NOT Use
112
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.6") {
111334
- return "0.2.6";
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");
@@ -130791,7 +130791,7 @@ async function reviewPass(opts) {
130791
130791
  const issues = await provider.listIssuesByLabel(state.label);
130792
130792
  for (const issue2 of issues) {
130793
130793
  const routing = detectStepRouting(issue2.labels, "review");
130794
- if (routing !== "human") continue;
130794
+ if (routing !== "human" && routing !== "agent") continue;
130795
130795
  const isManaged = await provider.issueHasReaction(issue2.iid, "eyes");
130796
130796
  if (!isManaged) continue;
130797
130797
  const status = await provider.getPrStatus(issue2.iid);
@@ -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 {