@xn-intenton-z2a/agentic-lib 7.1.14 → 7.1.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xn-intenton-z2a/agentic-lib",
3
- "version": "7.1.14",
3
+ "version": "7.1.16",
4
4
  "description": "Agentic-lib Agentic Coding Systems SDK powering automated GitHub workflows.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -32,7 +32,20 @@ env:
32
32
  configPath: ".github/agentic-lib/agents/agentic-lib.yml"
33
33
 
34
34
  jobs:
35
+ params:
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - name: Normalise params
39
+ id: normalise
40
+ shell: bash
41
+ run: |
42
+ STEP='${{ inputs.step }}'
43
+ echo "step=${STEP:-all}" >> $GITHUB_OUTPUT
44
+ outputs:
45
+ step: ${{ steps.normalise.outputs.step }}
46
+
35
47
  maintain:
48
+ needs: params
36
49
  runs-on: ubuntu-latest
37
50
  steps:
38
51
  - uses: actions/checkout@v4
@@ -58,7 +71,7 @@ jobs:
58
71
  echo "libraryWritablePaths=${LIBRARY};${SOURCES}" >> $GITHUB_OUTPUT
59
72
 
60
73
  - name: Maintain features
61
- if: inputs.step == 'all' || inputs.step == 'features' || github.event_name == 'schedule'
74
+ if: needs.params.outputs.step == 'all' || needs.params.outputs.step == 'features'
62
75
  uses: ./.github/agentic-lib/actions/agentic-step
63
76
  env:
64
77
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -70,7 +83,7 @@ jobs:
70
83
  writable-paths: ${{ steps.config.outputs.featuresWritablePaths }}
71
84
 
72
85
  - name: Maintain library
73
- if: inputs.step == 'all' || inputs.step == 'library' || github.event_name == 'schedule'
86
+ if: needs.params.outputs.step == 'all' || needs.params.outputs.step == 'library'
74
87
  uses: ./.github/agentic-lib/actions/agentic-step
75
88
  env:
76
89
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -31,8 +31,21 @@ env:
31
31
  configPath: ".github/agentic-lib/agents/agentic-lib.yml"
32
32
 
33
33
  jobs:
34
+ params:
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - name: Normalise params
38
+ id: normalise
39
+ shell: bash
40
+ run: |
41
+ STEP='${{ inputs.step }}'
42
+ echo "step=${STEP:-all}" >> $GITHUB_OUTPUT
43
+ outputs:
44
+ step: ${{ steps.normalise.outputs.step }}
45
+
34
46
  review-issues:
35
- if: inputs.step == 'all' || inputs.step == 'review' || github.event_name == 'schedule'
47
+ needs: params
48
+ if: needs.params.outputs.step == 'all' || needs.params.outputs.step == 'review'
36
49
  runs-on: ubuntu-latest
37
50
  steps:
38
51
  - uses: actions/checkout@v4
@@ -56,7 +69,8 @@ jobs:
56
69
  instructions: ".github/agentic-lib/agents/agent-review-issue.md"
57
70
 
58
71
  enhance-issues:
59
- if: inputs.step == 'all' || inputs.step == 'enhance' || github.event_name == 'schedule'
72
+ needs: params
73
+ if: needs.params.outputs.step == 'all' || needs.params.outputs.step == 'enhance'
60
74
  runs-on: ubuntu-latest
61
75
  steps:
62
76
  - uses: actions/checkout@v4
@@ -33,7 +33,20 @@ env:
33
33
  configPath: ".github/agentic-lib/agents/agentic-lib.yml"
34
34
 
35
35
  jobs:
36
+ params:
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - name: Normalise params
40
+ id: normalise
41
+ shell: bash
42
+ run: |
43
+ MODEL='${{ inputs.model }}'
44
+ echo "model=${MODEL:-claude-sonnet-4}" >> $GITHUB_OUTPUT
45
+ outputs:
46
+ model: ${{ steps.normalise.outputs.model }}
47
+
36
48
  transform:
49
+ needs: params
37
50
  runs-on: ubuntu-latest
38
51
  steps:
39
52
  - uses: actions/checkout@v4
@@ -72,7 +85,7 @@ jobs:
72
85
  config: ${{ env.configPath }}
73
86
  instructions: ".github/agentic-lib/agents/agent-issue-resolution.md"
74
87
  test-command: "npm test"
