@wickdninja/sweny-engine 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 (78) hide show
  1. package/README.md +75 -0
  2. package/dist/index.d.ts +6 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +6 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/recipes/triage/index.d.ts +7 -0
  7. package/dist/recipes/triage/index.d.ts.map +1 -0
  8. package/dist/recipes/triage/index.js +30 -0
  9. package/dist/recipes/triage/index.js.map +1 -0
  10. package/dist/recipes/triage/prompts.d.ts +6 -0
  11. package/dist/recipes/triage/prompts.d.ts.map +1 -0
  12. package/dist/recipes/triage/prompts.js +279 -0
  13. package/dist/recipes/triage/prompts.js.map +1 -0
  14. package/dist/recipes/triage/results.d.ts +14 -0
  15. package/dist/recipes/triage/results.d.ts.map +1 -0
  16. package/dist/recipes/triage/results.js +14 -0
  17. package/dist/recipes/triage/results.js.map +1 -0
  18. package/dist/recipes/triage/service-map.d.ts +15 -0
  19. package/dist/recipes/triage/service-map.d.ts.map +1 -0
  20. package/dist/recipes/triage/service-map.js +56 -0
  21. package/dist/recipes/triage/service-map.js.map +1 -0
  22. package/dist/recipes/triage/steps/build-context.d.ts +5 -0
  23. package/dist/recipes/triage/steps/build-context.d.ts.map +1 -0
  24. package/dist/recipes/triage/steps/build-context.js +85 -0
  25. package/dist/recipes/triage/steps/build-context.js.map +1 -0
  26. package/dist/recipes/triage/steps/create-issue.d.ts +5 -0
  27. package/dist/recipes/triage/steps/create-issue.d.ts.map +1 -0
  28. package/dist/recipes/triage/steps/create-issue.js +85 -0
  29. package/dist/recipes/triage/steps/create-issue.js.map +1 -0
  30. package/dist/recipes/triage/steps/create-pr.d.ts +5 -0
  31. package/dist/recipes/triage/steps/create-pr.d.ts.map +1 -0
  32. package/dist/recipes/triage/steps/create-pr.js +97 -0
  33. package/dist/recipes/triage/steps/create-pr.js.map +1 -0
  34. package/dist/recipes/triage/steps/cross-repo-check.d.ts +5 -0
  35. package/dist/recipes/triage/steps/cross-repo-check.d.ts.map +1 -0
  36. package/dist/recipes/triage/steps/cross-repo-check.js +44 -0
  37. package/dist/recipes/triage/steps/cross-repo-check.js.map +1 -0
  38. package/dist/recipes/triage/steps/implement-fix.d.ts +5 -0
  39. package/dist/recipes/triage/steps/implement-fix.d.ts.map +1 -0
  40. package/dist/recipes/triage/steps/implement-fix.js +100 -0
  41. package/dist/recipes/triage/steps/implement-fix.js.map +1 -0
  42. package/dist/recipes/triage/steps/investigate.d.ts +5 -0
  43. package/dist/recipes/triage/steps/investigate.d.ts.map +1 -0
  44. package/dist/recipes/triage/steps/investigate.js +74 -0
  45. package/dist/recipes/triage/steps/investigate.js.map +1 -0
  46. package/dist/recipes/triage/steps/notify.d.ts +5 -0
  47. package/dist/recipes/triage/steps/notify.d.ts.map +1 -0
  48. package/dist/recipes/triage/steps/notify.js +66 -0
  49. package/dist/recipes/triage/steps/notify.js.map +1 -0
  50. package/dist/recipes/triage/steps/novelty-gate.d.ts +5 -0
  51. package/dist/recipes/triage/steps/novelty-gate.d.ts.map +1 -0
  52. package/dist/recipes/triage/steps/novelty-gate.js +60 -0
  53. package/dist/recipes/triage/steps/novelty-gate.js.map +1 -0
  54. package/dist/recipes/triage/steps/verify-access.d.ts +5 -0
  55. package/dist/recipes/triage/steps/verify-access.d.ts.map +1 -0
  56. package/dist/recipes/triage/steps/verify-access.js +11 -0
  57. package/dist/recipes/triage/steps/verify-access.js.map +1 -0
  58. package/dist/recipes/triage/test-helpers.d.ts +17 -0
  59. package/dist/recipes/triage/test-helpers.d.ts.map +1 -0
  60. package/dist/recipes/triage/test-helpers.js +41 -0
  61. package/dist/recipes/triage/test-helpers.js.map +1 -0
  62. package/dist/recipes/triage/types.d.ts +79 -0
  63. package/dist/recipes/triage/types.d.ts.map +1 -0
  64. package/dist/recipes/triage/types.js +2 -0
  65. package/dist/recipes/triage/types.js.map +1 -0
  66. package/dist/registry.d.ts +4 -0
  67. package/dist/registry.d.ts.map +1 -0
  68. package/dist/registry.js +19 -0
  69. package/dist/registry.js.map +1 -0
  70. package/dist/runner.d.ts +13 -0
  71. package/dist/runner.d.ts.map +1 -0
  72. package/dist/runner.js +98 -0
  73. package/dist/runner.js.map +1 -0
  74. package/dist/types.d.ts +77 -0
  75. package/dist/types.d.ts.map +1 -0
  76. package/dist/types.js +2 -0
  77. package/dist/types.js.map +1 -0
  78. package/package.json +39 -0
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # @swenyai/engine
2
+
3
+ Workflow engine for SWEny -- orchestrates **Learn -> Act -> Report** pipelines with pluggable providers.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @swenyai/engine @swenyai/providers
9
+ ```
10
+
11
+ ## Quick Example
12
+
13
+ ```typescript
14
+ import { runWorkflow, createProviderRegistry } from "@swenyai/engine";
15
+ import { datadog } from "@swenyai/providers/observability";
16
+ import { linear } from "@swenyai/providers/issue-tracking";
17
+ import { github } from "@swenyai/providers/source-control";
18
+ import { slackWebhook } from "@swenyai/providers/notification";
19
+ import { claudeCode } from "@swenyai/providers/coding-agent";
20
+
21
+ // 1. Register providers
22
+ const providers = createProviderRegistry();
23
+ providers.set("observability", datadog({ apiKey, appKey }));
24
+ providers.set("issueTracker", linear({ apiKey }));
25
+ providers.set("sourceControl", github({ token, owner, repo }));
26
+ providers.set("notification", slackWebhook({ webhookUrl }));
27
+ providers.set("codingAgent", claudeCode({}));
28
+
29
+ // 2. Run a recipe
30
+ import { triageWorkflow } from "@swenyai/engine/recipes/triage";
31
+
32
+ const result = await runWorkflow(triageWorkflow, triageConfig, providers);
33
+ console.log(result.status); // "completed" | "failed" | "partial"
34
+ ```
35
+
36
+ ## Concepts
37
+
38
+ **Workflow** -- A pipeline of steps organized into three phases: Learn, Act, Report.
39
+
40
+ **Step** -- A single unit of work (e.g., "query logs", "create issue", "send notification").
41
+
42
+ **Recipe** -- A pre-built workflow. SWEny Triage is the first recipe.
43
+
44
+ **ProviderRegistry** -- A type-safe container for pluggable service integrations.
45
+
46
+ ## Built-in Recipes
47
+
48
+ | Recipe | Description |
49
+ |--------|-------------|
50
+ | **Triage** | Monitor observability logs -> investigate with AI -> create tickets -> open fix PRs -> notify |
51
+
52
+ ## Creating a Custom Workflow
53
+
54
+ ```typescript
55
+ import type { Workflow } from "@swenyai/engine";
56
+
57
+ const myWorkflow: Workflow<MyConfig> = {
58
+ name: "my-workflow",
59
+ phases: {
60
+ learn: [
61
+ { name: "fetch-data", phase: "learn", run: fetchData },
62
+ ],
63
+ act: [
64
+ { name: "process", phase: "act", run: processData },
65
+ ],
66
+ report: [
67
+ { name: "notify", phase: "report", run: sendReport },
68
+ ],
69
+ },
70
+ };
71
+ ```
72
+
73
+ ## License
74
+
75
+ [MIT](../../LICENSE)
@@ -0,0 +1,6 @@
1
+ export type { WorkflowPhase, StepResult, WorkflowStep, WorkflowContext, ProviderRegistry, Workflow, WorkflowResult, RunOptions, } from "./types.js";
2
+ export { runWorkflow, createProviderRegistry } from "./runner.js";
3
+ export { triageWorkflow } from "./recipes/triage/index.js";
4
+ export type { TriageConfig, InvestigationResult, ImplementResult, BuildContextData, IssueData, ImplementFixData, PrData, CrossRepoData, TriageStepDataMap, } from "./recipes/triage/index.js";
5
+ export { getStepData } from "./recipes/triage/index.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,aAAa,EACb,UAAU,EACV,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGlE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,gBAAgB,EAChB,MAAM,EACN,aAAa,EACb,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // Runtime
2
+ export { runWorkflow, createProviderRegistry } from "./runner.js";
3
+ // Recipes
4
+ export { triageWorkflow } from "./recipes/triage/index.js";
5
+ export { getStepData } from "./recipes/triage/index.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,UAAU;AACV,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAElE,UAAU;AACV,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAY3D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Workflow } from "../../types.js";
2
+ import type { TriageConfig } from "./types.js";
3
+ /** The triage recipe — first workflow on the SWEny platform. */
4
+ export declare const triageWorkflow: Workflow<TriageConfig>;
5
+ export type { TriageConfig, InvestigationResult, ImplementResult, BuildContextData, IssueData, ImplementFixData, PrData, CrossRepoData, TriageStepDataMap, } from "./types.js";
6
+ export { getStepData } from "./results.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/recipes/triage/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAW/C,gEAAgE;AAChE,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,YAAY,CAmBjD,CAAC;AAEF,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,gBAAgB,EAChB,MAAM,EACN,aAAa,EACb,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { verifyAccess } from "./steps/verify-access.js";
2
+ import { buildContext } from "./steps/build-context.js";
3
+ import { investigate } from "./steps/investigate.js";
4
+ import { noveltyGate } from "./steps/novelty-gate.js";
5
+ import { createIssue } from "./steps/create-issue.js";
6
+ import { crossRepoCheck } from "./steps/cross-repo-check.js";
7
+ import { implementFix } from "./steps/implement-fix.js";
8
+ import { createPr } from "./steps/create-pr.js";
9
+ import { sendNotification } from "./steps/notify.js";
10
+ /** The triage recipe — first workflow on the SWEny platform. */
11
+ export const triageWorkflow = {
12
+ name: "triage",
13
+ description: "Investigate production issues, implement fixes, and report results",
14
+ steps: [
15
+ // Learn phase — gather data
16
+ { name: "verify-access", phase: "learn", run: verifyAccess },
17
+ { name: "build-context", phase: "learn", run: buildContext },
18
+ { name: "investigate", phase: "learn", run: investigate },
19
+ // Act phase — fix the problem
20
+ { name: "novelty-gate", phase: "act", run: noveltyGate },
21
+ { name: "create-issue", phase: "act", run: createIssue },
22
+ { name: "cross-repo-check", phase: "act", run: crossRepoCheck },
23
+ { name: "implement-fix", phase: "act", run: implementFix },
24
+ { name: "create-pr", phase: "act", run: createPr },
25
+ // Report phase — notify stakeholders
26
+ { name: "notify", phase: "report", run: sendNotification },
27
+ ],
28
+ };
29
+ export { getStepData } from "./results.js";
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/recipes/triage/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,gEAAgE;AAChE,MAAM,CAAC,MAAM,cAAc,GAA2B;IACpD,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,oEAAoE;IACjF,KAAK,EAAE;QACL,4BAA4B;QAC5B,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE;QAC5D,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE;QAC5D,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE;QAEzD,8BAA8B;QAC9B,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE;QACxD,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE;QACxD,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,EAAE;QAC/D,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE;QAC1D,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE;QAElD,qCAAqC;QACrC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,gBAAgB,EAAE;KAC3D;CACF,CAAC;AAaF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ObservabilityProvider } from "@swenyai/providers/observability";
2
+ import type { TriageConfig } from "./types.js";
3
+ export declare function buildInvestigationPrompt(config: TriageConfig, observability: ObservabilityProvider, knownIssuesContent: string): string;
4
+ export declare function buildImplementPrompt(issueIdentifier: string): string;
5
+ export declare function buildPrDescriptionPrompt(issueIdentifier: string, issueUrl: string): string;
6
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/recipes/triage/prompts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAO/C,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,YAAY,EACpB,aAAa,EAAE,qBAAqB,EACpC,kBAAkB,EAAE,MAAM,GACzB,MAAM,CAgMR;AAMD,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CA0CpE;AAMD,wBAAgB,wBAAwB,CAAC,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAoC1F"}
@@ -0,0 +1,279 @@
1
+ import * as fs from "fs";
2
+ import { parseServiceMap } from "./service-map.js";
3
+ // ---------------------------------------------------------------------------
4
+ // Investigation Prompt
5
+ // ---------------------------------------------------------------------------
6
+ export function buildInvestigationPrompt(config, observability, knownIssuesContent) {
7
+ const parts = [];
8
+ parts.push(`You are an autonomous SRE agent investigating production issues.
9
+ You have access to multiple tools and data sources. Your job is to investigate issues,
10
+ understand problems, and prepare fixes.
11
+
12
+ ## CURRENT REPO
13
+ You are running inside: **${config.repository}**
14
+
15
+ ## YOUR INPUTS - REVIEW THESE FIRST
16
+
17
+ ### Linear Issue
18
+ ${config.issueOverride || "(none provided)"}
19
+
20
+ ### Additional Instructions
21
+ ${config.additionalInstructions || "(none provided)"}
22
+
23
+ ### Cross-Repo Dispatch
24
+ Dispatched from: (not dispatched — this is a direct run)
25
+ Context from dispatcher: (none)
26
+
27
+ ### Investigation Parameters
28
+ - Service Pattern: ${config.serviceFilter}
29
+ - Time Range: ${config.timeRange}
30
+ - Focus Area: ${config.severityFocus}
31
+ - Investigation Depth: ${config.investigationDepth}
32
+
33
+ ## DECIDE YOUR APPROACH
34
+
35
+ Based on the inputs above, decide how to proceed:
36
+
37
+ 1. **If a Linear Issue is provided** (e.g., ENG-123):
38
+ - Fetch the issue details and comments from Linear using the API
39
+ - Understand what the issue is about and any context from comments
40
+ - You may still query the observability provider for related logs if helpful
41
+ - Focus your investigation on this specific issue
42
+
43
+ 2. **If Additional Instructions are provided**:
44
+ - Follow them as your primary guide
45
+ - They may tell you to skip log investigation, focus on specific areas, etc.
46
+ - Use your judgment to combine with other inputs
47
+
48
+ 3. **If neither is provided** (default mode):
49
+ - Query the observability provider for recent errors
50
+ - Investigate the top issues
51
+ - Identify the best candidate for fixing
52
+
53
+ 4. **You can combine approaches** - e.g., work on a Linear issue AND check observability logs for related errors
54
+
55
+ ## AVAILABLE TOOLS
56
+
57
+ ${observability.getPromptInstructions()}
58
+
59
+ ### Linear API
60
+ The \`LINEAR_API_KEY\` environment variable is set. Use the Linear GraphQL API directly via curl:
61
+
62
+ \`\`\`bash
63
+ # Get issue details by identifier (e.g., ENG-123)
64
+ curl -s -X POST "https://api.linear.app/graphql" \\
65
+ -H "Content-Type: application/json" \\
66
+ -H "Authorization: \${LINEAR_API_KEY}" \\
67
+ -d '{"query":"query { issueSearch(query: \\"ENG-123\\") { nodes { id identifier title description url state { name } } } }"}'
68
+
69
+ # Search for existing issues by title/keyword
70
+ curl -s -X POST "https://api.linear.app/graphql" \\
71
+ -H "Content-Type: application/json" \\
72
+ -H "Authorization: \${LINEAR_API_KEY}" \\
73
+ -d '{"query":"query { issueSearch(query: \\"search terms\\", filter: { team: { id: { eq: \\"\${LINEAR_TEAM_ID}\\" } } }) { nodes { id identifier title url state { name } } } }"}'
74
+ \`\`\`
75
+
76
+ **Environment variables available:**
77
+ - \`LINEAR_API_KEY\` - API key (already configured)
78
+ - \`LINEAR_TEAM_ID\` - Team ID
79
+ - \`LINEAR_BUG_LABEL_ID\` - Bug label ID
80
+
81
+ ## SERVICE OWNERSHIP MAP
82
+
83
+ Read the service map at \`${config.serviceMapPath}\` to understand which GitHub repo
84
+ owns which service. This is critical for cross-repo dispatch.
85
+
86
+ **You MUST determine which repo should fix the bug you find.** Look at the service
87
+ name in the error logs and match it against the \`owns\` list in the service map.`);
88
+ // Inject service map if it exists
89
+ const serviceMap = parseServiceMap(config.serviceMapPath);
90
+ if (serviceMap.services.length > 0) {
91
+ parts.push("");
92
+ parts.push("### Service Map Reference");
93
+ if (fs.existsSync(config.serviceMapPath)) {
94
+ parts.push(fs.readFileSync(config.serviceMapPath, "utf-8"));
95
+ }
96
+ }
97
+ // Target repo identification
98
+ parts.push(`
99
+ ## TARGET REPO IDENTIFICATION
100
+
101
+ **Required**: In your \`best-candidate.md\` output, include these lines near the top
102
+ (after the TRIAGE_FINGERPRINT and RECOMMENDATION):
103
+
104
+ \`\`\`
105
+ TARGET_SERVICE: <service-map key, e.g., my-service>
106
+ TARGET_REPO: <GitHub repo, e.g., my-org/my-service>
107
+ \`\`\`
108
+
109
+ - If the bug belongs to **this repo**, set TARGET_REPO to the current repo.
110
+ - If the bug belongs to **a different repo**, set TARGET_REPO to that repo.
111
+ The workflow will automatically dispatch to the correct repo.`);
112
+ // Known issues context
113
+ if (knownIssuesContent) {
114
+ parts.push(`
115
+ ## KNOWN ISSUES - DO NOT DUPLICATE
116
+
117
+ The following issues and fixes have already been identified by previous triage runs.
118
+ Do NOT create new issues or propose fixes for the same underlying problems.
119
+ If the same error appears again, note it as a known issue and recommend a +1 on the existing issue instead.
120
+
121
+ ${knownIssuesContent}`);
122
+ }
123
+ // Investigation parameters and output requirements
124
+ parts.push(`
125
+ ## Investigation Parameters
126
+
127
+ - **Service Pattern**: \`${config.serviceFilter}\`
128
+ - **Time Range**: \`${config.timeRange}\`
129
+ - **Focus Area**: \`${config.severityFocus}\`
130
+ - **Investigation Depth**: \`${config.investigationDepth}\`
131
+
132
+ ## Output Requirements
133
+
134
+ Create these files with your findings:
135
+
136
+ ### 1. \`.github/triage-analysis/investigation-log.md\`
137
+ Document your investigation process - commands run, what you found, reasoning.
138
+
139
+ ### 2. \`.github/triage-analysis/issues-report.md\`
140
+ For each issue found:
141
+ - Severity, Environment (Production/Staging/Both), Frequency
142
+ - Description, Evidence (logs, stack traces)
143
+ - Root Cause Analysis, Impact, Suggested Fix
144
+ - Files to Modify, Confidence Level
145
+ - **Linear Status**: Check if this issue already exists in Linear
146
+ - If exists: Note the issue identifier (e.g., ENG-123) and URL
147
+ - If not found: Note as "No existing Linear issue found"
148
+
149
+ ### 3. \`.github/triage-analysis/best-candidate.md\`
150
+ Select the BEST issue to fix based on impact, frequency, fixability.
151
+ Include full technical analysis, exact code changes, test plan, rollback plan.
152
+
153
+ **CRITICAL - Title Format**: The first \`#\` heading in this file becomes the Linear issue title and PR title.
154
+ Do NOT prefix it with "Best Candidate Fix:", "Best Fix Candidate:", or any boilerplate.
155
+ Write a concise, descriptive bug title like you would for a real bug ticket. Examples:
156
+ - \`# extractUserFromResult Null Guard in EmitsEvent Decorator\`
157
+ - \`# SQS Message Retry Storm from Unhandled TypeError in Worker\`
158
+ - \`# PostgreSQL Vector Cast Syntax Error in Embedding Repository\`
159
+ Do NOT include backticks (\\\`) in the heading — they cause shell injection in CI.
160
+
161
+ **Important**: Include Linear Status at the top showing if this issue already exists:
162
+ - If exists: \`**Linear Issue**: [ENG-123](url) - Issue already tracked\`
163
+ - If not found: \`**Linear Issue**: None found - New issue will be created\`
164
+
165
+ **Required**: Include a TRIAGE_FINGERPRINT block in an HTML comment at the very top of best-candidate.md:
166
+ \`\`\`
167
+ <!-- TRIAGE_FINGERPRINT
168
+ error_pattern: <the key error message or pattern>
169
+ service: <service name>
170
+ first_seen: <date>
171
+ run_id: <github run id if available>
172
+ -->
173
+ \`\`\`
174
+
175
+ **Required**: Include a RECOMMENDATION line near the top (after the fingerprint):
176
+ - \`RECOMMENDATION: implement\` - This is a novel issue worth fixing
177
+ - \`RECOMMENDATION: +1 existing ENG-XXX\` - Same issue as an existing ticket, add occurrence
178
+ - \`RECOMMENDATION: skip\` - Not worth fixing (too minor, expected behavior, etc.)
179
+
180
+ ## START NOW
181
+
182
+ 1. Review your inputs above (Linear Issue, Additional Instructions, Parameters)
183
+ 2. Decide your approach based on what was provided
184
+ 3. Execute your investigation using the available APIs
185
+ 4. Write the output files
186
+
187
+ **CRITICAL**: You MUST write the output files BEFORE you run out of turns.
188
+ Write files early and update them if needed. Do NOT keep investigating without writing files.
189
+
190
+ **If Additional Instructions tell you to do something specific, follow them.**`);
191
+ return parts.join("\n");
192
+ }
193
+ // ---------------------------------------------------------------------------
194
+ // Implementation Prompt
195
+ // ---------------------------------------------------------------------------
196
+ export function buildImplementPrompt(issueIdentifier) {
197
+ return `You are implementing a fix for an issue identified from production logs.
198
+
199
+ ## Context
200
+
201
+ Read the best candidate analysis at \`.github/triage-analysis/best-candidate.md\`.
202
+ Also read \`.github/triage-analysis/investigation-log.md\` for context.
203
+
204
+ ## Your Task
205
+
206
+ 1. **Understand the issue**: Read the analysis thoroughly
207
+ 2. **Verify the fix approach**: Check the codebase to ensure the suggested fix is valid
208
+ 3. **Implement the fix**:
209
+ - Make minimal, focused changes
210
+ - Follow existing code patterns
211
+ - Add appropriate error handling
212
+ - Include TypeScript types
213
+ - Do NOT add unnecessary comments
214
+ - Do NOT refactor unrelated code
215
+
216
+ 4. **Verify your changes**:
217
+ - Run \`npm run lint\` to check for issues
218
+ - Run \`npm run build\` to verify compilation
219
+
220
+ 5. **Create a commit** with format:
221
+ \`\`\`
222
+ fix(<scope>): <brief description>
223
+
224
+ - <change 1>
225
+ - <change 2>
226
+
227
+ Identified by SWEny Triage
228
+ Linear: ${issueIdentifier}
229
+ \`\`\`
230
+
231
+ ## Safety Guidelines
232
+
233
+ - If the fix is too complex or risky, create \`.github/triage-analysis/fix-declined.md\` explaining why
234
+ - Do not make breaking changes
235
+ - Prefer defensive coding patterns
236
+
237
+ Start by reading the best-candidate.md file.`;
238
+ }
239
+ // ---------------------------------------------------------------------------
240
+ // PR Description Prompt
241
+ // ---------------------------------------------------------------------------
242
+ export function buildPrDescriptionPrompt(issueIdentifier, issueUrl) {
243
+ return `Generate a pull request description.
244
+
245
+ ## Context
246
+
247
+ 1. Read \`.github/triage-analysis/best-candidate.md\` for issue details
248
+ 2. Read \`.github/triage-analysis/investigation-log.md\` for context
249
+ 3. Run \`git diff main..HEAD\` to see the changes made
250
+
251
+ ## Output
252
+
253
+ Create \`.github/triage-analysis/pr-description.md\` with:
254
+
255
+ ## Summary
256
+ <What this PR fixes and why>
257
+
258
+ ## Issue Analysis
259
+ - Severity, Frequency, Services affected, Impact
260
+
261
+ ## Root Cause
262
+ <Technical explanation>
263
+
264
+ ## Solution
265
+ <Description and changes made>
266
+
267
+ ## Testing
268
+ - [ ] Lint passes
269
+ - [ ] Build passes
270
+ - [ ] Tests pass
271
+
272
+ ## Rollback Plan
273
+ <How to rollback>
274
+
275
+ ---
276
+ **Linear Issue**: [${issueIdentifier}](${issueUrl})
277
+ > Generated by SWEny Triage`;
278
+ }
279
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/recipes/triage/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAGzB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,MAAM,UAAU,wBAAwB,CACtC,MAAoB,EACpB,aAAoC,EACpC,kBAA0B;IAE1B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC;;;;;4BAKe,MAAM,CAAC,UAAU;;;;;EAK3C,MAAM,CAAC,aAAa,IAAI,iBAAiB;;;EAGzC,MAAM,CAAC,sBAAsB,IAAI,iBAAiB;;;;;;;qBAO/B,MAAM,CAAC,aAAa;gBACzB,MAAM,CAAC,SAAS;gBAChB,MAAM,CAAC,aAAa;yBACX,MAAM,CAAC,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BhD,aAAa,CAAC,qBAAqB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;4BA0BX,MAAM,CAAC,cAAc;;;;kFAIiC,CAAC,CAAC;IAElF,kCAAkC;IAClC,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC1D,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACxC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,KAAK,CAAC,IAAI,CAAC;;;;;;;;;;;;;gEAamD,CAAC,CAAC;IAEhE,uBAAuB;IACvB,IAAI,kBAAkB,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC;;;;;;;EAOb,kBAAkB,EAAE,CAAC,CAAC;IACtB,CAAC;IAED,mDAAmD;IACnD,KAAK,CAAC,IAAI,CAAC;;;2BAGc,MAAM,CAAC,aAAa;sBACzB,MAAM,CAAC,SAAS;sBAChB,MAAM,CAAC,aAAa;+BACX,MAAM,CAAC,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+EA4DuB,CAAC,CAAC;IAE/E,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,MAAM,UAAU,oBAAoB,CAAC,eAAuB;IAC1D,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA+BI,eAAe;;;;;;;;;6CASiB,CAAC;AAC9C,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,MAAM,UAAU,wBAAwB,CAAC,eAAuB,EAAE,QAAgB;IAChF,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAiCY,eAAe,KAAK,QAAQ;4BACrB,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { WorkflowContext } from "../../types.js";
2
+ import type { TriageConfig, TriageStepDataMap } from "./types.js";
3
+ /**
4
+ * Get typed step result data from the workflow context.
5
+ *
6
+ * Replaces unsafe casts like `ctx.results.get("investigate")?.data as unknown as T`.
7
+ * Step name is checked at compile time — typos are caught immediately.
8
+ *
9
+ * @example
10
+ * const investigation = getStepData(ctx, "investigate");
11
+ * // ^? InvestigationResult | undefined
12
+ */
13
+ export declare function getStepData<K extends keyof TriageStepDataMap>(ctx: WorkflowContext<TriageConfig>, stepName: K): TriageStepDataMap[K] | undefined;
14
+ //# sourceMappingURL=results.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../../src/recipes/triage/results.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAElE;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAC3D,GAAG,EAAE,eAAe,CAAC,YAAY,CAAC,EAClC,QAAQ,EAAE,CAAC,GACV,iBAAiB,CAAC,CAAC,CAAC,GAAG,SAAS,CAElC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Get typed step result data from the workflow context.
3
+ *
4
+ * Replaces unsafe casts like `ctx.results.get("investigate")?.data as unknown as T`.
5
+ * Step name is checked at compile time — typos are caught immediately.
6
+ *
7
+ * @example
8
+ * const investigation = getStepData(ctx, "investigate");
9
+ * // ^? InvestigationResult | undefined
10
+ */
11
+ export function getStepData(ctx, stepName) {
12
+ return ctx.results.get(stepName)?.data;
13
+ }
14
+ //# sourceMappingURL=results.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"results.js","sourceRoot":"","sources":["../../../src/recipes/triage/results.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CACzB,GAAkC,EAClC,QAAW;IAEX,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAwC,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Logger } from "@swenyai/providers";
2
+ export interface ServiceEntry {
3
+ name: string;
4
+ repo: string;
5
+ owns: string[];
6
+ }
7
+ export interface ServiceMap {
8
+ services: ServiceEntry[];
9
+ }
10
+ /**
11
+ * Parse a service-map.yml file using simple line-based parsing.
12
+ * Avoids YAML library dependency for ncc bundling simplicity.
13
+ */
14
+ export declare function parseServiceMap(filePath: string, logger?: Logger): ServiceMap;
15
+ //# sourceMappingURL=service-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-map.d.ts","sourceRoot":"","sources":["../../../src/recipes/triage/service-map.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAsD7E"}
@@ -0,0 +1,56 @@
1
+ import * as fs from "fs";
2
+ /**
3
+ * Parse a service-map.yml file using simple line-based parsing.
4
+ * Avoids YAML library dependency for ncc bundling simplicity.
5
+ */
6
+ export function parseServiceMap(filePath, logger) {
7
+ if (!fs.existsSync(filePath)) {
8
+ logger?.warn(`Service map not found at ${filePath}`);
9
+ return { services: [] };
10
+ }
11
+ const content = fs.readFileSync(filePath, "utf-8");
12
+ const lines = content.split("\n");
13
+ const services = [];
14
+ let current = null;
15
+ let inOwns = false;
16
+ for (const line of lines) {
17
+ const trimmed = line.trimEnd();
18
+ // Skip empty lines and comments
19
+ if (!trimmed || trimmed.startsWith("#"))
20
+ continue;
21
+ // Top-level "services:" header
22
+ if (trimmed === "services:")
23
+ continue;
24
+ // Service name (2-space indent, ends with colon)
25
+ const serviceMatch = trimmed.match(/^ (\S+):$/);
26
+ if (serviceMatch) {
27
+ if (current)
28
+ services.push(current);
29
+ current = { name: serviceMatch[1], repo: "", owns: [] };
30
+ inOwns = false;
31
+ continue;
32
+ }
33
+ // repo field
34
+ const repoMatch = trimmed.match(/^\s+repo:\s*"?([^"]+)"?$/);
35
+ if (repoMatch && current) {
36
+ current.repo = repoMatch[1];
37
+ inOwns = false;
38
+ continue;
39
+ }
40
+ // owns header
41
+ if (trimmed.match(/^\s+owns:\s*$/) && current) {
42
+ inOwns = true;
43
+ continue;
44
+ }
45
+ // owns list item
46
+ const ownsMatch = trimmed.match(/^\s+-\s+(.+)$/);
47
+ if (ownsMatch && current && inOwns) {
48
+ current.owns.push(ownsMatch[1].trim());
49
+ continue;
50
+ }
51
+ }
52
+ if (current)
53
+ services.push(current);
54
+ return { services };
55
+ }
56
+ //# sourceMappingURL=service-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-map.js","sourceRoot":"","sources":["../../../src/recipes/triage/service-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAazB;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,MAAe;IAC/D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,EAAE,IAAI,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC;QACrD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC1B,CAAC;IAED,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,IAAI,OAAO,GAAwB,IAAI,CAAC;IACxC,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAE/B,gCAAgC;QAChC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAElD,+BAA+B;QAC/B,IAAI,OAAO,KAAK,WAAW;YAAE,SAAS;QAEtC,iDAAiD;QACjD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACjD,IAAI,YAAY,EAAE,CAAC;YACjB,IAAI,OAAO;gBAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpC,OAAO,GAAG,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YACxD,MAAM,GAAG,KAAK,CAAC;YACf,SAAS;QACX,CAAC;QAED,aAAa;QACb,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC5D,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;YAC5B,MAAM,GAAG,KAAK,CAAC;YACf,SAAS;QACX,CAAC;QAED,cAAc;QACd,IAAI,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,OAAO,EAAE,CAAC;YAC9C,MAAM,GAAG,IAAI,CAAC;YACd,SAAS;QACX,CAAC;QAED,iBAAiB;QACjB,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACjD,IAAI,SAAS,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;YACnC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACvC,SAAS;QACX,CAAC;IACH,CAAC;IAED,IAAI,OAAO;QAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { StepResult, WorkflowContext } from "../../../types.js";
2
+ import type { TriageConfig } from "../types.js";
3
+ /** Build known-issues context from issue tracker + source control to prevent duplicates. */
4
+ export declare function buildContext(ctx: WorkflowContext<TriageConfig>): Promise<StepResult>;
5
+ //# sourceMappingURL=build-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-context.d.ts","sourceRoot":"","sources":["../../../../src/recipes/triage/steps/build-context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,4FAA4F;AAC5F,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAyF1F"}
@@ -0,0 +1,85 @@
1
+ import { canListTriageHistory } from "@swenyai/providers/issue-tracking";
2
+ /** Build known-issues context from issue tracker + source control to prevent duplicates. */
3
+ export async function buildContext(ctx) {
4
+ const issueTracker = ctx.providers.get("issueTracker");
5
+ const sourceControl = ctx.providers.get("sourceControl");
6
+ const config = ctx.config;
7
+ const lines = [];
8
+ lines.push("# Known Triage History (Last 30 Days)");
9
+ lines.push("");
10
+ lines.push("These issues have already been identified by previous SWEny Triage runs.");
11
+ lines.push("Do NOT create new issues or propose fixes for these same problems.");
12
+ lines.push("");
13
+ // 1. Fetch recent triage issues (last 30 days)
14
+ lines.push("## Tracked Issues");
15
+ try {
16
+ if (canListTriageHistory(issueTracker)) {
17
+ const triageHistory = await issueTracker.listTriageHistory(config.projectId, config.triageLabelId, 30);
18
+ if (triageHistory.length > 0) {
19
+ for (const entry of triageHistory) {
20
+ lines.push(`- **${entry.identifier}** [${entry.state}] ${entry.title} — ${entry.url}`);
21
+ }
22
+ }
23
+ else {
24
+ lines.push("_No triage-labeled issues found in last 30 days_");
25
+ }
26
+ }
27
+ else {
28
+ lines.push("_Issue tracker does not support triage history_");
29
+ }
30
+ }
31
+ catch (err) {
32
+ ctx.logger.warn(`Failed to fetch triage history: ${err}`);
33
+ lines.push("_Failed to fetch triage history_");
34
+ }
35
+ lines.push("");
36
+ // 2. Fetch recent triage PRs
37
+ lines.push("## Pull Requests");
38
+ try {
39
+ const triagePrs = await sourceControl.listPullRequests({
40
+ state: "all",
41
+ labels: ["triage"],
42
+ limit: 30,
43
+ });
44
+ lines.push("### Merged (fixed)");
45
+ const merged = triagePrs.filter((pr) => pr.state === "merged");
46
+ if (merged.length > 0) {
47
+ for (const pr of merged) {
48
+ lines.push(`- PR #${pr.number}: ${pr.title} — ${pr.url}`);
49
+ }
50
+ }
51
+ else {
52
+ lines.push("_None_");
53
+ }
54
+ lines.push("### Open (in progress)");
55
+ const open = triagePrs.filter((pr) => pr.state === "open");
56
+ if (open.length > 0) {
57
+ for (const pr of open) {
58
+ lines.push(`- PR #${pr.number}: ${pr.title} — ${pr.url}`);
59
+ }
60
+ }
61
+ else {
62
+ lines.push("_None_");
63
+ }
64
+ lines.push("### Closed (failed attempts)");
65
+ const closed = triagePrs.filter((pr) => pr.state === "closed");
66
+ if (closed.length > 0) {
67
+ for (const pr of closed) {
68
+ lines.push(`- PR #${pr.number}: ${pr.title} — ${pr.url}`);
69
+ }
70
+ }
71
+ else {
72
+ lines.push("_None_");
73
+ }
74
+ }
75
+ catch {
76
+ ctx.logger.warn("Failed to fetch triage PRs");
77
+ lines.push("_Failed to fetch triage PRs_");
78
+ }
79
+ const knownIssuesContent = lines.join("\n");
80
+ return {
81
+ status: "success",
82
+ data: { knownIssuesContent },
83
+ };
84
+ }
85
+ //# sourceMappingURL=build-context.js.map