@plainconceptsplatform/workflows 1.3.1 → 1.5.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.
Files changed (48) hide show
  1. package/dist/catalog-installation.d.ts +2 -0
  2. package/dist/catalog-installation.js +44 -2
  3. package/dist/index.js +3 -0
  4. package/dist/stack-defaults.d.ts +1 -0
  5. package/dist/stack-defaults.js +13 -1
  6. package/dist/worker-env.js +2 -3
  7. package/loops/actions/add-issue-labels/action.yml +50 -50
  8. package/loops/actions/agent-output.cjs +17 -17
  9. package/loops/actions/apply-agent-bundle/action.yml +24 -24
  10. package/loops/actions/apply-agent-comments/action.yml +42 -42
  11. package/loops/actions/apply-agent-labels/action.yml +55 -55
  12. package/loops/actions/apply-agent-output/action.yml +5 -0
  13. package/loops/actions/cleanup-artifacts/action.yml +91 -91
  14. package/loops/actions/close-agent-issues/action.yml +43 -43
  15. package/loops/actions/create-agent-issues/action.yml +64 -3
  16. package/loops/actions/create-issue-comment/action.yml +29 -29
  17. package/loops/actions/download-agent-output/action.yml +53 -53
  18. package/loops/actions/housekeeping/action.yml +95 -1
  19. package/loops/actions/link-pr-to-issue/action.yml +40 -40
  20. package/loops/actions/list-open-issues/action.yml +33 -33
  21. package/loops/actions/load-issue-context/action.yml +45 -45
  22. package/loops/actions/merge-agent-pr/action.yml +49 -49
  23. package/loops/actions/push-agent-branch/action.yml +45 -45
  24. package/loops/actions/remove-issue-labels/action.yml +37 -37
  25. package/loops/actions/update-agent-issues/action.yml +58 -58
  26. package/loops/actions/validate-merge-gate-output/action.yml +5 -0
  27. package/loops/actions/validate-merge-gate-output/validate-merge-gate-output.sh +8 -2
  28. package/loops/actions/validate-refine-output/validate-refine-output.sh +30 -1
  29. package/loops/actions/validate-review-output/action.yml +35 -35
  30. package/loops/actions/validate-triage-output/action.yml +36 -36
  31. package/loops/actions/verify-composite-actions/action.yml +9 -9
  32. package/loops/actions/verify-refine-output/action.yml +9 -9
  33. package/loops/actions/verify-refine-output/verify-refine-output.sh +17 -0
  34. package/loops/actions/verify-route-matrix/action.yml +9 -9
  35. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +221 -7
  36. package/loops/docs/diagrams.md +90 -12
  37. package/loops/scripts/compile-agent-workflows.mjs +76 -2
  38. package/loops/templates/agentics/agentics-maintenance.yml +121 -121
  39. package/loops/templates/issues/bug_report.yml +109 -109
  40. package/loops/templates/issues/feature_request.yml +75 -75
  41. package/loops/templates/release/github-release.yml +30 -30
  42. package/loops/workflows/agent-implement.md +43 -8
  43. package/loops/workflows/agent-merge-gate.md +102 -6
  44. package/loops/workflows/agent-refine.md +152 -5
  45. package/loops/workflows/authorize-bot-work.yml +105 -105
  46. package/loops/workflows/shared/platform-defaults.md +23 -23
  47. package/loops/workflows/work-router.yml +345 -2
  48. package/package.json +1 -1
@@ -30,6 +30,8 @@ export interface CatalogInstallResult {
30
30
  readonly baselines: readonly BaselineStatus[];
31
31
  readonly upToDate: boolean;
32
32
  readonly dryRun: boolean;
33
+ /** Problems the update cannot fix on its own, for a person to act on. */
34
+ readonly warnings: readonly string[];
33
35
  }