75
- model: ${{ inputs.model || 'claude-sonnet-4' }}
88
+ model: ${{ needs.params.outputs.model }}
76
89
  writable-paths: ${{ steps.config.outputs.writablePaths }}
77
90
 
78
91
  - name: Commit and push changes
@@ -3,7 +3,12 @@
3
3
  # .github/workflows/agent-supervisor.yml
4
4
  #
5
5
  # Reactive orchestration — triggers workflows based on telemetry.
6
- # When a build fails, dispatches fix-code. When issues pile up, dispatches review.
6
+ # Watches all agentic workflows and takes corrective or follow-up actions:
7
+ # - Failed builds on PR branches → dispatch fix-code (with loop protection)
8
+ # - Successful merges → dispatch transform to pick up next issue
9
+ # - Discussion bot actions → dispatch maintain or transform as follow-up
10
+ # - Stale issues → dispatch review
11
+ # - Stale conflicting PRs → remove automerge label
7
12
 
8
13
  name: agent-supervisor
9
14
  run-name: "agent-supervisor [${{ github.ref_name }}]"
@@ -14,6 +19,10 @@ on:
14
19
  - test
15
20
  - agent-flow-transform
16
21
  - agent-flow-maintain
22
+ - agent-flow-fix-code
23
+ - agent-flow-review
24
+ - agent-discussions-bot
25
+ - ci-automerge
17
26
  types:
18
27
  - completed
19
28
  workflow_dispatch:
@@ -22,8 +31,14 @@ permissions:
22
31
  actions: write
23
32
  contents: read
24
33
  issues: read
34
+ pull-requests: write
25
35
  checks: read
26
36
 
37
+ env:
38
+ maxFixAttempts: "3"
39
+ staleDays: "14"
40
+ stalePrDays: "3"
41
+
27
42
  jobs:
28
43
  evaluate:
29
44
  runs-on: ubuntu-latest
@@ -32,54 +47,173 @@ jobs:
32
47
  uses: actions/github-script@v7
33
48
  with:
34
49
  script: |
50
+ const owner = context.repo.owner;
51
+ const repo = context.repo.repo;
35
52
  const workflowRun = context.payload.workflow_run;
53
+ const workflowName = workflowRun?.name || '';
54
+ const conclusion = workflowRun?.conclusion || '';
55
+ const branch = workflowRun?.head_branch || '';
56
+ const isAgenticBranch = branch && !['main', 'master'].includes(branch);
57
+ const maxFixAttempts = parseInt(process.env.maxFixAttempts) || 3;
58
+ const staleDays = parseInt(process.env.staleDays) || 14;
59
+ const stalePrDays = parseInt(process.env.stalePrDays) || 3;
36
60
 
