@mestreyoda/fabrica 0.2.37 → 0.2.39

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 (2) hide show
  1. package/dist/index.js +170 -106
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -113905,8 +113905,8 @@ import fsSync from "node:fs";
113905
113905
  import path5 from "node:path";
113906
113906
  import { fileURLToPath as fileURLToPath3 } from "node:url";
113907
113907
  function getCurrentVersion() {
113908
- if ("0.2.37") {
113909
- return "0.2.37";
113908
+ if ("0.2.39") {
113909
+ return "0.2.39";
113910
113910
  }
113911
113911
  try {
113912
113912
  const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
@@ -130477,6 +130477,157 @@ function createProjectStatusTool(ctx) {
130477
130477
  });
130478
130478
  }
130479
130479
 
130480
+ // lib/setup/doctor-run.ts
130481
+ async function runIssueDoctor(opts) {
130482
+ const data = await readProjects(opts.workspacePath);
130483
+ const project = data.projects[opts.projectSlug];
130484
+ if (!project) throw new Error(`Project not found: ${opts.projectSlug}`);
130485
+ const issueRuntime = getIssueRuntime(project, opts.issueId) ?? null;
130486
+ const hasArtifact = Boolean(
130487
+ issueRuntime?.currentPrUrl || issueRuntime?.currentPrNumber || issueRuntime?.artifactOfRecord
130488
+ );
130489
+ const { provider } = await createProvider({
130490
+ repo: project.repo,
130491
+ provider: project.provider,
130492
+ providerProfile: project.providerProfile,
130493
+ runCommand: opts.runCommand,
130494
+ pluginConfig: opts.pluginConfig
130495
+ });
130496
+ let prStatus = null;
130497
+ try {
130498
+ const pr = await provider.getPrStatus(opts.issueId);
130499
+ if (pr.url || pr.number) prStatus = pr;
130500
+ } catch {
130501
+ prStatus = null;
130502
+ }
130503
+ let issue2 = null;
130504
+ try {
130505
+ issue2 = await provider.getIssue(opts.issueId);
130506
+ } catch {
130507
+ issue2 = null;
130508
+ }
130509
+ const convergenceCause = issueRuntime?.lastConvergenceCause ?? null;
130510
+ const convergenceAction = issueRuntime?.lastConvergenceAction ?? null;
130511
+ const retryCount = issueRuntime?.lastConvergenceRetryCount ?? 0;
130512
+ const convergenceReason = issueRuntime?.lastConvergenceReason ?? issueRuntime?.inconclusiveCompletionReason ?? null;
130513
+ const convergenceHeadSha = issueRuntime?.lastConvergenceHeadSha ?? null;
130514
+ const currentHeadSha = issueRuntime?.currentPrHeadSha ?? issueRuntime?.lastHeadSha ?? prStatus?.sourceBranch ?? null;
130515
+ const headShaChangedSinceLastConvergence = convergenceHeadSha && currentHeadSha ? convergenceHeadSha !== currentHeadSha : null;
130516
+ const progressState = issueRuntime?.sessionCompletedAt ? "completed" : issueRuntime?.firstWorkerActivityAt ? "active" : issueRuntime?.agentAcceptedAt ? "accepted_idle" : "no_dispatch";
130517
+ const summaryParts = [
130518
+ hasArtifact ? "artifact_present" : "artifact_missing",
130519
+ convergenceCause ? `cause=${convergenceCause}` : "cause=none",
130520
+ convergenceAction ? `action=${convergenceAction}` : "action=none",
130521
+ retryCount ? `retries=${retryCount}` : "retries=0",
130522
+ prStatus?.state ? `pr=${prStatus.state}` : "pr=unknown"
130523
+ ];
130524
+ const likelyNextAction = (() => {
130525
+ if (convergenceAction === "escalate_human") return "human_intervention";
130526
+ if (convergenceCause === "invalid_qa_evidence") return "repair_qa_evidence";
130527
+ if (convergenceCause === "merge_conflict") return "repair_merge_conflict";
130528
+ if (convergenceCause === "stalled_with_artifact") return "force_convergence_review";
130529
+ if (hasArtifact) return "post_pr_convergence";
130530
+ return "redispatch_or_investigate";
130531
+ })();
130532
+ return {
130533
+ projectSlug: project.slug,
130534
+ projectName: project.name,
130535
+ issueId: opts.issueId,
130536
+ issueRuntime,
130537
+ hasArtifact,
130538
+ lifecycle: {
130539
+ dispatchCycleId: issueRuntime?.lastDispatchCycleId ?? null,
130540
+ dispatchRunId: issueRuntime?.dispatchRunId ?? null,
130541
+ agentAcceptedAt: issueRuntime?.agentAcceptedAt ?? null,
130542
+ firstWorkerActivityAt: issueRuntime?.firstWorkerActivityAt ?? null,
130543
+ sessionCompletedAt: issueRuntime?.sessionCompletedAt ?? null,
130544
+ progressState
130545
+ },
130546
+ convergence: {
130547
+ cause: convergenceCause,
130548
+ action: convergenceAction,
130549
+ retryCount,
130550
+ reason: convergenceReason,
130551
+ at: issueRuntime?.lastConvergenceAt ?? null,
130552
+ headSha: convergenceHeadSha,
130553
+ headShaChangedSinceLastConvergence
130554
+ },
130555
+ pr: prStatus ? {
130556
+ url: prStatus.url ?? null,
130557
+ state: prStatus.state ?? null,
130558
+ number: prStatus.number ?? null,
130559
+ mergeable: prStatus.mergeable ?? null,
130560
+ currentIssueMatch: prStatus.currentIssueMatch ?? null,
130561
+ sourceBranch: prStatus.sourceBranch ?? null
130562
+ } : null,
130563
+ issue: issue2 ? {
130564
+ url: issue2.web_url ?? null,
130565
+ state: issue2.state ?? null,
130566
+ labels: issue2.labels ?? [],
130567
+ title: issue2.title ?? null
130568
+ } : null,
130569
+ recommendation: {
130570
+ summary: summaryParts.join(" | "),
130571
+ likelyNextAction
130572
+ }
130573
+ };
130574
+ }
130575
+ function formatIssueDoctor(result) {
130576
+ const lines = [
130577
+ `Issue run doctor \u2014 ${result.projectSlug}#${result.issueId}`,
130578
+ ` Artifact: ${result.hasArtifact ? "yes" : "no"}`,
130579
+ ` PR: ${result.pr?.url ?? "n/a"} (${result.pr?.state ?? "unknown"})`,
130580
+ ` Issue: ${result.issue?.url ?? "n/a"} (${result.issue?.state ?? "unknown"})`,
130581
+ ` Labels: ${result.issue?.labels?.join(", ") ?? "n/a"}`,
130582
+ ` Dispatch cycle: ${result.lifecycle.dispatchCycleId ?? "n/a"}`,
130583
+ ` Dispatch run: ${result.lifecycle.dispatchRunId ?? "n/a"}`,
130584
+ ` Progress state: ${result.lifecycle.progressState}`,
130585
+ ` Agent accepted: ${result.lifecycle.agentAcceptedAt ?? "n/a"}`,
130586
+ ` First worker activity: ${result.lifecycle.firstWorkerActivityAt ?? "n/a"}`,
130587
+ ` Session completed: ${result.lifecycle.sessionCompletedAt ?? "n/a"}`,
130588
+ ` Convergence cause: ${result.convergence.cause ?? "none"}`,
130589
+ ` Convergence action: ${result.convergence.action ?? "none"}`,
130590
+ ` Retry count: ${result.convergence.retryCount}`,
130591
+ ` Convergence head SHA: ${result.convergence.headSha ?? "n/a"}`,
130592
+ ` Head SHA changed since last convergence: ${result.convergence.headShaChangedSinceLastConvergence == null ? "unknown" : result.convergence.headShaChangedSinceLastConvergence ? "yes" : "no"}`,
130593
+ ` Last reason: ${result.convergence.reason ?? "n/a"}`,
130594
+ ` Suggested next action: ${result.recommendation.likelyNextAction}`
130595
+ ];
130596
+ return lines.join("\n");
130597
+ }
130598
+
130599
+ // lib/tools/admin/doctor-issue.ts
130600
+ function createDoctorIssueTool(ctx) {
130601
+ return (toolCtx) => ({
130602
+ name: "doctor_issue",
130603
+ label: "Doctor Issue",
130604
+ description: "Inspect one Fabrica issue/run with convergence metadata, PR context, issue labels, and a recommended next action.",
130605
+ parameters: {
130606
+ type: "object",
130607
+ required: ["projectSlug", "issueId"],
130608
+ properties: {
130609
+ projectSlug: { type: "string", description: "Project slug (for example: fabrica or my-project)." },
130610
+ issueId: { type: "number", description: "Issue number to inspect." }
130611
+ }
130612
+ },
130613
+ async execute(_id, params) {
130614
+ const workspaceDir = requireWorkspaceDir(toolCtx);
130615
+ const projectSlug = String(params.projectSlug ?? "").trim();
130616
+ const issueId = Number(params.issueId);
130617
+ if (!projectSlug) throw new Error("projectSlug is required");
130618
+ if (!Number.isFinite(issueId)) throw new Error("issueId must be a number");
130619
+ const result = await runIssueDoctor({
130620
+ workspacePath: workspaceDir,
130621
+ projectSlug,
130622
+ issueId,
130623
+ runCommand: ctx.runCommand,
130624
+ pluginConfig: ctx.pluginConfig
130625
+ });
130626
+ return jsonResult(result);
130627
+ }
130628
+ });
130629
+ }
130630
+
130480
130631
  // lib/tools/admin/project-register.ts
130481
130632
  import fs24 from "node:fs/promises";
130482
130633
  import path24 from "node:path";
@@ -131291,9 +131442,12 @@ function decidePostPrConvergence(params) {
131291
131442
  const { workflow, issueRuntime, reason, feedbackQueueLabel } = params;
131292
131443
  const cause = classifyConvergenceCause(reason);
131293
131444
  const hasArtifact = hasReviewableArtifact(issueRuntime);
131445
+ const progressHeadSha = issueRuntime?.currentPrHeadSha ?? issueRuntime?.lastHeadSha ?? issueRuntime?.artifactOfRecord?.headSha ?? null;
131294
131446
  const previousCause = issueRuntime?.lastConvergenceCause ?? null;
131295
131447
  const previousCount = issueRuntime?.lastConvergenceRetryCount ?? 0;
131296
- const retryCount = previousCause === cause ? previousCount + 1 : 1;
131448
+ const previousHeadSha = issueRuntime?.lastConvergenceHeadSha ?? null;
131449
+ const sameHeadSha = !progressHeadSha || !previousHeadSha ? true : progressHeadSha === previousHeadSha;
131450
+ const retryCount = previousCause === cause && sameHeadSha ? previousCount + 1 : 1;
131297
131451
  const maxRetries = getConvergenceRetryBudget(cause);
131298
131452
  const holdLabel = getPreferredHoldLabel(workflow);
131299
131453
  const shouldEscalate = hasArtifact && retryCount > maxRetries && Boolean(holdLabel);
@@ -131303,7 +131457,8 @@ function decidePostPrConvergence(params) {
131303
131457
  targetLabel: shouldEscalate ? holdLabel ?? feedbackQueueLabel : feedbackQueueLabel,
131304
131458
  retryCount,
131305
131459
  maxRetries,
131306
- hasArtifact
131460
+ hasArtifact,
131461
+ progressHeadSha
131307
131462
  };
131308
131463
  }
131309
131464
 
@@ -131895,7 +132050,8 @@ ${validationReason}`;
131895
132050
  lastConvergenceAction: convergence.action,
131896
132051
  lastConvergenceRetryCount: convergence.retryCount,
131897
132052
  lastConvergenceReason: validationReason,
131898
- lastConvergenceAt: (/* @__PURE__ */ new Date()).toISOString()
132053
+ lastConvergenceAt: (/* @__PURE__ */ new Date()).toISOString(),
132054
+ lastConvergenceHeadSha: convergence.progressHeadSha
131899
132055
  }).catch(() => {
131900
132056
  });
131901
132057
  await executeCompletion({
@@ -132021,7 +132177,8 @@ ${validationReason}`;
132021
132177
  lastConvergenceAction: null,
132022
132178
  lastConvergenceRetryCount: 0,
132023
132179
  lastConvergenceReason: null,
132024
- lastConvergenceAt: null
132180
+ lastConvergenceAt: null,
132181
+ lastConvergenceHeadSha: null
132025
132182
  }).catch(() => {
132026
132183
  });
