@sweny-ai/core 0.1.2 → 0.1.3
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/dist/executor.js +13 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/templates.d.ts +31 -0
- package/dist/templates.js +110 -0
- package/dist/workflows/implement.js +2 -0
- package/dist/workflows/triage.js +24 -15
- package/package.json +1 -1
package/dist/executor.js
CHANGED
|
@@ -46,9 +46,14 @@ export async function execute(workflow, input, options) {
|
|
|
46
46
|
return output;
|
|
47
47
|
},
|
|
48
48
|
}));
|
|
49
|
+
// Prepend additional context to instruction if provided
|
|
50
|
+
const additionalContext = typeof input?.additionalContext === "string" ? input.additionalContext : "";
|
|
51
|
+
const instruction = additionalContext
|
|
52
|
+
? `## Additional Context & Rules\n\n${additionalContext}\n\n---\n\n${node.instruction}`
|
|
53
|
+
: node.instruction;
|
|
49
54
|
// Run Claude on this node
|
|
50
55
|
const result = await claude.run({
|
|
51
|
-
instruction
|
|
56
|
+
instruction,
|
|
52
57
|
context,
|
|
53
58
|
tools: trackedTools,
|
|
54
59
|
outputSchema: node.output,
|
|
@@ -166,11 +171,13 @@ function validate(workflow, skills) {
|
|
|
166
171
|
if (!workflow.nodes[edge.to])
|
|
167
172
|
throw new Error(`Edge references unknown node: "${edge.to}"`);
|
|
168
173
|
}
|
|
169
|
-
//
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
// Check that each node has at least one available skill (if it lists any)
|
|
175
|
+
for (const [nodeId, node] of Object.entries(workflow.nodes)) {
|
|
176
|
+
if (node.skills.length === 0)
|
|
177
|
+
continue;
|
|
178
|
+
const available = node.skills.filter((id) => skills.has(id));
|
|
179
|
+
if (available.length === 0) {
|
|
180
|
+
consoleLogger.warn(`Node "${nodeId}" has no available skills (needs one of: ${node.skills.join(", ")})`);
|
|
174
181
|
}
|
|
175
182
|
}
|
|
176
183
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -35,3 +35,5 @@ export { buildAutoMcpServers } from "./mcp.js";
|
|
|
35
35
|
export type { McpServerConfig, McpAutoConfig } from "./types.js";
|
|
36
36
|
export { buildWorkflow, refineWorkflow } from "./workflow-builder.js";
|
|
37
37
|
export type { BuildWorkflowOptions } from "./workflow-builder.js";
|
|
38
|
+
export { resolveTemplates, loadAdditionalContext, loadTemplate } from "./templates.js";
|
|
39
|
+
export type { Templates } from "./templates.js";
|
package/dist/index.js
CHANGED
|
@@ -34,3 +34,5 @@ export { workflowZ, nodeZ, edgeZ, skillZ, parseWorkflow, validateWorkflow, workf
|
|
|
34
34
|
export { buildAutoMcpServers } from "./mcp.js";
|
|
35
35
|
// Workflow builder
|
|
36
36
|
export { buildWorkflow, refineWorkflow } from "./workflow-builder.js";
|
|
37
|
+
// Templates
|
|
38
|
+
export { resolveTemplates, loadAdditionalContext, loadTemplate } from "./templates.js";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template loading for issues and PRs.
|
|
3
|
+
*
|
|
4
|
+
* Templates can come from:
|
|
5
|
+
* 1. Local file path (relative to repo root)
|
|
6
|
+
* 2. URL (fetched at runtime)
|
|
7
|
+
* 3. Built-in defaults
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_ISSUE_TEMPLATE = "## Summary\n<!-- One-line description of the issue -->\n\n## Root Cause\n<!-- What caused this issue -->\n\n## Impact\n- **Severity**: <!-- critical / high / medium / low -->\n- **Affected Services**: <!-- list -->\n- **User Impact**: <!-- description -->\n\n## Steps to Reproduce\n1. ...\n\n## Recommended Fix\n<!-- Proposed solution -->\n\n## Related\n- Commits: <!-- relevant commits -->\n- PRs: <!-- related PRs -->\n";
|
|
10
|
+
export declare const DEFAULT_PR_TEMPLATE = "## Summary\n<!-- What does this PR do? -->\n\n## Root Cause\n<!-- What caused the issue this fixes? -->\n\n## Changes\n<!-- Bullet list of changes -->\n\n## Testing\n- [ ] Tested locally\n- [ ] No breaking changes\n\n## Related Issues\nFixes #\n";
|
|
11
|
+
export interface Templates {
|
|
12
|
+
issueTemplate: string;
|
|
13
|
+
prTemplate: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Load a template from a file path or URL.
|
|
17
|
+
* Returns the default if source is empty or loading fails.
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadTemplate(source: string | undefined, fallback: string, cwd?: string): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve issue and PR templates from config.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveTemplates(config: {
|
|
24
|
+
issueTemplate?: string;
|
|
25
|
+
prTemplate?: string;
|
|
26
|
+
}, cwd?: string): Promise<Templates>;
|
|
27
|
+
/**
|
|
28
|
+
* Load additional context documents (local files or URLs).
|
|
29
|
+
* Each source is loaded and wrapped with a header.
|
|
30
|
+
*/
|
|
31
|
+
export declare function loadAdditionalContext(sources: string[], cwd?: string): Promise<string>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template loading for issues and PRs.
|
|
3
|
+
*
|
|
4
|
+
* Templates can come from:
|
|
5
|
+
* 1. Local file path (relative to repo root)
|
|
6
|
+
* 2. URL (fetched at runtime)
|
|
7
|
+
* 3. Built-in defaults
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from "node:fs";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
export const DEFAULT_ISSUE_TEMPLATE = `## Summary
|
|
12
|
+
<!-- One-line description of the issue -->
|
|
13
|
+
|
|
14
|
+
## Root Cause
|
|
15
|
+
<!-- What caused this issue -->
|
|
16
|
+
|
|
17
|
+
## Impact
|
|
18
|
+
- **Severity**: <!-- critical / high / medium / low -->
|
|
19
|
+
- **Affected Services**: <!-- list -->
|
|
20
|
+
- **User Impact**: <!-- description -->
|
|
21
|
+
|
|
22
|
+
## Steps to Reproduce
|
|
23
|
+
1. ...
|
|
24
|
+
|
|
25
|
+
## Recommended Fix
|
|
26
|
+
<!-- Proposed solution -->
|
|
27
|
+
|
|
28
|
+
## Related
|
|
29
|
+
- Commits: <!-- relevant commits -->
|
|
30
|
+
- PRs: <!-- related PRs -->
|
|
31
|
+
`;
|
|
32
|
+
export const DEFAULT_PR_TEMPLATE = `## Summary
|
|
33
|
+
<!-- What does this PR do? -->
|
|
34
|
+
|
|
35
|
+
## Root Cause
|
|
36
|
+
<!-- What caused the issue this fixes? -->
|
|
37
|
+
|
|
38
|
+
## Changes
|
|
39
|
+
<!-- Bullet list of changes -->
|
|
40
|
+
|
|
41
|
+
## Testing
|
|
42
|
+
- [ ] Tested locally
|
|
43
|
+
- [ ] No breaking changes
|
|
44
|
+
|
|
45
|
+
## Related Issues
|
|
46
|
+
Fixes #
|
|
47
|
+
`;
|
|
48
|
+
/**
|
|
49
|
+
* Load a template from a file path or URL.
|
|
50
|
+
* Returns the default if source is empty or loading fails.
|
|
51
|
+
*/
|
|
52
|
+
export async function loadTemplate(source, fallback, cwd = process.cwd()) {
|
|
53
|
+
if (!source || source.trim() === "")
|
|
54
|
+
return fallback;
|
|
55
|
+
const trimmed = source.trim();
|
|
56
|
+
// URL
|
|
57
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
58
|
+
try {
|
|
59
|
+
const res = await fetch(trimmed, { signal: AbortSignal.timeout(10_000) });
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
console.warn(`[templates] Failed to fetch ${trimmed} (HTTP ${res.status}), using default`);
|
|
62
|
+
return fallback;
|
|
63
|
+
}
|
|
64
|
+
return await res.text();
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
console.warn(`[templates] Failed to fetch ${trimmed}: ${err.message}, using default`);
|
|
68
|
+
return fallback;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Local file
|
|
72
|
+
const resolved = path.resolve(cwd, trimmed);
|
|
73
|
+
try {
|
|
74
|
+
return fs.readFileSync(resolved, "utf-8");
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
console.warn(`[templates] Template file not found: ${resolved}, using default`);
|
|
78
|
+
return fallback;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve issue and PR templates from config.
|
|
83
|
+
*/
|
|
84
|
+
export async function resolveTemplates(config, cwd) {
|
|
85
|
+
const [issueTemplate, prTemplate] = await Promise.all([
|
|
86
|
+
loadTemplate(config.issueTemplate, DEFAULT_ISSUE_TEMPLATE, cwd),
|
|
87
|
+
loadTemplate(config.prTemplate, DEFAULT_PR_TEMPLATE, cwd),
|
|
88
|
+
]);
|
|
89
|
+
return { issueTemplate, prTemplate };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Load additional context documents (local files or URLs).
|
|
93
|
+
* Each source is loaded and wrapped with a header.
|
|
94
|
+
*/
|
|
95
|
+
export async function loadAdditionalContext(sources, cwd = process.cwd()) {
|
|
96
|
+
if (sources.length === 0)
|
|
97
|
+
return "";
|
|
98
|
+
const parts = [];
|
|
99
|
+
for (const source of sources) {
|
|
100
|
+
const trimmed = source.trim();
|
|
101
|
+
if (!trimmed)
|
|
102
|
+
continue;
|
|
103
|
+
const label = trimmed.startsWith("http") ? trimmed : path.basename(trimmed);
|
|
104
|
+
const content = await loadTemplate(trimmed, "", cwd);
|
|
105
|
+
if (content) {
|
|
106
|
+
parts.push(`### ${label}\n\n${content}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return parts.length > 0 ? parts.join("\n\n---\n\n") : "";
|
|
110
|
+
}
|
|
@@ -56,6 +56,8 @@ If the fix is too risky or complex, explain why and skip.`,
|
|
|
56
56
|
3. Reference the original issue in the PR body.
|
|
57
57
|
4. Add appropriate reviewers or labels if possible.
|
|
58
58
|
|
|
59
|
+
If context.prTemplate is provided, use it as the format for the PR body. Otherwise use a clear structure with: Summary, Changes, Testing, and Related Issues.
|
|
60
|
+
|
|
59
61
|
Return the PR URL.`,
|
|
60
62
|
skills: ["github"],
|
|
61
63
|
},
|
package/dist/workflows/triage.js
CHANGED
|
@@ -41,7 +41,15 @@ Be thorough — the investigation step depends on complete context. Use every to
|
|
|
41
41
|
4. Determine affected services and users.
|
|
42
42
|
5. Recommend a fix approach.
|
|
43
43
|
|
|
44
|
-
**Novelty check (REQUIRED
|
|
44
|
+
**Novelty check (REQUIRED — you MUST do this before finishing):**
|
|
45
|
+
Search the issue tracker for existing issues (BOTH open AND closed) that cover the same root cause, error pattern, or affected service. Use github_search_issues and/or linear_search_issues with multiple keyword variations.
|
|
46
|
+
|
|
47
|
+
A match means ANY of:
|
|
48
|
+
- An issue about the same root cause (even if closed/fixed)
|
|
49
|
+
- An issue about the same error message or pattern in the same service
|
|
50
|
+
- An issue that a human would consider "the same bug"
|
|
51
|
+
|
|
52
|
+
Set is_duplicate=true if ANY match is found. Set is_duplicate=false ONLY if you searched and found zero matches. You MUST always set this field.`,
|
|
45
53
|
skills: ["github", "linear"],
|
|
46
54
|
output: {
|
|
47
55
|
type: "object",
|
|
@@ -50,26 +58,27 @@ Be thorough — the investigation step depends on complete context. Use every to
|
|
|
50
58
|
severity: { type: "string", enum: ["critical", "high", "medium", "low"] },
|
|
51
59
|
affected_services: { type: "array", items: { type: "string" } },
|
|
52
60
|
is_duplicate: { type: "boolean" },
|
|
53
|
-
duplicate_of: { type: "string", description: "Issue ID if duplicate" },
|
|
61
|
+
duplicate_of: { type: "string", description: "Issue ID/URL if duplicate" },
|
|
54
62
|
recommendation: { type: "string" },
|
|
55
63
|
fix_approach: { type: "string" },
|
|
56
64
|
},
|
|
57
|
-
required: ["root_cause", "severity", "recommendation"],
|
|
65
|
+
required: ["root_cause", "severity", "is_duplicate", "recommendation"],
|
|
58
66
|
},
|
|
59
67
|
},
|
|
60
68
|
create_issue: {
|
|
61
|
-
name: "Create
|
|
62
|
-
instruction: `
|
|
69
|
+
name: "Create Issue",
|
|
70
|
+
instruction: `Create an issue documenting the investigation findings:
|
|
71
|
+
|
|
72
|
+
1. Use a clear, actionable title.
|
|
73
|
+
2. Include: root cause, severity, affected services, reproduction steps, and recommended fix.
|
|
74
|
+
3. Add appropriate labels (bug, severity level, affected service).
|
|
75
|
+
4. Link to relevant commits, PRs, or existing issues.
|
|
76
|
+
|
|
77
|
+
**Safety check**: If during creation you notice a very similar issue already exists, add a comment to it using github_add_comment or linear_add_comment instead of creating a duplicate.
|
|
63
78
|
|
|
64
|
-
|
|
65
|
-
2. **If a matching issue exists**: Add a comment to it (using github_add_comment or linear_add_comment) noting this re-occurrence with the current timestamp and any new context. Do NOT create a new issue. Return the existing issue's identifier and URL.
|
|
66
|
-
3. **If no matching issue exists**: Create a new issue with:
|
|
67
|
-
- A clear, actionable title
|
|
68
|
-
- Root cause, severity, affected services, reproduction steps, and recommended fix
|
|
69
|
-
- Appropriate labels (bug, severity level, affected service)
|
|
70
|
-
- Links to relevant commits, PRs, or existing issues
|
|
79
|
+
If context.issueTemplate is provided, use it as the format for the issue body. Otherwise use a clear structure with: Summary, Root Cause, Impact, Steps to Reproduce, and Recommended Fix.
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
Create the issue in whichever tracker is available to you.`,
|
|
73
82
|
skills: ["linear", "github"],
|
|
74
83
|
},
|
|
75
84
|
notify: {
|
|
@@ -97,13 +106,13 @@ Log a brief note about why it was skipped. No further action needed.`,
|
|
|
97
106
|
{
|
|
98
107
|
from: "investigate",
|
|
99
108
|
to: "create_issue",
|
|
100
|
-
when: "
|
|
109
|
+
when: "is_duplicate is false AND severity is medium or higher",
|
|
101
110
|
},
|
|
102
111
|
// investigate → skip (if duplicate or low priority)
|
|
103
112
|
{
|
|
104
113
|
from: "investigate",
|
|
105
114
|
to: "skip",
|
|
106
|
-
when: "
|
|
115
|
+
when: "is_duplicate is true, OR severity is low",
|
|
107
116
|
},
|
|
108
117
|
// create_issue → notify (always)
|
|
109
118
|
{ from: "create_issue", to: "notify" },
|