@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.
- package/README.md +75 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/recipes/triage/index.d.ts +7 -0
- package/dist/recipes/triage/index.d.ts.map +1 -0
- package/dist/recipes/triage/index.js +30 -0
- package/dist/recipes/triage/index.js.map +1 -0
- package/dist/recipes/triage/prompts.d.ts +6 -0
- package/dist/recipes/triage/prompts.d.ts.map +1 -0
- package/dist/recipes/triage/prompts.js +279 -0
- package/dist/recipes/triage/prompts.js.map +1 -0
- package/dist/recipes/triage/results.d.ts +14 -0
- package/dist/recipes/triage/results.d.ts.map +1 -0
- package/dist/recipes/triage/results.js +14 -0
- package/dist/recipes/triage/results.js.map +1 -0
- package/dist/recipes/triage/service-map.d.ts +15 -0
- package/dist/recipes/triage/service-map.d.ts.map +1 -0
- package/dist/recipes/triage/service-map.js +56 -0
- package/dist/recipes/triage/service-map.js.map +1 -0
- package/dist/recipes/triage/steps/build-context.d.ts +5 -0
- package/dist/recipes/triage/steps/build-context.d.ts.map +1 -0
- package/dist/recipes/triage/steps/build-context.js +85 -0
- package/dist/recipes/triage/steps/build-context.js.map +1 -0
- package/dist/recipes/triage/steps/create-issue.d.ts +5 -0
- package/dist/recipes/triage/steps/create-issue.d.ts.map +1 -0
- package/dist/recipes/triage/steps/create-issue.js +85 -0
- package/dist/recipes/triage/steps/create-issue.js.map +1 -0
- package/dist/recipes/triage/steps/create-pr.d.ts +5 -0
- package/dist/recipes/triage/steps/create-pr.d.ts.map +1 -0
- package/dist/recipes/triage/steps/create-pr.js +97 -0
- package/dist/recipes/triage/steps/create-pr.js.map +1 -0
- package/dist/recipes/triage/steps/cross-repo-check.d.ts +5 -0
- package/dist/recipes/triage/steps/cross-repo-check.d.ts.map +1 -0
- package/dist/recipes/triage/steps/cross-repo-check.js +44 -0
- package/dist/recipes/triage/steps/cross-repo-check.js.map +1 -0
- package/dist/recipes/triage/steps/implement-fix.d.ts +5 -0
- package/dist/recipes/triage/steps/implement-fix.d.ts.map +1 -0
- package/dist/recipes/triage/steps/implement-fix.js +100 -0
- package/dist/recipes/triage/steps/implement-fix.js.map +1 -0
- package/dist/recipes/triage/steps/investigate.d.ts +5 -0
- package/dist/recipes/triage/steps/investigate.d.ts.map +1 -0
- package/dist/recipes/triage/steps/investigate.js +74 -0
- package/dist/recipes/triage/steps/investigate.js.map +1 -0
- package/dist/recipes/triage/steps/notify.d.ts +5 -0
- package/dist/recipes/triage/steps/notify.d.ts.map +1 -0
- package/dist/recipes/triage/steps/notify.js +66 -0
- package/dist/recipes/triage/steps/notify.js.map +1 -0
- package/dist/recipes/triage/steps/novelty-gate.d.ts +5 -0
- package/dist/recipes/triage/steps/novelty-gate.d.ts.map +1 -0
- package/dist/recipes/triage/steps/novelty-gate.js +60 -0
- package/dist/recipes/triage/steps/novelty-gate.js.map +1 -0
- package/dist/recipes/triage/steps/verify-access.d.ts +5 -0
- package/dist/recipes/triage/steps/verify-access.d.ts.map +1 -0
- package/dist/recipes/triage/steps/verify-access.js +11 -0
- package/dist/recipes/triage/steps/verify-access.js.map +1 -0
- package/dist/recipes/triage/test-helpers.d.ts +17 -0
- package/dist/recipes/triage/test-helpers.d.ts.map +1 -0
- package/dist/recipes/triage/test-helpers.js +41 -0
- package/dist/recipes/triage/test-helpers.js.map +1 -0
- package/dist/recipes/triage/types.d.ts +79 -0
- package/dist/recipes/triage/types.d.ts.map +1 -0
- package/dist/recipes/triage/types.js +2 -0
- package/dist/recipes/triage/types.js.map +1 -0
- package/dist/registry.d.ts +4 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +19 -0
- package/dist/registry.js.map +1 -0
- package/dist/runner.d.ts +13 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +98 -0
- package/dist/runner.js.map +1 -0
- package/dist/types.d.ts +77 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- 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)
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|