132027
132184
  } else {
@@ -132032,7 +132189,8 @@ ${validationReason}`;
132032
132189
  lastConvergenceAction: null,
132033
132190
  lastConvergenceRetryCount: 0,
132034
132191
  lastConvergenceReason: null,
132035
- lastConvergenceAt: null
132192
+ lastConvergenceAt: null,
132193
+ lastConvergenceHeadSha: null
132036
132194
  }).catch(() => {
132037
132195
  });
132038
132196
  }
@@ -132555,7 +132713,8 @@ async function checkWorkerHealth(opts) {
132555
132713
  lastConvergenceAction: convergence.action,
132556
132714
  lastConvergenceRetryCount: convergence.retryCount,
132557
132715
  lastConvergenceReason: inconclusiveReason,
132558
- lastConvergenceAt: (/* @__PURE__ */ new Date()).toISOString()
132716
+ lastConvergenceAt: (/* @__PURE__ */ new Date()).toISOString(),
132717
+ lastConvergenceHeadSha: convergence.progressHeadSha
132559
132718
  }).catch(() => {
132560
132719
  });
132561
132720
  fix.fixed = true;
@@ -132938,7 +133097,8 @@ async function checkWorkerHealth(opts) {
132938
133097
  lastConvergenceAction: convergence.action,
132939
133098
  lastConvergenceRetryCount: convergence.retryCount,
132940
133099
  lastConvergenceReason: "stalled_with_artifact",
132941
- lastConvergenceAt: (/* @__PURE__ */ new Date()).toISOString()
133100
+ lastConvergenceAt: (/* @__PURE__ */ new Date()).toISOString(),
133101
+ lastConvergenceHeadSha: convergence.progressHeadSha
132942
133102
  }).catch(() => {
132943
133103
  });
132944
133104
  fix.fixed = true;
@@ -148951,103 +149111,6 @@ async function checkProjects(dataDir) {
148951
149111
  return results;
148952
149112
  }
148953
149113
 
148954
- // lib/setup/doctor-run.ts
148955
- async function runIssueDoctor(opts) {
148956
- const data = await readProjects(opts.workspacePath);
148957
- const project = data.projects[opts.projectSlug];
148958
- if (!project) throw new Error(`Project not found: ${opts.projectSlug}`);
148959
- const issueRuntime = getIssueRuntime(project, opts.issueId) ?? null;
148960
- const hasArtifact = Boolean(
148961
- issueRuntime?.currentPrUrl || issueRuntime?.currentPrNumber || issueRuntime?.artifactOfRecord
148962
- );
148963
- const { provider } = await createProvider({
148964
- repo: project.repo,
148965
- provider: project.provider,
148966
- providerProfile: project.providerProfile,
148967
- runCommand: opts.runCommand,
148968
- pluginConfig: opts.pluginConfig
148969
- });
148970
- let prStatus = null;
148971
- try {
148972
- const pr = await provider.getPrStatus(opts.issueId);
148973
- if (pr.url || pr.number) prStatus = pr;
148974
- } catch {
148975
- prStatus = null;
148976
- }
148977
- let issue2 = null;
148978
- try {
148979
- issue2 = await provider.getIssue(opts.issueId);
148980
- } catch {
148981
- issue2 = null;
148982
- }
148983
- const convergenceCause = issueRuntime?.lastConvergenceCause ?? null;
148984
- const convergenceAction = issueRuntime?.lastConvergenceAction ?? null;
148985
- const retryCount = issueRuntime?.lastConvergenceRetryCount ?? 0;
148986
- const convergenceReason = issueRuntime?.lastConvergenceReason ?? issueRuntime?.inconclusiveCompletionReason ?? null;
148987
- const summaryParts = [
148988
- hasArtifact ? "artifact_present" : "artifact_missing",
148989
- convergenceCause ? `cause=${convergenceCause}` : "cause=none",
148990
- convergenceAction ? `action=${convergenceAction}` : "action=none",
148991
- retryCount ? `retries=${retryCount}` : "retries=0",
148992
- prStatus?.state ? `pr=${prStatus.state}` : "pr=unknown"
148993
- ];
148994
- const likelyNextAction = (() => {
148995
- if (convergenceAction === "escalate_human") return "human_intervention";
148996
- if (convergenceCause === "invalid_qa_evidence") return "repair_qa_evidence";
148997
- if (convergenceCause === "merge_conflict") return "repair_merge_conflict";
148998
- if (convergenceCause === "stalled_with_artifact") return "force_convergence_review";
148999
- if (hasArtifact) return "post_pr_convergence";
149000
- return "redispatch_or_investigate";
149001
- })();
149002
- return {
149003
- projectSlug: project.slug,
149004
- projectName: project.name,
149005
- issueId: opts.issueId,
149006
- issueRuntime,
149007
- hasArtifact,
149008
- convergence: {
149009
- cause: convergenceCause,
149010
- action: convergenceAction,
149011
- retryCount,
149012
- reason: convergenceReason,
149013
- at: issueRuntime?.lastConvergenceAt ?? null
149014
- },
149015
- pr: prStatus ? {
149016
- url: prStatus.url ?? null,
149017
- state: prStatus.state ?? null,
149018
- number: prStatus.number ?? null,
149019
- mergeable: prStatus.mergeable ?? null,
149020
- currentIssueMatch: prStatus.currentIssueMatch ?? null,
149021
- sourceBranch: prStatus.sourceBranch ?? null
149022
- } : null,
149023
- issue: issue2 ? {
149024
- url: issue2.web_url ?? null,
149025
- state: issue2.state ?? null,
149026
- labels: issue2.labels ?? [],
149027
- title: issue2.title ?? null
149028
- } : null,
149029
- recommendation: {
149030
- summary: summaryParts.join(" | "),
149031
- likelyNextAction
149032
- }
149033
- };
149034
- }
149035
- function formatIssueDoctor(result) {
149036
- const lines = [
149037
- `Issue run doctor \u2014 ${result.projectSlug}#${result.issueId}`,
149038
- ` Artifact: ${result.hasArtifact ? "yes" : "no"}`,
149039
- ` PR: ${result.pr?.url ?? "n/a"} (${result.pr?.state ?? "unknown"})`,
149040
- ` Issue: ${result.issue?.url ?? "n/a"} (${result.issue?.state ?? "unknown"})`,
149041
- ` Labels: ${result.issue?.labels?.join(", ") ?? "n/a"}`,
149042
- ` Convergence cause: ${result.convergence.cause ?? "none"}`,
149043
- ` Convergence action: ${result.convergence.action ?? "none"}`,
149044
- ` Retry count: ${result.convergence.retryCount}`,
149045
- ` Last reason: ${result.convergence.reason ?? "n/a"}`,
149046
- ` Suggested next action: ${result.recommendation.likelyNextAction}`
149047
- ];
149048
- return lines.join("\n");
149049
- }
149050
-
149051
149114
  // lib/observability/metrics.ts
149052
149115
  init_constants();
149053
149116
  import { readFile as readFile3 } from "node:fs/promises";
@@ -151020,6 +151083,7 @@ var plugin = {
151020
151083
  api.registerTool(createTaskListTool(ctx), { names: ["task_list"] });
151021
151084
  api.registerTool(createTasksStatusTool(ctx), { names: ["tasks_status"] });
151022
151085
  api.registerTool(createProjectStatusTool(ctx), { names: ["project_status"] });
151086
+ api.registerTool(createDoctorIssueTool(ctx), { names: ["doctor_issue"] });
151023
151087
  api.registerTool(createProjectRegisterTool(ctx), { names: ["project_register"] });
151024
151088
  api.registerTool(createHealthTool(ctx), { names: ["health"] });
151025
151089
  api.registerTool(createSyncLabelsTool(ctx), { names: ["sync_labels"] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mestreyoda/fabrica",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "description": "Autonomous software engineering pipeline for OpenClaw. Turns ideas into deployed code via intake, dispatch, review, test, and merge.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",