@sweny-ai/core 0.1.56 → 0.1.58

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.
@@ -8,7 +8,7 @@ export function registerTriageCommand(program) {
8
8
  .description("Run the SWEny triage workflow")
9
9
  .option("--agent <provider>", "Coding agent: claude (default), codex, gemini")
10
10
  .option("--coding-agent-provider <provider>", "Coding agent provider (alias for --agent)")
11
- .option("--observability-provider <provider>", "Observability provider (default: datadog)")
11
+ .option("--observability-provider <provider>", "Observability provider (default: none)")
12
12
  .option("--issue-tracker-provider <provider>", "Issue tracker provider (default: github-issues)")
13
13
  .option("--source-control-provider <provider>", "Source control provider (default: github)")
14
14
  .option("--notification-provider <provider>", "Notification provider: console, github-summary, slack, teams, discord, webhook, email, file (default: console)")
@@ -86,7 +86,7 @@ export function parseCliInputs(options, fileConfig = {}) {
86
86
  return [fileVal];
87
87
  return [];
88
88
  };
89
- const obsProvider = options.observabilityProvider || f("observability-provider") || "datadog";
89
+ const obsProvider = options.observabilityProvider || f("observability-provider") || "none";
90
90
  return {
91
91
  codingAgentProvider: options.agent || options.codingAgentProvider || f("coding-agent-provider") || "claude",
92
92
  // Secrets: env only — never from config file
@@ -220,6 +220,8 @@ export function validateInputs(config) {
220
220
  }
221
221
  // Observability credentials by provider
222
222
  switch (config.observabilityProvider) {
223
+ case "none":
224
+ break; // No observability provider configured — nothing to validate
223
225
  case "datadog":
224
226
  if (!config.observabilityCredentials.apiKey)
225
227
  errors.push("Missing: DD_API_KEY — find API keys at https://app.datadoghq.com/organization-settings/api-keys");
@@ -331,7 +333,7 @@ export function validateInputs(config) {
331
333
  errors.push("Missing: RENDER_SERVICE_ID — find it in your service's Settings page (srv-...)");
332
334
  break;
333
335
  default:
334
- errors.push(`Unknown --observability-provider "${config.observabilityProvider}". Valid values: datadog, sentry, cloudwatch, splunk, elastic, newrelic, loki, prometheus, pagerduty, honeycomb, heroku, opsgenie, axiom, betterstack, vercel, supabase, netlify, fly, render, file`);
336
+ errors.push(`Unknown --observability-provider "${config.observabilityProvider}". Valid values: none, datadog, sentry, cloudwatch, splunk, elastic, newrelic, loki, prometheus, pagerduty, honeycomb, heroku, opsgenie, axiom, betterstack, vercel, supabase, netlify, fly, render, file`);
335
337
  }
336
338
  // Issue tracker credentials by provider
337
339
  switch (config.issueTrackerProvider) {
@@ -112,6 +112,27 @@ describe("validateInputs — notification provider", () => {
112
112
  expect(err).toContain("console");
113
113
  });
114
114
  });
115
+ describe("validateInputs — observability provider", () => {
116
+ it("accepts 'none' without requiring any credentials", () => {
117
+ const errors = validateInputs(baseConfig({ observabilityProvider: "none", observabilityCredentials: {} }));
118
+ expect(errors.filter((e) => e.includes("DD_") || e.includes("observability"))).toEqual([]);
119
+ });
120
+ it("requires DD keys when explicitly set to datadog", () => {
121
+ const errors = validateInputs(baseConfig({ observabilityProvider: "datadog", observabilityCredentials: {} }));
122
+ expect(errors.some((e) => e.includes("DD_API_KEY"))).toBe(true);
123
+ expect(errors.some((e) => e.includes("DD_APP_KEY"))).toBe(true);
124
+ });
125
+ });
126
+ describe("parseCliInputs — observability provider default", () => {
127
+ it("defaults to 'none' when no provider is configured", () => {
128
+ const config = parseCliInputs({}, {});
129
+ expect(config.observabilityProvider).toBe("none");
130
+ });
131
+ it("respects explicit datadog provider", () => {
132
+ const config = parseCliInputs({ observabilityProvider: "datadog" }, {});
133
+ expect(config.observabilityProvider).toBe("datadog");
134
+ });
135
+ });
115
136
  describe("fetch.auth + offline parsing", () => {
116
137
  it("parses offline flag", () => {
117
138
  const config = parseCliInputs({ offline: true }, {});
@@ -10,11 +10,12 @@
10
10
  * - Security: no auth token leakage, URL validation
11
11
  */
12
12
  import { mkdirSync, writeFileSync, rmSync } from "node:fs";
13
+ import { randomBytes } from "node:crypto";
13
14
  import { tmpdir } from "node:os";
14
15
  import * as path from "node:path";
15
16
  import { describe, it, expect, beforeEach, vi } from "vitest";
16
17
  import { classifySource, sourceZ, hashContent, resolveSource, resolveSources, } from "./sources.js";
17
- const fileTmp = path.join(tmpdir(), "sweny-sources-validation-test");
18
+ const fileTmp = path.join(tmpdir(), `sweny-sources-validation-test-${randomBytes(4).toString("hex")}`);
18
19
  const baseCtx = () => ({
19
20
  cwd: "/tmp",
20
21
  env: {},
@@ -253,6 +254,24 @@ describe("error messages include actionable information", () => {
253
254
  expect(e.message).toContain("--offline");
254
255
  }
255
256
  });
257
+ it("SOURCE_FILE_READ_FAILED on permission denied", async () => {
258
+ const unreadable = path.join(fileTmp, "unreadable.md");
259
+ writeFileSync(unreadable, "secret");
260
+ const { chmodSync } = await import("node:fs");
261
+ chmodSync(unreadable, 0o000);
262
+ try {
263
+ await resolveSource("./unreadable.md", "context[0]", { ...baseCtx(), cwd: fileTmp });
264
+ expect.unreachable("should have thrown");
265
+ }
266
+ catch (e) {
267
+ expect(e.message).toContain("SOURCE_FILE_READ_FAILED");
268
+ expect(e.message).toContain("unreadable.md");
269
+ expect(e.message).toContain("context[0]");
270
+ }
271
+ finally {
272
+ chmodSync(unreadable, 0o644);
273
+ }
274
+ });
256
275
  it("SOURCE_OFFLINE_REQUIRES_FETCH includes the URL and field path", async () => {
257
276
  try {
258
277
  await resolveSource("https://example.com/rules.md", "rules[1]", { ...baseCtx(), offline: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweny-ai/core",
3
- "version": "0.1.56",
3
+ "version": "0.1.58",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sweny": "./dist/cli/main.js"