@plainconceptsplatform/workflows 0.4.20 → 0.4.28
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/catalog-installation.js +1 -1
- package/dist/catalog-listing.test.js +1 -1
- package/dist/index.js +2 -2
- package/dist/index.test.js +3 -3
- package/dist/workflow-catalog.d.ts +1 -1
- package/dist/workflow-catalog.js +2 -0
- package/dist/workflow-catalog.test.js +1 -1
- package/loops/actions/agent-output.cjs +1 -1
- package/loops/actions/classify-route/classify-route.sh +8 -1
- package/loops/scripts/compile-agent-workflows.mjs +23 -20
- package/loops/workflows/agent-implement.md +34 -20
- package/loops/workflows/authorize-bot-work.yml +2 -0
- package/loops/workflows/shared/opencode-ci.md +79 -16
- package/loops/workflows/work-router.yml +15 -0
- package/package.json +1 -1
|
@@ -277,7 +277,7 @@ function catalogTemplateMeta(template) {
|
|
|
277
277
|
const entry = catalogTemplates.find((item) => item.name === template);
|
|
278
278
|
if (entry === undefined)
|
|
279
279
|
throw new Error(`Unknown template: ${template}`);
|
|
280
|
-
const directory = template.startsWith("opencode") ? "opencode" : template.startsWith("app-ci-") ? "ci" : template === "github-release" ? "release" : "agentics";
|
|
280
|
+
const directory = template.startsWith("opencode") ? "opencode" : template.startsWith("app-ci-") ? "ci" : template === "github-release" ? "release" : template === "visual-evidence" ? "visual-evidence" : "agentics";
|
|
281
281
|
const isWorkflow = entry.file.endsWith(".yml");
|
|
282
282
|
const target = template === "app-ci-dotnet-next"
|
|
283
283
|
? ".github/workflows/app-ci.yml"
|
|
@@ -16,7 +16,7 @@ describe("catalog listing", () => {
|
|
|
16
16
|
const routeNames = entries.filter((entry) => entry.kind === "route").map((entry) => entry.name);
|
|
17
17
|
const templateNames = entries.filter((entry) => entry.kind === "template").map((entry) => entry.name);
|
|
18
18
|
expect(routeNames).toEqual(["refine", "implement", "direct", "apply-review", "merge-gate", "audit", "propose"]);
|
|
19
|
-
expect(templateNames).toEqual(["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "github-release", "opencode.ci.json"]);
|
|
19
|
+
expect(templateNames).toEqual(["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "github-release", "opencode.ci.json", "visual-evidence"]);
|
|
20
20
|
});
|
|
21
21
|
it("reports all entries as not installed in an empty repository", async () => {
|
|
22
22
|
const repositoryPath = await createRepository({});
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { inspectRepository, parseVisibility, resolveVisibility } from "./repository-inspection.js";
|
|
3
3
|
import { installCatalog, installTemplate, isTemplateName } from "./catalog-installation.js";
|
|
4
4
|
import { formatCatalog, listCatalog, searchCatalog } from "./catalog-listing.js";
|
|
5
|
-
import { routeNames } from "./workflow-catalog.js";
|
|
5
|
+
import { routeNames, templateNames } from "./workflow-catalog.js";
|
|
6
6
|
import { runInteractive } from "./tui.js";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
@@ -125,7 +125,7 @@ function readVisibilityOption(options) {
|
|
|
125
125
|
return "invalid";
|
|
126
126
|
return parseVisibility(options[1]) ?? "invalid";
|
|
127
127
|
}
|
|
128
|
-
const TEMPLATE_NAMES = "
|
|
128
|
+
const TEMPLATE_NAMES = templateNames.join("|");
|
|
129
129
|
function parseAddOptions(options) {
|
|
130
130
|
const routes = [];
|
|
131
131
|
let template;
|
package/dist/index.test.js
CHANGED
|
@@ -34,7 +34,7 @@ describe("workflows CLI", () => {
|
|
|
34
34
|
it("rejects an unsupported template name", async () => {
|
|
35
35
|
const error = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
|
36
36
|
await expect(run(["add", "--template", "unknown"])).resolves.toBe(1);
|
|
37
|
-
expect(error).toHaveBeenCalledWith("--template must be one of: agentics-checks|agentics-maintenance|app-ci-dotnet-next|app-ci-node-monorepo|opencode.ci.json.");
|
|
37
|
+
expect(error).toHaveBeenCalledWith("--template must be one of: agentics-checks|agentics-maintenance|app-ci-dotnet-next|app-ci-node-monorepo|github-release|opencode.ci.json|visual-evidence.");
|
|
38
38
|
error.mockRestore();
|
|
39
39
|
});
|
|
40
40
|
it("rejects an unknown route passed to add", async () => {
|
|
@@ -65,9 +65,9 @@ describe("workflows CLI", () => {
|
|
|
65
65
|
// None installed: all [ ]
|
|
66
66
|
const installedCount = (output.match(/\[x\]/g) ?? []).length;
|
|
67
67
|
expect(installedCount).toBe(0);
|
|
68
|
-
// 7 routes +
|
|
68
|
+
// 7 routes + 7 templates = 14 entries
|
|
69
69
|
const uninstalledCount = (output.match(/\[ \]/g) ?? []).length;
|
|
70
|
-
expect(uninstalledCount).toBe(
|
|
70
|
+
expect(uninstalledCount).toBe(14);
|
|
71
71
|
log.mockRestore();
|
|
72
72
|
});
|
|
73
73
|
it("marks installed workflows with [x]", async () => {
|
|
@@ -14,7 +14,7 @@ export interface MandatoryFile {
|
|
|
14
14
|
}
|
|
15
15
|
export declare const mandatoryFiles: readonly MandatoryFile[];
|
|
16
16
|
export declare const generatedConsumerTargets: readonly [".github/workflows/agent-*.lock.yml", ".github/aw/actions-lock.json"];
|
|
17
|
-
export declare const templateNames: readonly ["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "github-release", "opencode.ci.json"];
|
|
17
|
+
export declare const templateNames: readonly ["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "github-release", "opencode.ci.json", "visual-evidence"];
|
|
18
18
|
export type TemplateName = (typeof templateNames)[number];
|
|
19
19
|
export interface CatalogTemplate {
|
|
20
20
|
readonly name: TemplateName;
|
package/dist/workflow-catalog.js
CHANGED
|
@@ -40,6 +40,7 @@ export const templateNames = [
|
|
|
40
40
|
"app-ci-node-monorepo",
|
|
41
41
|
"github-release",
|
|
42
42
|
"opencode.ci.json",
|
|
43
|
+
"visual-evidence",
|
|
43
44
|
];
|
|
44
45
|
export const catalogTemplates = [
|
|
45
46
|
{ name: "agentics-checks", file: "agentics-checks.yml", description: "Agentics checks: verifies generated agent lockfiles, actionlint, and compile on PRs touching workflow files." },
|
|
@@ -48,4 +49,5 @@ export const catalogTemplates = [
|
|
|
48
49
|
{ name: "app-ci-node-monorepo", file: "app-ci-node-monorepo.yml", description: "App CI pipeline for a Node monorepo: build, test, and lint on PRs and schedule." },
|
|
49
50
|
{ name: "github-release", file: "github-release.yml", description: "Publishes or updates a GitHub Release with generated notes whenever a v* tag is pushed." },
|
|
50
51
|
{ name: "opencode.ci.json", file: "opencode.ci.json", description: "Standalone OpenCode CI config: plainconcepts provider, GLM model registration, ci-workflow-agent, and LSP defaults for consumer repositories." },
|
|
52
|
+
{ name: "visual-evidence", file: "visual-evidence.yml", description: "Visual evidence: captures screenshots of UI changes on bot-authored PRs by reading the capturePlan left by the agent in evidence.json and executing it on a runner with Docker and Chrome access." },
|
|
51
53
|
];
|
|
@@ -17,7 +17,7 @@ describe("workflow catalog", () => {
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
it("lists supported optional templates", () => {
|
|
20
|
-
expect(templateNames).toEqual(["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "github-release", "opencode.ci.json"]);
|
|
20
|
+
expect(templateNames).toEqual(["agentics-checks", "agentics-maintenance", "app-ci-dotnet-next", "app-ci-node-monorepo", "github-release", "opencode.ci.json", "visual-evidence"]);
|
|
21
21
|
});
|
|
22
22
|
it("gives every catalog template a non-empty description and file", () => {
|
|
23
23
|
expect(catalogTemplates.map((template) => template.name)).toEqual([...templateNames]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Managed by @plainconceptsplatform/workflows. Source: loops/actions/agent-output.cjs. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
2
|
-
const fs = require('fs');
|
|
2
|
+
const fs = require('node:fs');
|
|
3
3
|
|
|
4
4
|
// Every apply-agent-* action reads the same artifact the same way: a missing file means the
|
|
5
5
|
// agent produced nothing, which is a normal outcome rather than a failure.
|
|
@@ -37,7 +37,10 @@ classify_route() {
|
|
|
37
37
|
case "${LABEL:-}" in
|
|
38
38
|
bot-working)
|
|
39
39
|
# Bot adds bot-working → route based on which work label is present
|
|
40
|
-
if
|
|
40
|
+
# BUT: if review label is present, do NOT route (human review required)
|
|
41
|
+
if has_label review; then
|
|
42
|
+
error="issue has review label; bot-working does not re-trigger while human review is required"
|
|
43
|
+
elif has_label implement; then
|
|
41
44
|
route="implement"
|
|
42
45
|
issue_number="${EVENT_ISSUE_NUMBER:-}"
|
|
43
46
|
elif has_label refine; then
|
|
@@ -63,6 +66,10 @@ classify_route() {
|
|
|
63
66
|
elif [ "${LABEL:-}" = "direct" ]; then
|
|
64
67
|
direct_mode="first"
|
|
65
68
|
fi
|
|
69
|
+
elif has_label bot-working; then
|
|
70
|
+
# Already has bot-working - the workflow is already running or queued.
|
|
71
|
+
# Don't re-trigger.
|
|
72
|
+
error="issue already has bot-working label; implement/refine/direct already in progress"
|
|
66
73
|
else
|
|
67
74
|
error="waiting for bot to add bot-working label"
|
|
68
75
|
fi
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Managed by @plainconceptsplatform/workflows. Source: loops/scripts/compile-agent-workflows.mjs. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
5
|
|
|
6
6
|
const workflowDirectory = existsSync("loops/workflows") ? "loops/workflows" : ".github/workflows";
|
|
7
7
|
|
|
@@ -22,23 +22,26 @@ const compile = spawnSync(resolveGhPath(), ["aw", "compile", "--strict", "--dir"
|
|
|
22
22
|
shell: false,
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
if (compile.error?.code === "ENOENT" || compile.status === null) {
|
|
25
|
+
if (compile.error?.code === "ENOENT" || compile.status === null) {
|
|
26
26
|
process.stderr.write("Could not run `gh aw compile`. Install githubnext/gh-aw first.\n");
|
|
27
27
|
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (compile.status !== 0) process.exit(compile.status ?? 1);
|
|
31
|
-
|
|
32
|
-
for (const file of readdirSync(workflowDirectory)) {
|
|
33
|
-
if (!file.endsWith(".lock.yml")) continue;
|
|
34
|
-
|
|
35
|
-
const path = join(workflowDirectory, file);
|
|
36
|
-
const content = readFileSync(path, "utf8");
|
|
37
|
-
const patched = content
|
|
38
|
-
.replaceAll("opencode run --print-logs --log-level DEBUG", "opencode run --log-level ERROR")
|
|
39
|
-
.replaceAll("opencode run --print-logs --log-level ERROR", "opencode run --log-level ERROR")
|
|
40
|
-
.replaceAll("--log-level
|
|
41
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (compile.status !== 0) process.exit(compile.status ?? 1);
|
|
31
|
+
|
|
32
|
+
for (const file of readdirSync(workflowDirectory)) {
|
|
33
|
+
if (!file.endsWith(".lock.yml")) continue;
|
|
34
|
+
|
|
35
|
+
const path = join(workflowDirectory, file);
|
|
36
|
+
const content = readFileSync(path, "utf8");
|
|
37
|
+
const patched = content
|
|
38
|
+
.replaceAll("opencode run --print-logs --log-level DEBUG", "opencode run --port 4096 --log-level ERROR")
|
|
39
|
+
.replaceAll("opencode run --print-logs --log-level ERROR", "opencode run --port 4096 --log-level ERROR")
|
|
40
|
+
.replaceAll("opencode run --log-level ERROR", "opencode run --port 4096 --log-level ERROR")
|
|
41
|
+
.replaceAll("--log-level DEBUG", "--log-level ERROR")
|
|
42
|
+
.replace(/GH_AW_INFO_MODEL: "[^"]*"/g, 'GH_AW_INFO_MODEL: "per-agent"')
|
|
43
|
+
.replace(/OPENCODE_MODEL: [^\n]+/g, "OPENCODE_MODEL: ''")
|
|
44
|
+
.replace(/GH_AW_INFO_MODEL_COSTS: '[^']*'/g, 'GH_AW_INFO_MODEL_COSTS: \'{"providers":{}}\'');
|
|
45
|
+
|
|
46
|
+
if (patched !== content) writeFileSync(path, patched);
|
|
44
47
|
}
|
|
@@ -97,12 +97,6 @@ jobs:
|
|
|
97
97
|
token: ${{ steps.app-token.outputs.token }}
|
|
98
98
|
issue-number: ${{ inputs.issue-number }}
|
|
99
99
|
labels: ${{ env.REVIEW_LABEL }}
|
|
100
|
-
- name: Ensure implement label is present
|
|
101
|
-
uses: ./.github/actions/add-issue-labels
|
|
102
|
-
with:
|
|
103
|
-
token: ${{ steps.app-token.outputs.token }}
|
|
104
|
-
issue-number: ${{ inputs.issue-number }}
|
|
105
|
-
labels: ${{ env.IMPLEMENT_LABEL }}
|
|
106
100
|
conclude:
|
|
107
101
|
needs: [agent, safe_outputs]
|
|
108
102
|
if: >
|
|
@@ -164,7 +158,7 @@ jobs:
|
|
|
164
158
|
with:
|
|
165
159
|
token: ${{ steps.app-token.outputs.token }}
|
|
166
160
|
issue-number: ${{ inputs.issue-number }}
|
|
167
|
-
labels: ${{ env.WORKING_LABEL }}
|
|
161
|
+
labels: ${{ env.WORKING_LABEL }},implement
|
|
168
162
|
- name: Flag for human review
|
|
169
163
|
uses: ./.github/actions/add-issue-labels
|
|
170
164
|
with:
|
|
@@ -250,18 +244,29 @@ timeout-minutes: 90
|
|
|
250
244
|
`ob-ops-evidence`) and owns its procedure. You must not skip a phase unless the
|
|
251
245
|
pipeline's refined-issue detection says to.
|
|
252
246
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
247
|
+
d. The `apply` phase uses `ob-plan-apply` which delegates implementation to specialist
|
|
248
|
+
subagent waves. Let it own worker resolution, concurrency, and retry , do not
|
|
249
|
+
implement the tasks yourself unless `ob-plan-apply` instructs you to.
|
|
250
|
+
|
|
251
|
+
e. **Evidence phase:** The agent sandbox cannot run Docker or headless Chromium.
|
|
252
|
+
`ob-ops-evidence` writes a `capturePlan` in `evidence.json` instead of capturing
|
|
253
|
+
screenshots. A separate "Visual evidence" CI workflow runs the capturePlan on a
|
|
254
|
+
runner with full access. Do not attempt workarounds — write the capturePlan and move on.
|
|
256
255
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
f. Implement only what the issue asks for: a vague sentence is not licence to redesign
|
|
257
|
+
a module. Never read outside this repository root. The issue context at
|
|
258
|
+
`${{ env.ISSUE_CONTEXT_PATH }}` defines acceptance criteria that the pipeline must
|
|
259
|
+
satisfy.
|
|
261
260
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
261
|
+
g. Follow repository documentation and established conventions. Keep changes focused,
|
|
262
|
+
protect secrets, do not bypass checks, and do not modify generated files unless the issue requires it.
|
|
263
|
+
Adhere to ${{ env.REPO_RULES }}.
|
|
264
|
+
|
|
265
|
+
h. **DECISIVE IMPLEMENTATION.** When a design choice is ambiguous, pick the most
|
|
266
|
+
standard interpretation and implement it immediately. Do not deliberate between
|
|
267
|
+
options for more than one turn. Do not ask clarifying questions — the issue author
|
|
268
|
+
expects you to use good judgment. If two approaches are equally valid, pick one and
|
|
269
|
+
proceed. You can always iterate based on PR feedback.
|
|
265
270
|
|
|
266
271
|
4. Verify before you conclude. From the repository root:
|
|
267
272
|
|
|
@@ -269,10 +274,19 @@ timeout-minutes: 90
|
|
|
269
274
|
${{ env.VERIFY_COMMANDS }}
|
|
270
275
|
```
|
|
271
276
|
|
|
272
|
-
|
|
273
|
-
|
|
277
|
+
If a check fails, fix the cause and rerun. Do not weaken a test, lower a threshold, or skip
|
|
278
|
+
a check to make it pass.
|
|
279
|
+
|
|
280
|
+
5. Before creating the pull request, update `changelog.json` in the project's
|
|
281
|
+
`src/shared/data/` folder (create `src/shared/data/changelog.json` if it does not
|
|
282
|
+
exist; in a monorepo use `apps/web/src/shared/data/changelog.json`). The file
|
|
283
|
+
has shape `{"version":1,"changes":[...]}`. Use `jq` to prepend a new entry
|
|
284
|
+
with `"timestamp"` (ISO 8601), `"issue"` (number), `"title"` (issue title),
|
|
285
|
+
`"summary"` (1-2 sentences of what you changed), and `"commit"` (short SHA).
|
|
286
|
+
Keep at most 10 entries: if there are already 10, drop the oldest. Commit
|
|
287
|
+
this file as part of the same branch before creating the PR.
|
|
274
288
|
|
|
275
|
-
|
|
289
|
+
6. You **must** call exactly one safe-output tool before finishing, or the workflow
|
|
276
290
|
reports a failure. All safe-output tools are on the `safeoutputs` MCP server. Call
|
|
277
291
|
them using the `safeoutputs/<tool>` convention , for example:
|
|
278
292
|
|
|
@@ -11,7 +11,7 @@ description: |
|
|
|
11
11
|
|
|
12
12
|
Consumer-specific steps (NuGet, .NET restore, OpenSpec, etc.) should be added after the
|
|
13
13
|
shared baseline in the consumer copy. The merge step below is package-owned and required
|
|
14
|
-
for the agent to resolve
|
|
14
|
+
for the agent to resolve its provider and its models.
|
|
15
15
|
|
|
16
16
|
# Consumer repositories should add stack-specific steps (NuGet cache, dotnet restore,
|
|
17
17
|
# OpenSpec, Playwright, etc.) after the shared baseline. The merge step at the end is
|
|
@@ -21,15 +21,51 @@ pre-agent-steps:
|
|
|
21
21
|
- name: Create agent scratch directory
|
|
22
22
|
run: mkdir -p .opencode/.tmp
|
|
23
23
|
|
|
24
|
+
- name: Start OpenCode server (persistent warm server for faster agent runs)
|
|
25
|
+
run: |
|
|
26
|
+
set -euo pipefail
|
|
27
|
+
|
|
28
|
+
OPENCODE_PORT=4096
|
|
29
|
+
mkdir -p /tmp/opencode-data /tmp/gh-aw
|
|
30
|
+
|
|
31
|
+
# Check if server is already running
|
|
32
|
+
if curl -sf "http://127.0.0.1:${OPENCODE_PORT}/health" >/dev/null 2>&1; then
|
|
33
|
+
echo "OpenCode server already running on port ${OPENCODE_PORT}"
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
echo "Starting OpenCode server on port ${OPENCODE_PORT}..."
|
|
38
|
+
export XDG_DATA_HOME=/tmp/opencode-data
|
|
39
|
+
nohup opencode serve --port "${OPENCODE_PORT}" --hostname 127.0.0.1 \
|
|
40
|
+
> /tmp/gh-aw/opencode-server.log 2>&1 &
|
|
41
|
+
|
|
42
|
+
SERVER_PID=$!
|
|
43
|
+
echo "Server PID: ${SERVER_PID}"
|
|
44
|
+
|
|
45
|
+
# Wait for server to be ready (max 30 seconds)
|
|
46
|
+
for i in $(seq 1 30); do
|
|
47
|
+
if curl -sf "http://127.0.0.1:${OPENCODE_PORT}/health" >/dev/null 2>&1; then
|
|
48
|
+
echo "OpenCode server is ready on port ${OPENCODE_PORT}"
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
sleep 1
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
# Server didn't start in time - continue without it (agent will cold-start)
|
|
55
|
+
echo "::warning::OpenCode server did not start in 30s, agent will run without warm server"
|
|
56
|
+
kill "${SERVER_PID}" 2>/dev/null || true
|
|
57
|
+
|
|
24
58
|
- name: Install ripgrep
|
|
25
59
|
run: |
|
|
26
60
|
set -euo pipefail
|
|
27
61
|
|
|
28
|
-
if
|
|
29
|
-
|
|
30
|
-
|
|
62
|
+
if command -v rg > /dev/null 2>&1; then
|
|
63
|
+
echo "ripgrep already installed: $(rg --version | head -1)"
|
|
64
|
+
exit 0
|
|
31
65
|
fi
|
|
32
66
|
|
|
67
|
+
sudo apt-get update
|
|
68
|
+
sudo apt-get install --yes ripgrep
|
|
33
69
|
rg --version
|
|
34
70
|
|
|
35
71
|
- name: Activate the pnpm version package.json pins
|
|
@@ -50,6 +86,11 @@ pre-agent-steps:
|
|
|
50
86
|
run: |
|
|
51
87
|
set -euo pipefail
|
|
52
88
|
|
|
89
|
+
if command -v rtk > /dev/null 2>&1 && rtk --version 2>/dev/null | grep -q "${RTK_VERSION}"; then
|
|
90
|
+
echo "RTK ${RTK_VERSION} already installed"
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
53
94
|
tarball="$RUNNER_TEMP/rtk.tar.gz"
|
|
54
95
|
curl -fsSL -o "$tarball" \
|
|
55
96
|
"https://github.com/rtk-ai/rtk/releases/download/v${RTK_VERSION}/rtk-x86_64-unknown-linux-musl.tar.gz"
|
|
@@ -64,6 +105,12 @@ pre-agent-steps:
|
|
|
64
105
|
- name: Install agentmemory
|
|
65
106
|
run: |
|
|
66
107
|
set -euo pipefail
|
|
108
|
+
|
|
109
|
+
if command -v agentmemory > /dev/null 2>&1 && agentmemory --version 2>/dev/null | grep -q "${AGENTMEMORY_VERSION}"; then
|
|
110
|
+
echo "agentmemory ${AGENTMEMORY_VERSION} already installed"
|
|
111
|
+
exit 0
|
|
112
|
+
fi
|
|
113
|
+
|
|
67
114
|
npm install -g "@agentmemory/agentmemory@${AGENTMEMORY_VERSION}"
|
|
68
115
|
agentmemory --version
|
|
69
116
|
|
|
@@ -71,26 +118,42 @@ pre-agent-steps:
|
|
|
71
118
|
continue-on-error: true
|
|
72
119
|
run: |
|
|
73
120
|
set -euo pipefail
|
|
74
|
-
|
|
121
|
+
|
|
122
|
+
if command -v codegraph > /dev/null 2>&1 && codegraph --version 2>/dev/null | grep -q "${CODEGRAPH_VERSION}"; then
|
|
123
|
+
echo "codegraph ${CODEGRAPH_VERSION} already installed"
|
|
124
|
+
else
|
|
125
|
+
npm install -g "@colbymchenry/codegraph@${CODEGRAPH_VERSION}"
|
|
126
|
+
fi
|
|
127
|
+
|
|
75
128
|
codegraph init
|
|
76
129
|
|
|
77
|
-
- name: Install
|
|
130
|
+
- name: Install OpenSpec CLI
|
|
78
131
|
run: |
|
|
79
132
|
set -euo pipefail
|
|
80
133
|
|
|
81
|
-
if
|
|
82
|
-
echo "
|
|
134
|
+
if command -v openspec > /dev/null 2>&1 && openspec --version 2>/dev/null | grep -q "1.8.0"; then
|
|
135
|
+
echo "openspec 1.8.0 already installed"
|
|
83
136
|
exit 0
|
|
84
137
|
fi
|
|
85
138
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
139
|
+
npm install -g "@fission-ai/openspec@1.8.0"
|
|
140
|
+
openspec --version
|
|
141
|
+
|
|
142
|
+
# NOTE: playwright-cli and SQL Server startup steps have been removed from the
|
|
143
|
+
# shared CI baseline. Visual evidence capture now runs in a separate "Visual
|
|
144
|
+
# evidence" workflow that executes on the raw runner (not inside the awf
|
|
145
|
+
# sandbox), where Docker and headless Chromium are available. The agent's
|
|
146
|
+
# /ops-evidence skill writes a capturePlan in evidence.json when blocked;
|
|
147
|
+
# the Visual Evidence workflow reads and executes it. If you need playwright-cli
|
|
148
|
+
# or SQL Server inside the agent sandbox for other reasons, add them as
|
|
149
|
+
# consumer-specific steps after the shared baseline.
|
|
150
|
+
|
|
151
|
+
- name: Cache NuGet packages
|
|
152
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
153
|
+
with:
|
|
154
|
+
path: ~/.nuget/packages
|
|
155
|
+
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.slnx', '**/Directory.Packages.props') }}
|
|
156
|
+
restore-keys: nuget-${{ runner.os }}-
|
|
94
157
|
|
|
95
158
|
- name: Install workspace dependencies
|
|
96
159
|
run: pnpm install --frozen-lockfile
|
|
@@ -9,6 +9,21 @@ name: "All Work Router"
|
|
|
9
9
|
# describe the trigger but not the route the classifier picks.
|
|
10
10
|
run-name: >-
|
|
11
11
|
${{ github.event_name == 'issues'
|
|
12
|
+
&& github.event.label.name == 'bot-working'
|
|
13
|
+
&& contains(github.event.issue.labels.*.name, 'implement')
|
|
14
|
+
&& format('Working (Implement): {0} (#{1})', github.event.issue.title, github.event.issue.number)
|
|
15
|
+
|| github.event_name == 'issues'
|
|
16
|
+
&& github.event.label.name == 'bot-working'
|
|
17
|
+
&& contains(github.event.issue.labels.*.name, 'refine')
|
|
18
|
+
&& format('Working (Refine): {0} (#{1})', github.event.issue.title, github.event.issue.number)
|
|
19
|
+
|| github.event_name == 'issues'
|
|
20
|
+
&& github.event.label.name == 'bot-working'
|
|
21
|
+
&& contains(github.event.issue.labels.*.name, 'direct')
|
|
22
|
+
&& format('Working (Direct): {0} (#{1})', github.event.issue.title, github.event.issue.number)
|
|
23
|
+
|| github.event_name == 'issues'
|
|
24
|
+
&& github.event.label.name == 'bot-working'
|
|
25
|
+
&& format('Working: {0} (#{1})', github.event.issue.title, github.event.issue.number)
|
|
26
|
+
|| github.event_name == 'issues'
|
|
12
27
|
&& format('{0} label on #{1} by {2}', github.event.label.name, github.event.issue.number, github.actor)
|
|
13
28
|
|| github.event_name == 'issue_comment'
|
|
14
29
|
&& format('comment on #{0} by {1}', github.event.issue.number, github.actor)
|