34
36
  export interface CatalogInstallOptions {
35
37
  /** Also overwrite files whose ownership header was removed, and changed templates. */
@@ -7,7 +7,7 @@ import { promisify } from "node:util";
7
7
  import { parse as parseYaml } from "yaml";
8
8
  import { catalogTemplates, mandatoryFiles, retiredWorkflowFiles, routeNames, templateNames, workflowRoutes } from "./workflow-catalog.js";
9
9
  import { processRoutes, excludedWorkerFiles } from "./route-processing.js";
10
- import { generateOpencodeCi, generateOpencodeConfig, generateStackDefaults, injectStackEnv } from "./stack-defaults.js";
10
+ import { generateOpencodeCi, generateOpencodeConfig, generateStackDefaults, injectStackEnv, shippedVerifyDefaults } from "./stack-defaults.js";
11
11
  import { mergeRouter, mergeWorker } from "./worker-env.js";
12
12
  import { fetchBaseline, hasOwnershipHeader, installedVersion, packageVersion, stampVersion } from "./package-baseline.js";
13
13
  const execFileAsync = promisify(execFile);
@@ -133,6 +133,7 @@ export async function installCatalog(repositoryPath, options = {}) {
133
133
  baselines,
134
134
  upToDate: updates.length === 0 && removals.length === 0,
135
135
  dryRun: options.dryRun ?? false,
136
+ warnings: await verifyScriptWarnings(repositoryPath, updates, existing),
136
137
  };
137
138
  if (options.dryRun)
138
139
  return result;
@@ -158,12 +159,22 @@ export async function installCatalog(repositoryPath, options = {}) {
158
159
  // the empty string as the consumer's deliberate choice. All four consuming repositories were in
159
160
  // that state, so every merge-gate repair pushed a fix with the verification block interpolating
160
161
  // to nothing. An empty value is the absence of a choice, so an update fills it.
162
+ //
163
+ // So is a value the package wrote itself. The implement worker shipped a dotnet-only line, and
164
+ // every full-stack consumer kept it as if someone had chosen it, so the bot never built the web
165
+ // app before opening a pull request. A consumer holding a shipped default gets the current one:
166
+ // its own value is blanked here, which the env merge then reads as no choice.
161
167
  function applyStackDefaults(files, inspection, existing) {
162
168
  const defaults = generateStackDefaults(inspection);
163
169
  const result = new Map(files);
164
170
  for (const [target, content] of result) {
165
- if (isWorker(target) && (!existing.has(target) || hasEmptyVerifyCommands(existing.get(target)))) {
171
+ const current = existing.get(target);
172
+ if (isWorker(target) && (current === undefined || hasEmptyVerifyCommands(current))) {
173
+ result.set(target, injectStackEnv(content, defaults));
174
+ }
175
+ else if (isWorker(target) && hasShippedVerifyCommands(current, defaults.verifyCommands)) {
166
176
  result.set(target, injectStackEnv(content, defaults));
177
+ existing.set(target, current.replace(VERIFY_LINE, ' VERIFY_COMMANDS: ""'));
167
178
  }
168
179
  else if (target.endsWith("opencode-ci.md")) {
169
180
  result.set(target, generateOpencodeCi(content, inspection));
@@ -174,11 +185,42 @@ function applyStackDefaults(files, inspection, existing) {
174
185
  }
175
186
  return result;
176
187
  }
188
+ const VERIFY_LINE = /^ {2}VERIFY_COMMANDS: *(.*?) *$/m;
177
189
  // A worker whose VERIFY_COMMANDS is present but empty. Anything else, including a value the
178
190
  // consumer wrote, is left alone.
179
191
  function hasEmptyVerifyCommands(current) {
180
192
  return typeof current === "string" && /^ {2}VERIFY_COMMANDS: *(""|'')? *$/m.test(current);
181
193
  }
194
+ function verifyCommandsOf(content) {
195
+ const raw = VERIFY_LINE.exec(content)?.[1];
196
+ return raw?.replace(/^(["'])(.*)\1$/, "$2");
197
+ }
198
+ function hasShippedVerifyCommands(current, replacement) {
199
+ if (current === undefined)
200
+ return false;
201
+ const value = verifyCommandsOf(current);
202
+ return value !== undefined && value !== replacement && shippedVerifyDefaults.has(value);
203
+ }
204
+ // `pnpm verify` is only a command if the repository defines it. The update still writes it, since
205
+ // it is the right default, and says what is missing instead of leaving the bot to find out.
206
+ async function verifyScriptWarnings(repositoryPath, updates, existing) {
207
+ const written = new Map(existing);
208
+ for (const update of updates)
209
+ written.set(update.target, update.content);
210
+ const workers = [...written].filter(([target, content]) => isWorker(target) && /\bpnpm verify\b/.test(verifyCommandsOf(content) ?? ""));
211
+ if (workers.length === 0)
212
+ return [];
213
+ let scripts = {};
214
+ try {
215
+ scripts = JSON.parse(await readFile(join(repositoryPath, "package.json"), "utf8")).scripts ?? {};
216
+ }
217
+ catch {
218
+ scripts = {};
219
+ }
220
+ if (typeof scripts.verify === "string")
221
+ return [];
222
+ return [`VERIFY_COMMANDS runs \`pnpm verify\` in ${workers.map(([target]) => target).sort().join(", ")}, but package.json has no "verify" script. Add one that runs what CI blocks on.`];
223
+ }
182
224
  // Directories that belong wholly to the package. A file here that carries our ownership header
183
225
  // and is no longer in the package was deleted upstream, and without this it would sit in every
184
226
  // consumer forever: that is how `stale-recovery` and `update-changelog` outlived the code that
package/dist/index.js CHANGED
@@ -139,6 +139,8 @@ export async function run(arguments_, repositoryPath = process.cwd()) {
139
139
  console.error(`Template conflicts found. Re-run with --force to overwrite consumer-owned copies:\n${allConflicts.join("\n")}`);
140
140
  return 1;
141
141
  }
142
+ for (const warning of catalog?.warnings ?? [])
143
+ console.error(`warning: ${warning}`);
142
144
  console.log(JSON.stringify({
143
145
  command,
144
146
  dryRun,
@@ -177,6 +179,7 @@ function summarize(result) {
177
179
  baselines: result.baselines,
178
180
  changes: result.changes.filter((change) => change.status !== "unchanged"),
179
181
  unchanged: result.changes.filter((change) => change.status === "unchanged").length,
182
+ ...(result.warnings.length > 0 ? { warnings: result.warnings } : {}),
180
183
  };
181
184
  }
182
185
  // A fixed baseline for every recorded version, instead of asking npm. Rolling out a version that
@@ -6,6 +6,7 @@ export interface StackDefaults {
6
6
  readonly hasNodeOnly: boolean;
7
7
  }
8
8
  export declare function generateStackDefaults(inspection: RepositoryInspection): StackDefaults;
9
+ export declare const shippedVerifyDefaults: ReadonlySet<string>;
9
10
  export declare function injectStackEnv(content: string, defaults: StackDefaults): string;
10
11
  export declare function generateOpencodeCi(baseContent: string, inspection: RepositoryInspection): string;
11
12
  export declare function generateOpencodeConfig(baseContent: string, inspection: RepositoryInspection): string;
@@ -4,8 +4,12 @@ export function generateStackDefaults(inspection) {
4
4
  const hasNodeOnly = hasNode && !hasDotnet;
5
5
  let verifyCommands;
6
6
  let repoRulesBase;
7
+ // A full-stack repository verifies with its own `pnpm verify`, which runs what its CI blocks on.
8
+ // The command this replaced was prose the agent had to interpret, and the implement worker never
9
+ // got it at all: it kept the package's dotnet-only line, so bot pull requests shipped with the
10
+ // web build broken (Pliny-Bot #639).
7
11
  if (hasDotnet && hasNode) {
8
- verifyCommands = ".NET: dotnet restore && dotnet build -c Release && dotnet test | Web: pnpm lint && pnpm test && pnpm build";
12
+ verifyCommands = "pnpm verify";
9
13
  repoRulesBase = "Full-stack .NET + React/Next.js repository. Follow Clean Architecture layering: API → Application → Domain. Infrastructure implements Application ports. Do not reference EF Core or ASP.NET from Application. Frontend communicates exclusively via HTTP endpoints. Run both .NET and frontend verification.";
10
14
  }
11
15
  else if (hasDotnet) {
@@ -18,6 +22,14 @@ export function generateStackDefaults(inspection) {
18
22
  }
19
23
  return { verifyCommands, repoRulesBase, hasDotnet, hasNodeOnly };
20
24
  }
25
+ // Every VERIFY_COMMANDS value the package itself has ever written. A consumer holding one of these
26
+ // never chose it, so an update may replace it with the current default for the stack.
27
+ export const shippedVerifyDefaults = new Set([
28
+ ".NET: dotnet restore && dotnet build -c Release && dotnet test | Web: pnpm lint && pnpm test && pnpm build",
29
+ "dotnet restore && dotnet build -c Release --no-restore && dotnet test",
30
+ "dotnet restore && dotnet build -c Release --no-restore && dotnet test -c Release --no-build",
31
+ "pnpm verify",
32
+ ]);
21
33
  // Only a worker that prints a verification block declares VERIFY_COMMANDS. Adding the key to
22
34
  // the others made byte-identical workers read as changed on every update, so a worker without
23
35
  // the key is left without it.
@@ -139,9 +139,8 @@ function engineEndpoint(content) {
139
139
  // triage, refine, implement, apply-review and audit on RunnerLandingZone while its merge-gate
140
140
  // and release stayed on agents-arc -- the pool was applied to the workers installed at the time
141
141
  // and never to the ones added later, and nothing reported the split. The pool is package-owned
142
- // now: the agent jobs belong on the Azure fleet (agents-arc, runner group `agentic`), and the
143
- // router and release worker name RunnerLandingZone. The route matrix asserts both, so a repo
144
- // cannot drift back without a red run. Per the standing rule for this package, anything a
142
+ // now: every job, router and release included, belongs on the Azure fleet (agents-arc, runner
143
+ // group `agentic`). The route matrix asserts it, so a repo cannot drift back without a red run. Per the standing rule for this package, anything a
145
144
  // consumer really must vary belongs in `env:` at the top of the file, not in `runs-on`.
146
145
  // GitHub evaluates no expression in a `workflow_run.workflows:` list or in a `cron:`, so the two
147
146
  // router values that are also needed there cannot be read from `env:` at those two lines. The
@@ -1,50 +1,50 @@
1
- # Managed by @plainconceptsplatform/workflows. Source: loops/actions/add-issue-labels/action.yml. Update with workflows update --force; consumer edits may be overwritten.
2
- name: Add issue labels
3
- description: Add one or more labels to an issue or pull request.
4
- inputs:
5
- token:
6
- description: GitHub token used by github-script.
7
- required: true
8
- issue-number:
9
- description: Issue or pull request number.
10
- required: true
11
- labels:
12
- description: Labels to add, one per line. A comma-separated list is accepted as well.
13
- required: true
14
- runs:
15
- using: composite
16
- steps:
17
- - name: Add labels
18
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
19
- env:
20
- ISSUE_NUMBER: ${{ inputs.issue-number }}
21
- LABELS: ${{ inputs.labels }}
22
- with:
23
- github-token: ${{ inputs.token }}
24
- script: |
25
- const labels = process.env.LABELS.split(/\r?\n|,/).map((label) => label.trim()).filter(Boolean);
26
- if (labels.length === 0) {
27
- core.notice('No labels to add.');
28
- return;
29
- }
30
-
31
- // Create what does not exist yet, so shipping a new label in the package does not
32
- // require a person to go and make it in every repository first. Its sibling
33
- // remove-issue-labels has swallowed 404 for a long time; this one had no guard at all,
34
- // so a label the package introduced would fail the step in every consumer until
35
- // somebody created it by hand. 422 is "already exists", which is the happy path on
36
- // every run after the first.
37
- for (const name of labels) {
38
- try {
39
- await github.rest.issues.createLabel({ ...context.repo, name, color: 'ededed' });
40
- core.notice(`Created the ${name} label.`);
41
- } catch (error) {
42
- if (error.status !== 422) throw error;
43
- }
44
- }
45
-
46
- await github.rest.issues.addLabels({
47
- ...context.repo,
48
- issue_number: Number(process.env.ISSUE_NUMBER),
49
- labels,
50
- });
1
+ # Managed by @plainconceptsplatform/workflows. Source: loops/actions/add-issue-labels/action.yml. Update with workflows update --force; consumer edits may be overwritten.
2
+ name: Add issue labels
3
+ description: Add one or more labels to an issue or pull request.
4
+ inputs:
5
+ token:
6
+ description: GitHub token used by github-script.
7
+ required: true
8
+ issue-number:
9
+ description: Issue or pull request number.
10
+ required: true
11
+ labels:
12
+ description: Labels to add, one per line. A comma-separated list is accepted as well.
13
+ required: true
14
+ runs:
15
+ using: composite
16
+ steps:
17
+ - name: Add labels
18
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
19
+ env:
20
+ ISSUE_NUMBER: ${{ inputs.issue-number }}
21
+ LABELS: ${{ inputs.labels }}
22
+ with:
23
+ github-token: ${{ inputs.token }}
24
+ script: |
25
+ const labels = process.env.LABELS.split(/\r?\n|,/).map((label) => label.trim()).filter(Boolean);
26
+ if (labels.length === 0) {
27
+ core.notice('No labels to add.');
28
+ return;
29
+ }
30
+
31
+ // Create what does not exist yet, so shipping a new label in the package does not
32
+ // require a person to go and make it in every repository first. Its sibling
33
+ // remove-issue-labels has swallowed 404 for a long time; this one had no guard at all,
34
+ // so a label the package introduced would fail the step in every consumer until
35
+ // somebody created it by hand. 422 is "already exists", which is the happy path on
36
+ // every run after the first.
37
+ for (const name of labels) {
38
+ try {
39
+ await github.rest.issues.createLabel({ ...context.repo, name, color: 'ededed' });
40
+ core.notice(`Created the ${name} label.`);
41
+ } catch (error) {
42
+ if (error.status !== 422) throw error;
43
+ }
44
+ }
45
+
46
+ await github.rest.issues.addLabels({
47
+ ...context.repo,
48
+ issue_number: Number(process.env.ISSUE_NUMBER),
49
+ labels,
50
+ });
@@ -1,17 +1,17 @@
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('node:fs');
3
-
4
- // Every apply-agent-* action reads the same artifact the same way: a missing file means the
5
- // agent produced nothing, which is a normal outcome rather than a failure.
6
- function readAgentItems(outputFile, type) {
7
- if (!outputFile || !fs.existsSync(outputFile)) {
8
- return [];
9
- }
10
-
11
- const parsed = JSON.parse(fs.readFileSync(outputFile, 'utf8'));
12
- const items = Array.isArray(parsed.items) ? parsed.items : [];
13
-
14
- return type ? items.filter((item) => item.type === type) : items;
15
- }
16
-
17
- module.exports = { readAgentItems };
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('node:fs');
3
+
4
+ // Every apply-agent-* action reads the same artifact the same way: a missing file means the
5
+ // agent produced nothing, which is a normal outcome rather than a failure.
6
+ function readAgentItems(outputFile, type) {
7
+ if (!outputFile || !fs.existsSync(outputFile)) {
8
+ return [];
9
+ }
10
+
11
+ const parsed = JSON.parse(fs.readFileSync(outputFile, 'utf8'));
12
+ const items = Array.isArray(parsed.items) ? parsed.items : [];
13
+
14
+ return type ? items.filter((item) => item.type === type) : items;
15
+ }
16
+
17
+ module.exports = { readAgentItems };
@@ -1,24 +1,24 @@
1
- # Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-bundle/action.yml. Update with workflows update --force; consumer edits may be overwritten.
2
- name: Apply agent bundle
3
- description: Fast-forward an intended branch to the single commit exported by a verified git bundle.
4
- inputs:
5
- bundle-file:
6
- description: Path to the git bundle from the agent artifact.
7
- required: true
8
- target-branch:
9
- description: Branch that must fast-forward to the bundle commit.
10
- required: true
11
- base-branch:
12
- description: Existing branch used only when target-branch does not exist.
13
- required: false
14
- default: ''
15
- runs:
16
- using: composite
17
- steps:
18
- - name: Fast-forward branch from bundle
19
- shell: bash
20
- env:
21
- BUNDLE_FILE: ${{ inputs.bundle-file }}
22
- TARGET_BRANCH: ${{ inputs.target-branch }}
23
- BASE_BRANCH: ${{ inputs.base-branch }}
24
- run: bash "$GITHUB_ACTION_PATH/apply-bundle.sh" "$BUNDLE_FILE" "$TARGET_BRANCH" "$BASE_BRANCH"
1
+ # Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-bundle/action.yml. Update with workflows update --force; consumer edits may be overwritten.
2
+ name: Apply agent bundle
3
+ description: Fast-forward an intended branch to the single commit exported by a verified git bundle.
4
+ inputs:
5
+ bundle-file:
6
+ description: Path to the git bundle from the agent artifact.
7
+ required: true
8
+ target-branch:
9
+ description: Branch that must fast-forward to the bundle commit.
10
+ required: true
11
+ base-branch:
12
+ description: Existing branch used only when target-branch does not exist.
13
+ required: false
14
+ default: ''
15
+ runs:
16
+ using: composite
17
+ steps:
18
+ - name: Fast-forward branch from bundle
19
+ shell: bash
20
+ env:
21
+ BUNDLE_FILE: ${{ inputs.bundle-file }}
22
+ TARGET_BRANCH: ${{ inputs.target-branch }}
23
+ BASE_BRANCH: ${{ inputs.base-branch }}
24
+ run: bash "$GITHUB_ACTION_PATH/apply-bundle.sh" "$BUNDLE_FILE" "$TARGET_BRANCH" "$BASE_BRANCH"
@@ -1,42 +1,42 @@
1
- # Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-comments/action.yml. Update with workflows update --force; consumer edits may be overwritten.
2
- name: Apply agent comments
3
- description: Post every add_comment item from agent_output.json.
4
- inputs:
5
- output-file:
6
- description: Path to agent_output.json.
7
- required: true
8
- token:
9
- description: GitHub token with issues:write and pull-requests:write.
10
- required: true
11
- runs:
12
- using: composite
13
- steps:
14
- - name: Post comments
15
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
16
- env:
17
- AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
18
- OUTPUT_FILE: ${{ inputs.output-file }}
19
- with:
20
- github-token: ${{ inputs.token }}
21
- script: |
22
- const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
23
- const items = readAgentItems(process.env.OUTPUT_FILE, 'add_comment');
24
-
25
- let posted = 0;
26
-
27
- for (const item of items) {
28
- if (!item.item_number) {
29
- core.warning(`add_comment item has no item_number, skipping: ${JSON.stringify(item)}`);
30
- continue;
31
- }
32
-
33
- await github.rest.issues.createComment({
34
- ...context.repo,
35
- issue_number: Number(item.item_number),
36
- body: item.body,
37
- });
38
-
39
- posted += 1;
40
- }
41
-
42
- core.info(`Posted ${posted} comment(s).`);
1
+ # Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-comments/action.yml. Update with workflows update --force; consumer edits may be overwritten.
2
+ name: Apply agent comments
3
+ description: Post every add_comment item from agent_output.json.
4
+ inputs:
5
+ output-file:
6
+ description: Path to agent_output.json.
7
+ required: true
8
+ token:
9
+ description: GitHub token with issues:write and pull-requests:write.
10
+ required: true
11
+ runs:
12
+ using: composite
13
+ steps:
14
+ - name: Post comments
15
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
16
+ env:
17
+ AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
18
+ OUTPUT_FILE: ${{ inputs.output-file }}
19
+ with:
20
+ github-token: ${{ inputs.token }}
21
+ script: |
22
+ const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
23
+ const items = readAgentItems(process.env.OUTPUT_FILE, 'add_comment');
24
+
25
+ let posted = 0;
26
+
27
+ for (const item of items) {
28
+ if (!item.item_number) {
29
+ core.warning(`add_comment item has no item_number, skipping: ${JSON.stringify(item)}`);
30
+ continue;
31
+ }
32
+
33
+ await github.rest.issues.createComment({
34
+ ...context.repo,
35
+ issue_number: Number(item.item_number),
36
+ body: item.body,
37
+ });
38
+
39
+ posted += 1;
40
+ }
41
+
42
+ core.info(`Posted ${posted} comment(s).`);
@@ -1,55 +1,55 @@
1
- # Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-labels/action.yml. Update with workflows update --force; consumer edits may be overwritten.
2
- name: Apply agent labels
3
- description: Apply every add_labels and remove_labels item from agent_output.json.
4
- inputs:
5
- output-file:
6
- description: Path to agent_output.json.
7
- required: true
8
- token:
9
- description: GitHub token with issues:write.
10
- required: true
11
- runs:
12
- using: composite
13
- steps:
14
- - name: Apply label changes
15
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
16
- env:
17
- AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
18
- OUTPUT_FILE: ${{ inputs.output-file }}
19
- with:
20
- github-token: ${{ inputs.token }}
21
- script: |
22
- const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
23
- const items = readAgentItems(process.env.OUTPUT_FILE)
24
- .filter((item) => item.type === 'add_labels' || item.type === 'remove_labels');
25
-
26
- let added = 0;
27
- let removed = 0;
28
-
29
- for (const item of items) {
30
- const labels = (item.labels ?? []).map((label) => label.name).filter(Boolean);
31
-
32
- if (!item.item_number || labels.length === 0) {
33
- core.warning(`${item.type} item is not actionable, skipping: ${JSON.stringify(item)}`);
34
- continue;
35
- }
36
-
37
- const issue_number = Number(item.item_number);
38
-
39
- if (item.type === 'add_labels') {
40
- await github.rest.issues.addLabels({ ...context.repo, issue_number, labels });
41
- added += labels.length;
42
- continue;
43
- }
44
-
45
- for (const name of labels) {
46
- try {
47
- await github.rest.issues.removeLabel({ ...context.repo, issue_number, name });
48
- removed += 1;
49
- } catch (error) {
50
- if (error.status !== 404) throw error;
51
- }
52
- }
53
- }
54
-
55
- core.info(`Added ${added} label(s), removed ${removed}.`);
1
+ # Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-labels/action.yml. Update with workflows update --force; consumer edits may be overwritten.
2
+ name: Apply agent labels
3
+ description: Apply every add_labels and remove_labels item from agent_output.json.
4
+ inputs:
5
+ output-file:
6
+ description: Path to agent_output.json.
7
+ required: true
8
+ token:
9
+ description: GitHub token with issues:write.
10
+ required: true
11
+ runs:
12
+ using: composite
13
+ steps:
14
+ - name: Apply label changes
15
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
16
+ env:
17
+ AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
18
+ OUTPUT_FILE: ${{ inputs.output-file }}
19
+ with:
20
+ github-token: ${{ inputs.token }}
21
+ script: |
22
+ const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
23
+ const items = readAgentItems(process.env.OUTPUT_FILE)
24
+ .filter((item) => item.type === 'add_labels' || item.type === 'remove_labels');
25
+
26
+ let added = 0;
27
+ let removed = 0;
28
+
29
+ for (const item of items) {
30
+ const labels = (item.labels ?? []).map((label) => label.name).filter(Boolean);
31
+
32
+ if (!item.item_number || labels.length === 0) {
33
+ core.warning(`${item.type} item is not actionable, skipping: ${JSON.stringify(item)}`);
34
+ continue;
35
+ }
36
+
37
+ const issue_number = Number(item.item_number);
38
+
39
+ if (item.type === 'add_labels') {
40
+ await github.rest.issues.addLabels({ ...context.repo, issue_number, labels });
41
+ added += labels.length;
42
+ continue;
43
+ }
44
+
45
+ for (const name of labels) {
46
+ try {
47
+ await github.rest.issues.removeLabel({ ...context.repo, issue_number, name });
48
+ removed += 1;
49
+ } catch (error) {
50
+ if (error.status !== 404) throw error;
51
+ }
52
+ }
53
+ }
54
+
55
+ core.info(`Added ${added} label(s), removed ${removed}.`);
@@ -39,6 +39,10 @@ inputs:
39
39
  description: Issue an update_issue item targets when it names none.
40
40
  required: false
41
41
  default: ''
42
+ parent-issue:
43
+ description: The split parent; see create-agent-issues. Empty for anything but a split.
44
+ required: false
45
+ default: ''
42
46
  outputs:
43
47
  item-count:
44
48
  description: Number of items the agent produced.
@@ -92,6 +96,7 @@ runs:
92
96
  with:
93
97
  output-file: ${{ steps.output.outputs.output-file }}
94
98
  token: ${{ inputs.token }}
99
+ parent-issue: ${{ inputs.parent-issue }}
95
100
 
96
101
  - name: Post agent comments
97
102
  if: steps.output.outputs.item-count != '0'