37
- // If a workflow failed, trigger fix-code
38
- if (workflowRun && workflowRun.conclusion === 'failure') {
39
- const branch = workflowRun.head_branch;
40
- const isAgenticBranch = !['main', 'master'].includes(branch);
61
+ core.info(`Supervisor: workflow="${workflowName}" conclusion="${conclusion}" branch="${branch}"`);
41
62
 
42
- if (isAgenticBranch) {
43
- core.info(`Workflow "${workflowRun.name}" failed on branch ${branch}. Dispatching fix-code.`);
63
+ // ─── 1. Failure on PR branch → dispatch fix-code (with loop protection) ───
44
64
 
45
- // Find the PR for this branch
65
+ try {
66
+ if (workflowRun && conclusion === 'failure' && isAgenticBranch) {
46
67
  const { data: prs } = await github.rest.pulls.list({
47
- ...context.repo,
48
- state: 'open',
49
- head: `${context.repo.owner}:${branch}`,
68
+ owner, repo, state: 'open',
69
+ head: `${owner}:${branch}`,
50
70
  per_page: 1,
51
71
  });
52
72
 
53
73
  if (prs.length > 0) {
54
- await github.rest.actions.createWorkflowDispatch({
55
- ...context.repo,
74
+ const pr = prs[0];
75
+ const prNumber = pr.number;
76
+
77
+ // Count prior fix-code runs for this branch to prevent infinite loops
78
+ const { data: fixRuns } = await github.rest.actions.listWorkflowRuns({
79
+ owner, repo,
56
80
  workflow_id: 'agent-flow-fix-code.yml',
57
- ref: context.ref,
58
- inputs: { 'pr-number': String(prs[0].number) },
81
+ branch,
82
+ per_page: maxFixAttempts + 1,
59
83
  });
60
- core.info(`Dispatched fix-code for PR #${prs[0].number}`);
84
+ const fixCount = fixRuns.total_count;
85
+
86
+ if (fixCount >= maxFixAttempts) {
87
+ core.info(`PR #${prNumber} has had ${fixCount} fix attempts (limit: ${maxFixAttempts}). Removing automerge label.`);
88
+ try {
89
+ await github.rest.issues.removeLabel({
90
+ owner, repo, issue_number: prNumber, name: 'automerge',
91
+ });
92
+ } catch (e) { /* label may not exist */ }
93
+ await github.rest.issues.createComment({
94
+ owner, repo, issue_number: prNumber,
95
+ body: `Supervisor: ${fixCount} fix-code attempts have failed. Removing automerge label — manual intervention needed.`,
96
+ });
97
+ } else {
98
+ core.info(`Dispatching fix-code for PR #${prNumber} (attempt ${fixCount + 1}/${maxFixAttempts}).`);
99
+ await github.rest.actions.createWorkflowDispatch({
100
+ owner, repo,
101
+ workflow_id: 'agent-flow-fix-code.yml',
102
+ ref: 'main',
103
+ inputs: { 'pr-number': String(prNumber) },
104
+ });
105
+ }
106
+ }
107
+ }
108
+ } catch (err) {
109
+ core.warning(`Fix-code dispatch failed: ${err.message}`);
110
+ }
111
+
112
+ // ─── 2. Successful merge → dispatch transform for next issue ──────────
113
+
114
+ try {
115
+ if (workflowName === 'ci-automerge' && conclusion === 'success') {
116
+ core.info('Automerge succeeded. Dispatching transform to pick up next issue.');
117
+ await github.rest.actions.createWorkflowDispatch({
118
+ owner, repo,
119
+ workflow_id: 'agent-flow-transform.yml',
120
+ ref: 'main',
121
+ });
122
+ }
123
+ } catch (err) {
124
+ core.warning(`Post-merge transform dispatch failed: ${err.message}`);
125
+ }
126
+
127
+ // ─── 3. Discussion bot follow-through ─────────────────────────────────
128
+
129
+ try {
130
+ if (workflowName === 'agent-discussions-bot' && conclusion === 'success') {
131
+ // Check the most recent run's jobs for the outcome
132
+ const runId = workflowRun.id;
133
+ const { data: jobs } = await github.rest.actions.listJobsForWorkflowRun({
134
+ owner, repo, run_id: runId,
135
+ });
136
+ const respondJob = jobs.jobs.find(j => j.name === 'respond');
137
+ if (respondJob) {
138
+ // Look through step names/outputs for action indicators
139
+ const steps = respondJob.steps || [];
140
+ const agenticStep = steps.find(s => s.name === 'Respond to discussion');
141
+ if (agenticStep && agenticStep.conclusion === 'success') {
142
+ core.info('Discussion bot completed. Dispatching maintain to pick up any new features.');
143
+ await github.rest.actions.createWorkflowDispatch({
144
+ owner, repo,
145
+ workflow_id: 'agent-flow-maintain.yml',
146
+ ref: 'main',
147
+ });
148
+ }
61
149
  }
62
150
  }
151
+ } catch (err) {
152
+ core.warning(`Discussion bot follow-through failed: ${err.message}`);
63
153
  }
64
154
 
65
- // Check for stale issues (open > 14 days with no activity)
66
- const { data: issues } = await github.rest.issues.listForRepo({
67
- ...context.repo,
68
- state: 'open',
69
- labels: 'automated',
70
- sort: 'updated',
71
- direction: 'asc',
72
- per_page: 5,
73
- });
74
-
75
- const fourteenDaysAgo = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000);
76
- const staleIssues = issues.filter(i => new Date(i.updated_at) < fourteenDaysAgo);
77
-
78
- if (staleIssues.length > 0) {
79
- core.info(`Found ${staleIssues.length} stale issue(s). Dispatching review.`);
80
- await github.rest.actions.createWorkflowDispatch({
81
- ...context.repo,
82
- workflow_id: 'agent-flow-review.yml',
83
- ref: context.ref,
155
+ // ─── 4. Stale issues dispatch review ────────────────────────────────
156
+
157
+ try {
158
+ const { data: issues } = await github.rest.issues.listForRepo({
159
+ owner, repo, state: 'open',
160
+ labels: 'automated',
161
+ sort: 'updated',
162
+ direction: 'asc',
163
+ per_page: 5,
84
164
  });
165
+
166
+ const cutoff = new Date(Date.now() - staleDays * 24 * 60 * 60 * 1000);
167
+ const staleIssues = issues.filter(i => new Date(i.updated_at) < cutoff);
168
+
169
+ if (staleIssues.length > 0) {
170
+ core.info(`Found ${staleIssues.length} stale issue(s). Dispatching review.`);
171
+ await github.rest.actions.createWorkflowDispatch({
172
+ owner, repo,
173
+ workflow_id: 'agent-flow-review.yml',
174
+ ref: 'main',
175
+ });
176
+ }
177
+ } catch (err) {
178
+ core.warning(`Stale issue check failed: ${err.message}`);
179
+ }
180
+
181
+ // ─── 5. Stale conflicting PRs → remove automerge label ────────────────
182
+
183
+ try {
184
+ const { data: openPRs } = await github.rest.pulls.list({
185
+ owner, repo, state: 'open',
186
+ sort: 'updated',
187
+ direction: 'asc',
188
+ per_page: 10,
189
+ });
190
+
191
+ const prCutoff = new Date(Date.now() - stalePrDays * 24 * 60 * 60 * 1000);
192
+ for (const pr of openPRs) {
193
+ const hasAutomerge = pr.labels.some(l => l.name === 'automerge');
194
+ if (!hasAutomerge) continue;
195
+
196
+ const isStale = new Date(pr.updated_at) < prCutoff;
197
+ if (!isStale) continue;
198
+
199
+ // Fetch full PR to get mergeable_state
200
+ const { data: fullPr } = await github.rest.pulls.get({
201
+ owner, repo, pull_number: pr.number,
202
+ });
203
+
204
+ if (fullPr.mergeable_state === 'dirty' || fullPr.mergeable === false) {
205
+ core.info(`PR #${pr.number} has conflicts and is stale (${stalePrDays}+ days). Removing automerge label.`);
206
+ try {
207
+ await github.rest.issues.removeLabel({
208
+ owner, repo, issue_number: pr.number, name: 'automerge',
209
+ });
210
+ } catch (e) { /* label may not exist */ }
211
+ await github.rest.issues.createComment({
212
+ owner, repo, issue_number: pr.number,
213
+ body: `Supervisor: this PR has had conflicts for ${stalePrDays}+ days. Removing automerge label — resolve conflicts and re-add to retry.`,
214
+ });
215
+ }
216
+ }
217
+ } catch (err) {
218
+ core.warning(`Stale PR check failed: ${err.message}`);
85
219
  }
@@ -356,18 +356,18 @@ jobs:
356
356
  message = `Branch '${pullRequest.head.ref}' deleted.`;
357
357
  prMerged = 'true';
358
358
  } else if (pullRequest.mergeable_state === 'dirty' || pullRequest.mergeable === false) {
359
- message = `PR #${pullNumber} has conflicts. Closing.`;
359
+ message = `PR #${pullNumber} has conflicts. Removing automerge label.`;
360
+ try {
361
+ await github.rest.issues.removeLabel({
362
+ owner, repo, issue_number: pullNumber, name: 'automerge',
363
+ });
364
+ } catch (e) {
365
+ core.info(`Could not remove automerge label: ${e.message}`);
366
+ }
360
367
  await github.rest.issues.createComment({
361
368
  owner, repo, issue_number: pullNumber,
362
- body: `This pull request is being closed due to conflicts (mergeable_state: ${pullRequest.mergeable_state}, mergeable: ${pullRequest.mergeable}).`,
369
+ body: `Automerge label removed this PR has conflicts (mergeable_state: ${pullRequest.mergeable_state}). Resolve conflicts and re-add the label to retry.`,
363
370
  });
364
- await github.rest.pulls.update({ owner, repo, pull_number: pullNumber, state: "closed" });
365
- try {
366
- await github.rest.git.deleteRef({ owner, repo, ref: `heads/${pullRequest.head.ref}` });
367
- message += ` Branch '${pullRequest.head.ref}' deleted.`;
368
- } catch (error) {
369
- message += ` Failed to delete branch: ${error.message}`;
370
- }
371
371
  prMerged = 'false';
372
372
  } else if (pullRequest.mergeable_state === 'unstable') {
373
373
  message = `PR #${pullNumber} checks still running (mergeable_state: unstable). Will retry when test workflow completes.`;