@sweny-ai/core 0.1.149 → 0.1.151
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/cli/builtin-workflows.d.ts +45 -0
- package/dist/cli/builtin-workflows.js +129 -0
- package/dist/cli/check.d.ts +2 -8
- package/dist/cli/check.js +2 -34
- package/dist/cli/config.js +2 -0
- package/dist/cli/main.js +49 -13
- package/dist/cli/packs.d.ts +9 -5
- package/dist/cli/packs.js +100 -50
- package/dist/executor.js +15 -3
- package/dist/harness/auth.d.ts +38 -0
- package/dist/harness/auth.js +93 -0
- package/dist/harness/claude-code.d.ts +8 -0
- package/dist/harness/claude-code.js +8 -1
- package/dist/harness/codex.d.ts +10 -1
- package/dist/harness/codex.js +17 -3
- package/dist/harness/pi.js +14 -3
- package/dist/harness/policy.js +6 -0
- package/dist/harness/tool-bridge/server.d.ts +20 -0
- package/dist/harness/tool-bridge/server.js +27 -10
- package/dist/harness/tool-bridge/shim.d.ts +41 -4
- package/dist/harness/tool-bridge/shim.js +118 -17
- package/dist/harness/types.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/node-policy.js +1 -0
- package/dist/safe-outputs.d.ts +17 -5
- package/dist/safe-outputs.js +85 -11
- package/dist/schema.d.ts +80 -0
- package/dist/schema.js +23 -0
- package/dist/types.d.ts +13 -0
- package/dist/workflows/browser.js +2 -2
- package/dist/workflows/implement.yml +61 -19
- package/dist/workflows/triage.yml +171 -139
- package/package.json +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bind a built-in workflow (triage, implement) to the providers `sweny triage`
|
|
3
|
+
* and `sweny implement` run with (#365).
|
|
4
|
+
*
|
|
5
|
+
* The bundled YAML is written for the exemplary case: analysis nodes are
|
|
6
|
+
* `read`, and tracker and PR writes are safe outputs that the github or linear
|
|
7
|
+
* skill applies after the node. Here the CLI pins each output to the tracker
|
|
8
|
+
* the user configured (`via`) and to the repository (`target`), so a Linear key
|
|
9
|
+
* in the environment never redirects a GitHub-issues user's issues, and back.
|
|
10
|
+
*
|
|
11
|
+
* Two setups cannot run that way. Each affected node keeps its pre-#365 shape,
|
|
12
|
+
* and the CLI prints one notice naming the nodes and the reason:
|
|
13
|
+
*
|
|
14
|
+
* - Reads that need the shell or an MCP server (a Jira or GitLab integration,
|
|
15
|
+
* an observability provider with no built-in skill, user MCP servers or
|
|
16
|
+
* workspace tools). A `read` node has neither, so the analysis nodes run
|
|
17
|
+
* write-capable with file edits and every skill write tool denied, as before.
|
|
18
|
+
* - Writes no safe-output skill can apply (a Jira or file tracker, GitLab or
|
|
19
|
+
* file source control). Those nodes use the tracker's own tools, as before.
|
|
20
|
+
*
|
|
21
|
+
* Pure: returns a new workflow, never mutates the input.
|
|
22
|
+
*/
|
|
23
|
+
import type { SafeOutputType, Workflow } from "../types.js";
|
|
24
|
+
export interface BuiltinProviders {
|
|
25
|
+
/** `github-issues` | `linear` | `jira` | `file` */
|
|
26
|
+
issueTracker: string;
|
|
27
|
+
/** `github` | `gitlab` | `file` */
|
|
28
|
+
sourceControl: string;
|
|
29
|
+
observability: string[];
|
|
30
|
+
/** `owner/repo`; pins GitHub outputs. */
|
|
31
|
+
repository?: string;
|
|
32
|
+
/** User-configured MCP servers (`SWENY_MCP_SERVERS`). */
|
|
33
|
+
userMcpServers?: string[];
|
|
34
|
+
workspaceTools?: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface BoundWorkflow {
|
|
37
|
+
workflow: Workflow;
|
|
38
|
+
/** One line per fallback, for the CLI to print. Empty when every node runs as written. */
|
|
39
|
+
notes: string[];
|
|
40
|
+
}
|
|
41
|
+
/** What a read-only node could not reach for these providers. Empty when it reaches everything. */
|
|
42
|
+
export declare function readGaps(p: BuiltinProviders): string[];
|
|
43
|
+
/** The skill that applies this output type for these providers, or undefined when none can. */
|
|
44
|
+
export declare function outputSkill(type: SafeOutputType, p: BuiltinProviders): string | undefined;
|
|
45
|
+
export declare function bindBuiltinWorkflow(workflow: Workflow, p: BuiltinProviders): BoundWorkflow;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bind a built-in workflow (triage, implement) to the providers `sweny triage`
|
|
3
|
+
* and `sweny implement` run with (#365).
|
|
4
|
+
*
|
|
5
|
+
* The bundled YAML is written for the exemplary case: analysis nodes are
|
|
6
|
+
* `read`, and tracker and PR writes are safe outputs that the github or linear
|
|
7
|
+
* skill applies after the node. Here the CLI pins each output to the tracker
|
|
8
|
+
* the user configured (`via`) and to the repository (`target`), so a Linear key
|
|
9
|
+
* in the environment never redirects a GitHub-issues user's issues, and back.
|
|
10
|
+
*
|
|
11
|
+
* Two setups cannot run that way. Each affected node keeps its pre-#365 shape,
|
|
12
|
+
* and the CLI prints one notice naming the nodes and the reason:
|
|
13
|
+
*
|
|
14
|
+
* - Reads that need the shell or an MCP server (a Jira or GitLab integration,
|
|
15
|
+
* an observability provider with no built-in skill, user MCP servers or
|
|
16
|
+
* workspace tools). A `read` node has neither, so the analysis nodes run
|
|
17
|
+
* write-capable with file edits and every skill write tool denied, as before.
|
|
18
|
+
* - Writes no safe-output skill can apply (a Jira or file tracker, GitLab or
|
|
19
|
+
* file source control). Those nodes use the tracker's own tools, as before.
|
|
20
|
+
*
|
|
21
|
+
* Pure: returns a new workflow, never mutates the input.
|
|
22
|
+
*/
|
|
23
|
+
import { builtinSkills } from "../skills/index.js";
|
|
24
|
+
import { resolveNodePermissions } from "../node-policy.js";
|
|
25
|
+
/** Observability providers a read-only node can reach: a built-in skill, or a local log file (Read). */
|
|
26
|
+
const SKILL_OBSERVABILITY = new Set(["sentry", "datadog", "betterstack", "file"]);
|
|
27
|
+
/** Trackers whose issues and comments a safe-output skill applies. */
|
|
28
|
+
const TRACKER_SKILL = { "github-issues": "github", linear: "linear" };
|
|
29
|
+
/** Skill write tools a legacy node keeps for each output type it declared. */
|
|
30
|
+
const LEGACY_WRITE_TOOLS = {
|
|
31
|
+
issue: ["github_create_issue", "linear_create_issue"],
|
|
32
|
+
// Reopening a closed duplicate goes with the +1 comment.
|
|
33
|
+
comment: ["github_add_comment", "linear_add_comment", "linear_update_issue"],
|
|
34
|
+
pr: ["github_create_pr"],
|
|
35
|
+
label: ["github_add_labels"],
|
|
36
|
+
};
|
|
37
|
+
const writeToolsBySkill = new Map(builtinSkills.map((s) => [s.id, s.tools.filter((t) => t.access !== "read").map((t) => t.name)]));
|
|
38
|
+
function skillWriteTools(node) {
|
|
39
|
+
return node.skills.flatMap((id) => writeToolsBySkill.get(id) ?? []);
|
|
40
|
+
}
|
|
41
|
+
function union(...lists) {
|
|
42
|
+
return [...new Set(lists.flatMap((l) => l ?? []))];
|
|
43
|
+
}
|
|
44
|
+
/** What a read-only node could not reach for these providers. Empty when it reaches everything. */
|
|
45
|
+
export function readGaps(p) {
|
|
46
|
+
const gaps = [];
|
|
47
|
+
for (const o of p.observability)
|
|
48
|
+
if (!SKILL_OBSERVABILITY.has(o))
|
|
49
|
+
gaps.push(o);
|
|
50
|
+
if (!(p.issueTracker in TRACKER_SKILL) && p.issueTracker !== "file")
|
|
51
|
+
gaps.push(p.issueTracker);
|
|
52
|
+
if (p.sourceControl !== "github")
|
|
53
|
+
gaps.push(`${p.sourceControl} source control`);
|
|
54
|
+
if ((p.userMcpServers ?? []).length > 0)
|
|
55
|
+
gaps.push("user MCP servers");
|
|
56
|
+
if ((p.workspaceTools ?? []).length > 0)
|
|
57
|
+
gaps.push("workspace tools");
|
|
58
|
+
return [...new Set(gaps)];
|
|
59
|
+
}
|
|
60
|
+
/** The skill that applies this output type for these providers, or undefined when none can. */
|
|
61
|
+
export function outputSkill(type, p) {
|
|
62
|
+
if (type === "issue" || type === "comment")
|
|
63
|
+
return TRACKER_SKILL[p.issueTracker];
|
|
64
|
+
return p.sourceControl === "github" ? "github" : undefined;
|
|
65
|
+
}
|
|
66
|
+
/** A read node that needs the shell or MCP: write-capable, but no file edits and no skill write tool. */
|
|
67
|
+
function legacyRead(node) {
|
|
68
|
+
return {
|
|
69
|
+
...node,
|
|
70
|
+
permissions: { access: "write", deny: ["write", "edit"] },
|
|
71
|
+
tools: { ...(node.tools ?? {}), deny: union(node.tools?.deny, skillWriteTools(node)) },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** An output node whose writes no safe-output skill can apply: the tracker's own tools, as before. */
|
|
75
|
+
function legacyWrite(node) {
|
|
76
|
+
const { outputs, ...rest } = node;
|
|
77
|
+
const keep = new Set((outputs ?? []).flatMap((o) => LEGACY_WRITE_TOOLS[o.type]));
|
|
78
|
+
const deny = union(node.tools?.deny, skillWriteTools(node)).filter((t) => !keep.has(t));
|
|
79
|
+
const tools = node.tools?.allow
|
|
80
|
+
? { allow: node.tools.allow, ...(deny.length > 0 ? { deny } : {}) }
|
|
81
|
+
: deny.length > 0
|
|
82
|
+
? { deny }
|
|
83
|
+
: undefined;
|
|
84
|
+
const { tools: _old, ...base } = rest;
|
|
85
|
+
return { ...base, permissions: "write", ...(tools ? { tools } : {}) };
|
|
86
|
+
}
|
|
87
|
+
function bindOutput(o, p) {
|
|
88
|
+
const via = o.via ?? outputSkill(o.type, p);
|
|
89
|
+
const target = o.target ?? (via === "github" && p.repository ? p.repository : undefined);
|
|
90
|
+
return { ...o, ...(via ? { via } : {}), ...(target ? { target } : {}) };
|
|
91
|
+
}
|
|
92
|
+
export function bindBuiltinWorkflow(workflow, p) {
|
|
93
|
+
const gaps = readGaps(p);
|
|
94
|
+
const legacyReads = [];
|
|
95
|
+
const legacyWrites = [];
|
|
96
|
+
const unsupported = new Set();
|
|
97
|
+
const nodes = {};
|
|
98
|
+
for (const [id, node] of Object.entries(workflow.nodes)) {
|
|
99
|
+
const outputs = node.outputs ?? [];
|
|
100
|
+
const access = resolveNodePermissions(node, workflow).access;
|
|
101
|
+
const missing = outputs.filter((o) => !outputSkill(o.type, p));
|
|
102
|
+
if (missing.length > 0) {
|
|
103
|
+
for (const o of missing)
|
|
104
|
+
unsupported.add(o.type === "pr" || o.type === "label" ? p.sourceControl : p.issueTracker);
|
|
105
|
+
legacyWrites.push(id);
|
|
106
|
+
nodes[id] = legacyWrite(node);
|
|
107
|
+
}
|
|
108
|
+
else if (outputs.length > 0) {
|
|
109
|
+
nodes[id] = { ...node, outputs: outputs.map((o) => bindOutput(o, p)) };
|
|
110
|
+
}
|
|
111
|
+
else if (access === "read" && gaps.length > 0) {
|
|
112
|
+
legacyReads.push(id);
|
|
113
|
+
nodes[id] = legacyRead(node);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
nodes[id] = node;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const notes = [];
|
|
120
|
+
if (legacyReads.length > 0) {
|
|
121
|
+
notes.push(`${legacyReads.join(", ")} run write-capable (file edits and write tools still denied): ` +
|
|
122
|
+
`a read-only step cannot reach ${gaps.join(", ")}`);
|
|
123
|
+
}
|
|
124
|
+
if (legacyWrites.length > 0) {
|
|
125
|
+
notes.push(`${legacyWrites.join(", ")} write through the tracker's own tools: ` +
|
|
126
|
+
`no safe-output skill writes to ${[...unsupported].join(", ")}`);
|
|
127
|
+
}
|
|
128
|
+
return { workflow: { ...workflow, nodes }, notes };
|
|
129
|
+
}
|
package/dist/cli/check.d.ts
CHANGED
|
@@ -29,15 +29,9 @@ export declare function checkDatadog(creds: Record<string, string>): Promise<Che
|
|
|
29
29
|
/**
|
|
30
30
|
* Detect a local Claude Code login the way `sweny workflow run` benefits from
|
|
31
31
|
* it: the spawned Claude Code agent finds its own login, so no env credential
|
|
32
|
-
* is needed.
|
|
33
|
-
* on macOS, the Keychain entry (attributes only, the secret is never read).
|
|
32
|
+
* is needed. Lives with the harness login probes (#339); re-exported here.
|
|
34
33
|
*/
|
|
35
|
-
export
|
|
36
|
-
env?: Record<string, string | undefined>;
|
|
37
|
-
home?: string;
|
|
38
|
-
platform?: NodeJS.Platform;
|
|
39
|
-
keychainHasLogin?: () => boolean;
|
|
40
|
-
}): boolean;
|
|
34
|
+
export { detectClaudeCodeLogin } from "../harness/auth.js";
|
|
41
35
|
/**
|
|
42
36
|
* Skill ids used by the workflows in `.sweny/workflows/`. `found` is false when
|
|
43
37
|
* there are no workflow files, in which case callers keep the legacy checks.
|
package/dist/cli/check.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import * as os from "node:os";
|
|
3
2
|
import * as path from "node:path";
|
|
4
|
-
import { spawnSync } from "node:child_process";
|
|
5
3
|
import { parse as parseYaml } from "yaml";
|
|
6
4
|
import { validateInputs } from "./config.js";
|
|
7
5
|
import { findSkillEnvGaps } from "./skill-env.js";
|
|
@@ -349,39 +347,9 @@ function networkErrorMessage(err) {
|
|
|
349
347
|
/**
|
|
350
348
|
* Detect a local Claude Code login the way `sweny workflow run` benefits from
|
|
351
349
|
* it: the spawned Claude Code agent finds its own login, so no env credential
|
|
352
|
-
* is needed.
|
|
353
|
-
* on macOS, the Keychain entry (attributes only, the secret is never read).
|
|
350
|
+
* is needed. Lives with the harness login probes (#339); re-exported here.
|
|
354
351
|
*/
|
|
355
|
-
export
|
|
356
|
-
const env = opts.env ?? process.env;
|
|
357
|
-
const home = opts.home ?? os.homedir();
|
|
358
|
-
const platform = opts.platform ?? process.platform;
|
|
359
|
-
const configDir = env.CLAUDE_CONFIG_DIR || path.join(home, ".claude");
|
|
360
|
-
try {
|
|
361
|
-
if (fs.existsSync(path.join(configDir, ".credentials.json")))
|
|
362
|
-
return true;
|
|
363
|
-
}
|
|
364
|
-
catch {
|
|
365
|
-
// fall through
|
|
366
|
-
}
|
|
367
|
-
if (platform === "darwin") {
|
|
368
|
-
const probe = opts.keychainHasLogin ??
|
|
369
|
-
(() => {
|
|
370
|
-
const r = spawnSync("security", ["find-generic-password", "-s", "Claude Code-credentials"], {
|
|
371
|
-
stdio: "ignore",
|
|
372
|
-
timeout: 3000,
|
|
373
|
-
});
|
|
374
|
-
return r.status === 0;
|
|
375
|
-
});
|
|
376
|
-
try {
|
|
377
|
-
return probe();
|
|
378
|
-
}
|
|
379
|
-
catch {
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
352
|
+
export { detectClaudeCodeLogin } from "../harness/auth.js";
|
|
385
353
|
/**
|
|
386
354
|
* Skill ids used by the workflows in `.sweny/workflows/`. `found` is false when
|
|
387
355
|
* there are no workflow files, in which case callers keep the legacy checks.
|
package/dist/cli/config.js
CHANGED
|
@@ -23,6 +23,7 @@ export function registerTriageCommand(program) {
|
|
|
23
23
|
.option("--base-branch <branch>", "Base branch for PRs (default: main)")
|
|
24
24
|
.option("--pr-labels <labels>", "Comma-separated PR labels (default: agent,triage,needs-review)")
|
|
25
25
|
.option("--dry-run", "Analyze only, do not create issues or PRs", false)
|
|
26
|
+
.option("--stage", "Run normally, but preview the issues, comments and PR instead of filing them; stops before any code is pushed", false)
|
|
26
27
|
.option("--review-mode <mode>", "PR merge behavior: auto (merge when CI passes) | review (human approval, default)", "review")
|
|
27
28
|
.option("--no-novelty-mode", "Disable novelty mode (allow +1 on existing issues)")
|
|
28
29
|
.option("--issue-override <issue>", "Work on a specific existing issue")
|
|
@@ -670,6 +671,7 @@ export function registerImplementCommand(program) {
|
|
|
670
671
|
.option("--issue-tracker-provider <provider>", "Issue tracker (linear|jira|github-issues|file)")
|
|
671
672
|
.option("--source-control-provider <provider>", "Source control (github|gitlab|file)")
|
|
672
673
|
.option("--dry-run", "Skip creating PR — report only", false)
|
|
674
|
+
.option("--stage", "Run normally, but preview the PR and issue comment instead of writing them", false)
|
|
673
675
|
.option("--max-implement-turns <n>", "Max coding agent turns (default: 40)")
|
|
674
676
|
.option("--base-branch <branch>", "Base branch for PRs (default: main)")
|
|
675
677
|
.option("--repository <owner/repo>", "Repository (auto-detected from git remote)")
|
package/dist/cli/main.js
CHANGED
|
@@ -10,6 +10,7 @@ import { execute } from "../executor.js";
|
|
|
10
10
|
import { triageWorkflow, implementWorkflow, seedContentWorkflow } from "../workflows/index.js";
|
|
11
11
|
import { consoleLogger } from "../types.js";
|
|
12
12
|
import { createHarness } from "../harness/index.js";
|
|
13
|
+
import { bindBuiltinWorkflow } from "./builtin-workflows.js";
|
|
13
14
|
import { isSupportedAgent, unsupportedAgentError } from "../harness/agents.js";
|
|
14
15
|
import { resolveHarnessPolicy } from "../harness/policy.js";
|
|
15
16
|
import { builtinSkills, createSkillMap, validateWorkflowSkills } from "../skills/index.js";
|
|
@@ -94,6 +95,8 @@ program
|
|
|
94
95
|
await runNew({ marketplaceId: id, template: options.template, yes: options.yes });
|
|
95
96
|
});
|
|
96
97
|
// ── sweny check ───────────────────────────────────────────────────────
|
|
98
|
+
/** validateInputs' generic agent-auth lines, replaced by the harness preflight reason (#339). */
|
|
99
|
+
const AGENT_AUTH_ERROR_PREFIXES = ["Missing: ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN", "Missing: CODEX_API_KEY"];
|
|
97
100
|
program
|
|
98
101
|
.command("check")
|
|
99
102
|
.description("Verify provider credentials and connectivity")
|
|
@@ -103,13 +106,22 @@ program
|
|
|
103
106
|
const workflows = discoverWorkflowSkillIds(process.cwd());
|
|
104
107
|
const scope = workflows.found ? workflows.skillIds : undefined;
|
|
105
108
|
const claudeCodeLogin = detectClaudeCodeLogin();
|
|
106
|
-
|
|
109
|
+
let errors = validateCheckInputs(config, { scope, claudeCodeLogin });
|
|
110
|
+
// #339: the agent's own preflight (install and login), named by --agent.
|
|
111
|
+
// Its reason replaces the generic agent-auth line from validateInputs.
|
|
112
|
+
const agent = config.codingAgentProvider;
|
|
113
|
+
const pre = isSupportedAgent(agent) ? await createHarness(agent, { logger: consoleLogger }).preflight() : undefined;
|
|
114
|
+
if (pre && !pre.ok) {
|
|
115
|
+
errors = [pre.reason, ...errors.filter((e) => !AGENT_AUTH_ERROR_PREFIXES.some((p) => e.startsWith(p)))];
|
|
116
|
+
}
|
|
107
117
|
if (errors.length > 0) {
|
|
108
118
|
console.error(formatValidationErrors(errors));
|
|
109
119
|
process.exit(1);
|
|
110
120
|
}
|
|
111
121
|
console.log(chalk.dim("\n Checking provider connectivity…\n"));
|
|
112
122
|
const results = await checkProviderConnectivity(config, { scope, claudeCodeLogin });
|
|
123
|
+
if (pre?.ok)
|
|
124
|
+
results.unshift({ name: `Agent (${agent})`, status: "ok", detail: `ready (${pre.version})` });
|
|
113
125
|
console.log(formatCheckResults(results));
|
|
114
126
|
const hasFailure = results.some((r) => r.status === "fail");
|
|
115
127
|
process.exit(hasFailure ? 1 : 0);
|
|
@@ -178,8 +190,9 @@ async function resolveRulesAndContext(config) {
|
|
|
178
190
|
};
|
|
179
191
|
}
|
|
180
192
|
/**
|
|
181
|
-
* Build the harness for `--agent` (#331)
|
|
182
|
-
*
|
|
193
|
+
* Build the harness for `--agent` (#331) and preflight it before any node
|
|
194
|
+
* runs (#339): the adapter checks its own install and login, so a missing
|
|
195
|
+
* CLI or a missing login fails fast with the fix, naming the agent.
|
|
183
196
|
*/
|
|
184
197
|
async function harnessFor(agent, opts, harnessPolicy) {
|
|
185
198
|
if (!isSupportedAgent(agent)) {
|
|
@@ -190,15 +203,31 @@ async function harnessFor(agent, opts, harnessPolicy) {
|
|
|
190
203
|
...opts,
|
|
191
204
|
policy: resolveHarnessPolicy(process.env, harnessPolicy, opts.logger),
|
|
192
205
|
});
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
process.exit(1);
|
|
198
|
-
}
|
|
206
|
+
const pre = await harness.preflight();
|
|
207
|
+
if (!pre.ok) {
|
|
208
|
+
console.error(chalk.red(`\n ${pre.reason}\n`));
|
|
209
|
+
process.exit(1);
|
|
199
210
|
}
|
|
200
211
|
return harness;
|
|
201
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Bind a built-in workflow to this run's providers (#365) and say which nodes
|
|
215
|
+
* fall back to their pre-safe-outputs shape, and why.
|
|
216
|
+
*/
|
|
217
|
+
function bindForCli(workflow, config) {
|
|
218
|
+
const bound = bindBuiltinWorkflow(workflow, {
|
|
219
|
+
issueTracker: config.issueTrackerProvider,
|
|
220
|
+
sourceControl: config.sourceControlProvider,
|
|
221
|
+
observability: config.observabilityProviders,
|
|
222
|
+
repository: config.repository || undefined,
|
|
223
|
+
userMcpServers: Object.keys(config.mcpServers),
|
|
224
|
+
workspaceTools: config.workspaceTools,
|
|
225
|
+
});
|
|
226
|
+
if (!config.json)
|
|
227
|
+
for (const note of bound.notes)
|
|
228
|
+
console.error(c.subtle(` ${workflow.id}: ${note}`));
|
|
229
|
+
return bound.workflow;
|
|
230
|
+
}
|
|
202
231
|
// ── sweny triage ──────────────────────────────────────────────────────
|
|
203
232
|
const triageCmd = registerTriageCommand(program);
|
|
204
233
|
triageCmd.action(async (options) => {
|
|
@@ -401,8 +430,11 @@ triageCmd.action(async (options) => {
|
|
|
401
430
|
console.log(c.subtle(` cloud: ${cloudHandle.dashboardUrl}`));
|
|
402
431
|
}
|
|
403
432
|
const observer = composeObservers(progressObserver, config.verbose ? createVerboseToolObserver() : undefined, config.stream ? createStreamObserver() : undefined, createCloudStreamObserver(config, cloudHandle));
|
|
433
|
+
// #365: pin safe outputs to the configured tracker and repo; nodes these
|
|
434
|
+
// providers cannot run read-only or through safe outputs keep their old shape.
|
|
435
|
+
const triageRun = bindForCli(triageWorkflow, config);
|
|
404
436
|
try {
|
|
405
|
-
const { results, trace } = await execute(
|
|
437
|
+
const { results, trace } = await execute(triageRun, workflowInput, {
|
|
406
438
|
skills,
|
|
407
439
|
harness: claude,
|
|
408
440
|
observer,
|
|
@@ -412,6 +444,7 @@ triageCmd.action(async (options) => {
|
|
|
412
444
|
fetchAuth: config.fetchAuth,
|
|
413
445
|
offline: config.offline,
|
|
414
446
|
fileRoot: config.fileRoot || undefined,
|
|
447
|
+
stageOutputs: options.stage === true,
|
|
415
448
|
});
|
|
416
449
|
const durationMs = Date.now() - runStart;
|
|
417
450
|
// Output
|
|
@@ -605,8 +638,9 @@ implementCmd.action(async (issueId, options) => {
|
|
|
605
638
|
console.log(c.subtle(` cloud: ${implCloudHandle.dashboardUrl}`));
|
|
606
639
|
}
|
|
607
640
|
const observer = composeObservers(implProgressObserver, config.verbose ? createVerboseToolObserver() : undefined, Boolean(options.stream) ? createStreamObserver() : undefined, createCloudStreamObserver(config, implCloudHandle));
|
|
641
|
+
const implementRun = bindForCli(implementWorkflow, config);
|
|
608
642
|
try {
|
|
609
|
-
const { results } = await execute(
|
|
643
|
+
const { results } = await execute(implementRun, workflowInput, {
|
|
610
644
|
skills,
|
|
611
645
|
harness: claude,
|
|
612
646
|
observer,
|
|
@@ -616,6 +650,7 @@ implementCmd.action(async (issueId, options) => {
|
|
|
616
650
|
fetchAuth: config.fetchAuth,
|
|
617
651
|
offline: config.offline,
|
|
618
652
|
fileRoot: config.fileRoot || undefined,
|
|
653
|
+
stageOutputs: options.stage === true,
|
|
619
654
|
});
|
|
620
655
|
const hasFailed = [...results.values()].some((r) => r.status === "failed");
|
|
621
656
|
const implDurationMs = Date.now() - implRunStart;
|
|
@@ -1278,11 +1313,12 @@ workflowCmd
|
|
|
1278
1313
|
program
|
|
1279
1314
|
.command("tool-bridge", { hidden: true })
|
|
1280
1315
|
.description("Internal: stdio MCP shim for sweny skill tools")
|
|
1281
|
-
.
|
|
1316
|
+
.option("--socket <path>", "Per-run bridge socket")
|
|
1317
|
+
.option("--connect <host:port>", "Per-run bridge TCP endpoint (inside the process sandbox, via its proxy)")
|
|
1282
1318
|
.option("--token <token>", "Per-run token (default: SWENY_TOOL_BRIDGE_TOKEN)")
|
|
1283
1319
|
.action(async (options) => {
|
|
1284
1320
|
const { runToolBridgeShim } = await import("../harness/tool-bridge/shim.js");
|
|
1285
|
-
await runToolBridgeShim({ socket: options.socket, token: options.token, version });
|
|
1321
|
+
await runToolBridgeShim({ socket: options.socket, connect: options.connect, token: options.token, version });
|
|
1286
1322
|
});
|
|
1287
1323
|
// ── sweny upgrade / update ────────────────────────────────────────────
|
|
1288
1324
|
// Self-update the globally-installed @sweny-ai/core. Mirrors the UX of
|
package/dist/cli/packs.d.ts
CHANGED
|
@@ -10,11 +10,15 @@
|
|
|
10
10
|
* - validates with no credentials
|
|
11
11
|
* - every node declares an `output` schema and at least one `eval` gate
|
|
12
12
|
* - value and function gates first; a judge only where a rule cannot decide
|
|
13
|
-
* -
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* -
|
|
17
|
-
* read
|
|
13
|
+
* - safe outputs (#365): every GitHub write (issue, comment) is a declared
|
|
14
|
+
* output with a cap, and a pin or title prefix where one applies; sweny
|
|
15
|
+
* applies it after the node. No node holds a GitHub write tool.
|
|
16
|
+
* - least privilege: a workflow-level `permissions` ceiling, and every node
|
|
17
|
+
* is `read` except a delivery node that needs a write no safe output
|
|
18
|
+
* covers (Slack); its write tools are named in `pack.writes` and its tool
|
|
19
|
+
* list is restricted with `tools.allow`
|
|
20
|
+
* - harness-agnostic: instructions name SWEny skill tools, never a specific
|
|
21
|
+
* agent's built-in tools
|
|
18
22
|
*/
|
|
19
23
|
import type { WorkflowTemplate } from "./templates.js";
|
|
20
24
|
export declare const PACK_TEMPLATES: WorkflowTemplate[];
|