@xn-intenton-z2a/agentic-lib 7.1.59 → 7.1.61

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.
@@ -11,9 +11,9 @@
11
11
 
12
12
  name: agentic-lib-bot
13
13
  run-name: "agentic-lib-bot [${{ github.ref_name }}]"
14
- #concurrency:
15
- # group: agentic-lib-bot-${{ github.event.discussion.node_id || github.run_id }}
16
- # cancel-in-progress: false
14
+ #@dist concurrency:
15
+ #@dist group: agentic-lib-bot-${{ github.event.discussion.node_id || github.run_id }}
16
+ #@dist cancel-in-progress: false
17
17
 
18
18
  on:
19
19
  #@dist discussion:
@@ -86,6 +86,9 @@ jobs:
86
86
  respond:
87
87
  needs: params
88
88
  runs-on: ubuntu-latest
89
+ outputs:
90
+ action: ${{ steps.respond.outputs.action }}
91
+ action-arg: ${{ steps.respond.outputs.action-arg }}
89
92
  steps:
90
93
  - uses: actions/checkout@v4
91
94
  with:
@@ -114,8 +117,19 @@ jobs:
114
117
  }
115
118
  core.setOutput('url', url);
116
119
 
120
+ - name: Skip if comment is from bot
121
+ id: guard
122
+ shell: bash
123
+ run: |
124
+ COMMENT_USER='${{ github.event.comment.user.login }}'
125
+ if [ "$COMMENT_USER" = "github-actions[bot]" ] || [ "$COMMENT_USER" = "github-actions" ]; then
126
+ echo "Bot comment detected — skipping to avoid self-reply loop"
127
+ echo "skip=true" >> $GITHUB_OUTPUT
128
+ fi
129
+
117
130
  - name: Respond to discussion
118
- if: steps.discussion-url.outputs.url != ''
131
+ id: respond
132
+ if: steps.discussion-url.outputs.url != '' && steps.guard.outputs.skip != 'true'
119
133
  uses: ./.github/agentic-lib/actions/agentic-step
120
134
  env:
121
135
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -126,3 +140,18 @@ jobs:
126
140
  instructions: ".github/agentic-lib/agents/agent-discussion-bot.md"
127
141
  discussion-url: ${{ steps.discussion-url.outputs.url }}
128
142
  model: ${{ needs.params.outputs.model }}
143
+
144
+ dispatch-supervisor:
145
+ needs: [params, respond]
146
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.respond.outputs.action == 'request-supervisor'
147
+ runs-on: ubuntu-latest
148
+ steps:
149
+ - name: Dispatch supervisor workflow
150
+ env:
151
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
152
+ run: |
153
+ gh workflow run agentic-lib-workflow.yml \
154
+ --repo "${{ github.repository }}" \
155
+ -f mode=full \
156
+ -f model="${{ needs.params.outputs.model }}" \
157
+ -f message="${{ needs.respond.outputs.action-arg }}"
@@ -154,7 +154,7 @@ jobs:
154
154
  node-version: "24"
155
155
 
156
156
  - name: Close hanging init PRs
157
- if: env.INIT_MODE == 'purge' && needs.params.outputs.dry-run != 'true'
157
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && env.INIT_MODE == 'purge' && needs.params.outputs.dry-run != 'true'
158
158
  env:
159
159
  GH_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
160
160
  run: |
@@ -203,7 +203,7 @@ jobs:
203
203
  - run: npm test
204
204
 
205
205
  - name: Update schedule (if requested)
206
- if: needs.params.outputs.schedule != '' && needs.params.outputs.dry-run != 'true'
206
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.params.outputs.schedule != '' && needs.params.outputs.dry-run != 'true'
207
207
  uses: actions/github-script@v7
208
208
  with:
209
209
  script: |
@@ -255,7 +255,7 @@ jobs:
255
255
  }
256
256
 
257
257
  - name: Commit and push to main [skip ci]
258
- if: needs.params.outputs.dry-run != 'true'
258
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.params.outputs.dry-run != 'true'
259
259
  env:
260
260
  GH_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
261
261
  run: |
@@ -146,7 +146,7 @@ jobs:
146
146
  }
147
147
 
148
148
  - name: Commit and push
149
- if: inputs.dry-run != 'true' && inputs.dry-run != true
149
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && inputs.dry-run != 'true' && inputs.dry-run != true
150
150
  env:
151
151
  GH_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
152
152
  run: |
@@ -125,7 +125,14 @@ jobs:
125
125
  PR='${{ inputs.pr-number }}'
126
126
  echo "pr-number=${PR}" >> $GITHUB_OUTPUT
127
127
  DRY_RUN='${{ inputs.dry-run }}'
128
- echo "dry-run=${DRY_RUN:-true}" >> $GITHUB_OUTPUT
128
+ # Schedule triggers have no inputs, so DRY_RUN is empty.
129
+ # Default to false for schedule events (they should run live).
130
+ if [ "${{ github.event_name }}" = "schedule" ]; then
131
+ DRY_RUN="${DRY_RUN:-false}"
132
+ else
133
+ DRY_RUN="${DRY_RUN:-true}"
134
+ fi
135
+ echo "dry-run=${DRY_RUN}" >> $GITHUB_OUTPUT
129
136
  CONFIG='${{ inputs.config-path }}'
130
137
  echo "config-path=${CONFIG:-${{ env.configPath }}}" >> $GITHUB_OUTPUT
131
138
  outputs:
@@ -142,11 +149,11 @@ jobs:
142
149
  pr-cleanup:
143
150
  needs: params
144
151
  if: |
145
- needs.params.outputs.dry-run != 'true' &&
146
- (needs.params.outputs.mode == 'full' || needs.params.outputs.mode == 'pr-cleanup-only')
152
+ needs.params.outputs.mode == 'full' || needs.params.outputs.mode == 'pr-cleanup-only'
147
153
  runs-on: ubuntu-latest
148
154
  steps:
149
155
  - name: PR cleanup
156
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.params.outputs.dry-run != 'true'
150
157
  uses: actions/github-script@v7
151
158
  with:
152
159
  script: |
@@ -214,8 +221,7 @@ jobs:
214
221
  telemetry:
215
222
  needs: params
216
223
  if: |
217
- needs.params.outputs.dry-run != 'true' &&
218
- (needs.params.outputs.mode == 'full' || needs.params.outputs.mode == 'dev-only' || needs.params.outputs.mode == 'review-only')
224
+ needs.params.outputs.mode == 'full' || needs.params.outputs.mode == 'dev-only' || needs.params.outputs.mode == 'review-only'
219
225
  runs-on: ubuntu-latest
220
226
  steps:
221
227
  - uses: actions/checkout@v4
@@ -292,7 +298,6 @@ jobs:
292
298
  needs: [params, pr-cleanup, telemetry]
293
299
  if: |
294
300
  always() &&
295
- needs.params.outputs.dry-run != 'true' &&
296
301
  (needs.params.outputs.mode == 'full' || needs.params.outputs.mode == 'dev-only') &&
297
302
  needs.params.result == 'success'
298
303
  runs-on: ubuntu-latest
@@ -312,6 +317,7 @@ jobs:
312
317
  run: npm ci
313
318
 
314
319
  - name: Run supervisor
320
+ if: github.repository != 'xn-intenton-z2a/agentic-lib'
315
321
  uses: ./.github/agentic-lib/actions/agentic-step
316
322
  env:
317
323
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -382,7 +388,7 @@ jobs:
382
388
  model: ${{ needs.params.outputs.model }}
383
389
 
384
390
  - name: Commit and push changes
385
- if: needs.params.outputs.dry-run != 'true'
391
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.params.outputs.dry-run != 'true'
386
392
  uses: ./.github/agentic-lib/actions/commit-if-changed
387
393
  with:
388
394
  commit-message: "agentic-step: maintain features and library"
@@ -393,7 +399,6 @@ jobs:
393
399
  needs: [params, supervisor]
394
400
  if: |
395
401
  always() &&
396
- needs.params.outputs.dry-run != 'true' &&
397
402
  needs.params.outputs.mode == 'full' &&
398
403
  needs.params.result == 'success'
399
404
  runs-on: ubuntu-latest
@@ -533,7 +538,7 @@ jobs:
533
538
  model: ${{ needs.params.outputs.model }}
534
539
 
535
540
  - name: Commit and push fixes
536
- if: env.FIX_PR_NUMBER != ''
541
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && env.FIX_PR_NUMBER != ''
537
542
  uses: ./.github/agentic-lib/actions/commit-if-changed
538
543
  with:
539
544
  commit-message: "agentic-step: fix failing tests / resolve conflicts"
@@ -674,14 +679,14 @@ jobs:
674
679
  writable-paths: ${{ steps.config.outputs.writablePaths }}
675
680
 
676
681
  - name: Commit and push
677
- if: steps.issue.outputs.issue-number != '' && needs.params.outputs.dry-run != 'true'
682
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && steps.issue.outputs.issue-number != '' && needs.params.outputs.dry-run != 'true'
678
683
  uses: ./.github/agentic-lib/actions/commit-if-changed
679
684
  with:
680
685
  commit-message: "agentic-step: transform issue #${{ steps.issue.outputs.issue-number }}"
681
686
  push-ref: ${{ steps.branch.outputs.branchName }}
682
687
 
683
688
  - name: Create PR and attempt merge
684
- if: steps.issue.outputs.issue-number != '' && needs.params.outputs.dry-run != 'true'
689
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && steps.issue.outputs.issue-number != '' && needs.params.outputs.dry-run != 'true'
685
690
  uses: actions/github-script@v7
686
691
  with:
687
692
  script: |
package/agentic-lib.toml CHANGED
@@ -38,9 +38,17 @@ library-limit = 32
38
38
  # Profile sets defaults for all tuning knobs: min | recommended | max
39
39
  # min = fast & cheap (CI testing), recommended = balanced, max = thorough
40
40
  profile = "min" #@dist "recommended"
41
- model = "gpt-5-mini" # gpt-5-mini | claude-sonnet-4 | gpt-4.1
41
+ #
42
+ # Model selection — each has different strengths:
43
+ # gpt-5-mini — Fast, cheap, supports reasoning-effort. Best for CI and iteration.
44
+ # claude-sonnet-4 — Strong code quality, nuanced reasoning. Best for complex transforms.
45
+ # gpt-4.1 — High capability, large context. Best for thorough analysis.
46
+ # Note: reasoning-effort only works with gpt-5-mini and o4-mini.
47
+ # For other models it is automatically skipped.
48
+ model = "gpt-5-mini"
49
+ #
42
50
  # Override individual knobs below. Omit or comment out to use profile defaults.
43
- # reasoning-effort = "low" # low | medium | high | xhigh
51
+ # reasoning-effort = "low" # low | medium | high | none (none = disable entirely)
44
52
  infinite-sessions = false # set to true for long sessions with compaction
45
53
  # features-scan = 3 # feature files to include in prompts
46
54
  # source-scan = 3 # source files to include
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xn-intenton-z2a/agentic-lib",
3
- "version": "7.1.59",
3
+ "version": "7.1.61",
4
4
  "description": "Agentic-lib Agentic Coding Systems SDK powering automated GitHub workflows.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -29,19 +29,17 @@
29
29
  "license": "GPL-3.0, MIT",
30
30
  "devDependencies": {
31
31
  "@microsoft/eslint-formatter-sarif": "^3.1.0",
32
- "eslint": "^9.25.0",
32
+ "eslint": "^9.39.3",
33
33
  "eslint-config-google": "^0.14.0",
34
34
  "eslint-config-prettier": "^10.1.8",
35
- "eslint-plugin-import": "^2.31.0",
36
- "eslint-plugin-prettier": "^5.4.0",
35
+ "eslint-plugin-import": "^2.32.0",
36
+ "eslint-plugin-prettier": "^5.5.5",
37
37
  "eslint-plugin-promise": "^7.2.1",
38
- "eslint-plugin-react": "^7.37.5",
39
38
  "eslint-plugin-security": "^4.0.0",
40
- "eslint-plugin-sonarjs": "^4.0.0",
41
- "js-yaml": "^4.1.0",
42
- "markdown-it": "^14.1.0",
39
+ "eslint-plugin-sonarjs": "^4.0.1",
40
+ "js-yaml": "^4.1.1",
41
+ "markdown-it": "^14.1.1",
43
42
  "markdown-it-github": "^0.5.0",
44
- "npm-check-updates": "^19.6.3",
45
43
  "prettier": "^3.8.1",
46
44
  "vitest": "^4.0.18"
47
45
  },
@@ -85,7 +83,8 @@
85
83
  "src/mcp/"
86
84
  ],
87
85
  "overrides": {
88
- "minimatch": ">=10.2.3"
86
+ "minimatch": ">=10.2.3",
87
+ "@github/copilot": ">=0.0.423"
89
88
  },
90
89
  "repository": {
91
90
  "type": "git",
@@ -99,8 +98,8 @@
99
98
  "dependencies": {
100
99
  "@actions/core": "^3.0.0",
101
100
  "@actions/github": "^9.0.0",
102
- "@github/copilot-sdk": "^0.1.29",
103
- "@modelcontextprotocol/sdk": "^1.27.0",
101
+ "@github/copilot-sdk": "^0.1.31-unstable.0",
102
+ "@modelcontextprotocol/sdk": "^1.27.1",
104
103
  "smol-toml": "^1.6.0"
105
104
  }
106
105
  }
@@ -52,6 +52,10 @@ outputs:
52
52
  description: "Total tokens consumed by the Copilot SDK"
53
53
  model:
54
54
  description: "Model used for the completion"
55
+ action:
56
+ description: "Action chosen by the task (e.g. request-supervisor, create-feature, nop)"
57
+ action-arg:
58
+ description: "Argument for the chosen action (free text)"
55
59
 
56
60
  runs:
57
61
  using: "node24"
@@ -116,9 +116,12 @@ function readPackageJson(tomlPath, depsRelPath) {
116
116
  function resolveTuning(tuningSection) {
117
117
  const profileName = tuningSection.profile || "recommended";
118
118
  const profile = TUNING_PROFILES[profileName] || TUNING_PROFILES.recommended;
119
- const tuning = { ...profile };
119
+ const tuning = { ...profile, profileName };
120
120
 
121
- if (tuningSection["reasoning-effort"]) tuning.reasoningEffort = tuningSection["reasoning-effort"];
121
+ // "none" explicitly disables reasoning-effort regardless of profile
122
+ if (tuningSection["reasoning-effort"]) {
123
+ tuning.reasoningEffort = tuningSection["reasoning-effort"] === "none" ? "" : tuningSection["reasoning-effort"];
124
+ }
122
125
  if (tuningSection["infinite-sessions"] === true || tuningSection["infinite-sessions"] === false) {
123
126
  tuning.infiniteSessions = tuningSection["infinite-sessions"];
124
127
  }
@@ -10,6 +10,11 @@ import { join } from "path";
10
10
  import { createAgentTools } from "./tools.js";
11
11
  import * as core from "@actions/core";
12
12
 
13
+ // Models known to support the reasoningEffort SessionConfig parameter.
14
+ // Updated from Copilot SDK ModelInfo.supportedReasoningEfforts (v0.1.30).
15
+ // When in doubt, omit reasoning-effort — the SDK uses its default.
16
+ const MODELS_SUPPORTING_REASONING_EFFORT = new Set(["gpt-5-mini", "o4-mini"]);
17
+
13
18
  /**
14
19
  * Build the CopilotClient options for authentication.
15
20
  *
@@ -38,6 +43,34 @@ export function buildClientOptions(githubToken) {
38
43
  return { env };
39
44
  }
40
45
 
46
+ /**
47
+ * Log tuning parameter application with profile context.
48
+ *
49
+ * @param {string} param - Parameter name
50
+ * @param {*} value - Resolved value being applied
51
+ * @param {string} profileName - Profile the default came from
52
+ * @param {string} model - Model being used
53
+ * @param {Object} [clip] - Optional clipping info { available, requested }
54
+ */
55
+ export function logTuningParam(param, value, profileName, model, clip) {
56
+ const clipInfo = clip
57
+ ? ` (requested=${clip.requested}, available=${clip.available}, excess=${clip.requested - clip.available})`
58
+ : "";
59
+ core.info(`[tuning] ${param}=${value} profile=${profileName} model=${model}${clipInfo}`);
60
+ }
61
+
62
+ /**
63
+ * Check if a model supports reasoningEffort.
64
+ * Uses the static allowlist; at runtime the SDK's models.list() could be used
65
+ * but that requires an authenticated client which isn't available at config time.
66
+ *
67
+ * @param {string} model - Model name
68
+ * @returns {boolean}
69
+ */
70
+ export function supportsReasoningEffort(model) {
71
+ return MODELS_SUPPORTING_REASONING_EFFORT.has(model);
72
+ }
73
+
41
74
  /**
42
75
  * Run a Copilot SDK session and return the response.
43
76
  * Handles the full lifecycle: create client → create session → send → stop.
@@ -49,11 +82,21 @@ export function buildClientOptions(githubToken) {
49
82
  * @param {string[]} options.writablePaths - Paths the agent may modify
50
83
  * @param {string} [options.githubToken] - Optional token; falls back to COPILOT_GITHUB_TOKEN env var.
51
84
  * @param {Object} [options.tuning] - Tuning config (reasoningEffort, infiniteSessions)
85
+ * @param {string} [options.profileName] - Profile name for logging
52
86
  * @returns {Promise<{content: string, tokensUsed: number}>}
53
87
  */
54
- export async function runCopilotTask({ model, systemMessage, prompt, writablePaths, githubToken, tuning }) {
88
+ export async function runCopilotTask({
89
+ model,
90
+ systemMessage,
91
+ prompt,
92
+ writablePaths,
93
+ githubToken,
94
+ tuning,
95
+ profileName,
96
+ }) {
97
+ const profile = profileName || "unknown";
55
98
  core.info(
56
- `[copilot] Creating client (model=${model}, promptLen=${prompt.length}, writablePaths=${writablePaths.length}, tuning=${tuning?.reasoningEffort || "default"})`,
99
+ `[copilot] Creating client (model=${model}, promptLen=${prompt.length}, writablePaths=${writablePaths.length}, tuning=${tuning?.reasoningEffort || "default"}, profile=${profile})`,
57
100
  );
58
101
 
59
102
  const clientOptions = buildClientOptions(githubToken);
@@ -68,12 +111,32 @@ export async function runCopilotTask({ model, systemMessage, prompt, writablePat
68
111
  onPermissionRequest: approveAll,
69
112
  workingDirectory: process.cwd(),
70
113
  };
71
- if (tuning?.reasoningEffort) {
72
- sessionConfig.reasoningEffort = tuning.reasoningEffort;
114
+
115
+ // Only set reasoningEffort for models that support it
116
+ if (tuning?.reasoningEffort && tuning.reasoningEffort !== "none") {
117
+ if (supportsReasoningEffort(model)) {
118
+ sessionConfig.reasoningEffort = tuning.reasoningEffort;
119
+ logTuningParam("reasoningEffort", tuning.reasoningEffort, profile, model);
120
+ } else {
121
+ core.info(
122
+ `[copilot] Skipping reasoningEffort="${tuning.reasoningEffort}" — not supported by model "${model}". Only supported by: ${[...MODELS_SUPPORTING_REASONING_EFFORT].join(", ")}`,
123
+ );
124
+ }
73
125
  }
126
+
74
127
  if (tuning?.infiniteSessions === true) {
75
128
  sessionConfig.infiniteSessions = {};
129
+ logTuningParam("infiniteSessions", true, profile, model);
76
130
  }
131
+
132
+ // Log scan/context tuning params
133
+ if (tuning?.featuresScan) logTuningParam("featuresScan", tuning.featuresScan, profile, model);
134
+ if (tuning?.sourceScan) logTuningParam("sourceScan", tuning.sourceScan, profile, model);
135
+ if (tuning?.sourceContent) logTuningParam("sourceContent", tuning.sourceContent, profile, model);
136
+ if (tuning?.issuesScan) logTuningParam("issuesScan", tuning.issuesScan, profile, model);
137
+ if (tuning?.documentSummary) logTuningParam("documentSummary", tuning.documentSummary, profile, model);
138
+ if (tuning?.discussionComments) logTuningParam("discussionComments", tuning.discussionComments, profile, model);
139
+
77
140
  const session = await client.createSession(sessionConfig);
78
141
  core.info(`[copilot] Session created: ${session.sessionId}`);
79
142
 
@@ -105,7 +168,9 @@ export async function runCopilotTask({ model, systemMessage, prompt, writablePat
105
168
  totalInputTokens += input;
106
169
  totalOutputTokens += output;
107
170
  totalCost += cost;
108
- core.info(`[copilot] event=${eventType}: model=${d.model} input=${input} output=${output} cacheRead=${cacheRead} cost=${cost}`);
171
+ core.info(
172
+ `[copilot] event=${eventType}: model=${d.model} input=${input} output=${output} cacheRead=${cacheRead} cost=${cost}`,
173
+ );
109
174
  } else if (eventType === "session.idle") {
110
175
  core.info(`[copilot] event=${eventType}`);
111
176
  } else if (eventType === "session.error") {
@@ -161,18 +226,31 @@ export function scanDirectory(dirPath, extensions, options = {}) {
161
226
 
162
227
  if (!existsSync(dirPath)) return [];
163
228
 
164
- return readdirSync(dirPath, recursive ? { recursive: true } : undefined)
165
- .filter((f) => exts.some((ext) => f.endsWith(ext)))
166
- .slice(0, fileLimit)
167
- .map((f) => {
168
- try {
169
- const content = readFileSync(join(dirPath, f), "utf8");
170
- return { name: f, content: contentLimit ? content.substring(0, contentLimit) : content };
171
- } catch (err) {
172
- core.debug(`[scanDirectory] ${join(dirPath, f)}: ${err.message}`);
173
- return { name: f, content: "" };
229
+ const allFiles = readdirSync(dirPath, recursive ? { recursive: true } : undefined).filter((f) =>
230
+ exts.some((ext) => f.endsWith(ext)),
231
+ );
232
+ const clipped = allFiles.slice(0, fileLimit);
233
+ if (allFiles.length > fileLimit) {
234
+ core.info(
235
+ `[scanDirectory] Clipped ${dirPath}: ${allFiles.length} files found, returning ${fileLimit} (excess=${allFiles.length - fileLimit})`,
236
+ );
237
+ }
238
+
239
+ return clipped.map((f) => {
240
+ try {
241
+ const raw = readFileSync(join(dirPath, f), "utf8");
242
+ const content = contentLimit ? raw.substring(0, contentLimit) : raw;
243
+ if (contentLimit && raw.length > contentLimit) {
244
+ core.info(
245
+ `[scanDirectory] Clipped ${f}: ${raw.length} chars, returning ${contentLimit} (excess=${raw.length - contentLimit})`,
246
+ );
174
247
  }
175
- });
248
+ return { name: f, content };
249
+ } catch (err) {
250
+ core.debug(`[scanDirectory] ${join(dirPath, f)}: ${err.message}`);
251
+ return { name: f, content: "" };
252
+ }
253
+ });
176
254
  }
177
255
 
178
256
  /**
@@ -95,6 +95,8 @@ async function run() {
95
95
  if (result.prNumber) core.setOutput("pr-number", String(result.prNumber));
96
96
  if (result.tokensUsed) core.setOutput("tokens-used", String(result.tokensUsed));
97
97
  if (result.model) core.setOutput("model", result.model);
98
+ if (result.action) core.setOutput("action", result.action);
99
+ if (result.actionArg) core.setOutput("action-arg", result.actionArg);
98
100
 
99
101
  // Log to intentïon.md (commit-if-changed excludes this on non-default branches)
100
102
  const intentionFilepath = config.intentionBot?.intentionFilepath;
@@ -13,6 +13,10 @@ async function gatherContext(octokit, repo, config, t) {
13
13
  const mission = readOptionalFile(config.paths.mission.path);
14
14
  const recentActivity = readOptionalFile(config.intentionBot.intentionFilepath).split("\n").slice(-20).join("\n");
15
15
 
16
+ // Extract discussion URL from recent activity for supervisor reporting
17
+ const discussionUrlMatch = recentActivity.match(/https:\/\/github\.com\/[^/]+\/[^/]+\/discussions\/\d+/);
18
+ const activeDiscussionUrl = discussionUrlMatch ? discussionUrlMatch[0] : "";
19
+
16
20
  const featuresPath = config.paths.features.path;
17
21
  const featureNames = existsSync(featuresPath)
18
22
  ? scanDirectory(featuresPath, ".md").map((f) => f.name.replace(".md", ""))
@@ -80,6 +84,7 @@ async function gatherContext(octokit, repo, config, t) {
80
84
  packageJson: config.packageJson,
81
85
  featureIssuesWipLimit: config.featureDevelopmentIssuesWipLimit,
82
86
  maintenanceIssuesWipLimit: config.maintenanceIssuesWipLimit,
87
+ activeDiscussionUrl,
83
88
  };
84
89
  }
85
90
 
@@ -118,6 +123,7 @@ function buildPrompt(ctx, agentInstructions) {
118
123
  "```",
119
124
  "",
120
125
  ...(ctx.packageJson ? ["### Dependencies (package.json)", "```json", ctx.packageJson, "```", ""] : []),
126
+ ...(ctx.activeDiscussionUrl ? [`### Active Discussion`, `${ctx.activeDiscussionUrl}`, ""] : []),
121
127
  ...(ctx.oldestReadyIssue
122
128
  ? [`### Oldest Ready Issue`, `#${ctx.oldestReadyIssue.number}: ${ctx.oldestReadyIssue.title}`, ""]
123
129
  : []),
@@ -109,6 +109,13 @@ export async function transform(context) {
109
109
  "Determine the single most impactful next step to transform this repository.",
110
110
  "Then implement that step.",
111
111
  "",
112
+ "## When NOT to make changes",
113
+ "If the existing code already satisfies all requirements in MISSION.md and all open issues have been addressed:",
114
+ "- Do NOT make cosmetic changes (reformatting, renaming, reordering)",
115
+ "- Do NOT add features beyond what MISSION.md specifies",
116
+ "- Do NOT rewrite working code for stylistic preferences",
117
+ "- Instead, report that the mission is satisfied and make no file changes",
118
+ "",
112
119
  formatPathsSection(writablePaths, readOnlyPaths, config),
113
120
  "",
114
121
  "## Constraints",
@@ -8,12 +8,32 @@ You are the voice of this GitHub repository. You exist as this repository — re
8
8
  - **Be engaging.** Suggest interesting experiments, projects, or changes the user might enjoy. Encourage them to try things and ask questions.
9
9
  - **Don't dump capabilities.** Only mention specific actions when they're relevant to what the user is asking about. Don't list all your actions in every response.
10
10
 
11
+ ## Scope — Stay On Topic
12
+
13
+ You are this repository. Only discuss topics related to:
14
+ - The current mission (MISSION.md) and its progress
15
+ - The code, tests, features, and issues in this repository
16
+ - How to use, clone, or contribute to this project
17
+ - Experiments and ideas that relate to the project's purpose
18
+ - The autonomous development pipeline and how it works
19
+
20
+ If someone asks about something completely unrelated to this repository (e.g. trivia, unrelated topics, general knowledge):
21
+ - Politely redirect: "That's outside my scope — I'm focused on this repository's mission. Want to hear about what I'm working on instead?"
22
+ - Use it as an opportunity to engage them with the project: suggest an experiment, ask what they'd like to build, or share recent progress.
23
+
11
24
  ## Mission Alignment
12
25
 
13
26
  Your mission comes from MISSION.md. Everything you do should serve that mission.
14
27
  - If a user requests something that contradicts the mission, push back politely and suggest an aligned alternative.
15
28
  - Be proactive about suggesting features that advance the mission without needing to be asked.
16
29
 
30
+ ## Encouraging Engagement
31
+
32
+ - When users visit, invite them to participate: "Want to try changing the mission and see what happens?"
33
+ - Share concrete progress: "I just completed X — here's what changed."
34
+ - Suggest experiments: "You could fork this and set MISSION.md to build a [specific idea]. Want to try?"
35
+ - Ask what they think about the current direction.
36
+
17
37
  ## Supervisor Integration
18
38
 
19
39
  You work with a supervisor system that orchestrates the repository's workflows. When a user requests an action:
@@ -67,6 +67,23 @@ When the transform agent has implemented all major features but minor polish rem
67
67
 
68
68
  - The `set-schedule` action requires a `WORKFLOW_TOKEN` secret (classic PAT with `workflow` scope) to push workflow file changes to main.
69
69
 
70
+ ## Stability Detection
71
+
72
+ Check the Recent Activity log for patterns that indicate the mission is done:
73
+ - If the last 2+ workflow runs produced no transform commits (only maintain-only or nop outcomes), AND all open issues are closed, the mission is likely accomplished. Follow the "Mission Accomplished" protocol above.
74
+ - Look for `transform: nop` or `transform: transformed` patterns in the activity log to distinguish productive iterations from idle ones.
75
+
76
+ ## Discussions Awareness
77
+
78
+ Check the Recent Activity log for discussion bot referrals (lines containing `discussion-request-supervisor`). These indicate a user asked the bot something that requires supervisor action. **Prioritise responding to these referrals.**
79
+
80
+ Also check for notable progress worth reporting:
81
+ - Mission milestones achieved (all core functions implemented, all tests passing)
82
+ - Schedule changes (mission accomplished, throttling down)
83
+ - Significant code changes (large PRs merged, new features completed)
84
+
85
+ When notable progress exists or there are unresponded referrals, use `respond:discussions | message: <status update> | discussion-url: <url>` to post an update. Keep it concise — 2-3 sentences summarising what happened and what's next.
86
+
70
87
  ## Guidelines
71
88
 
72
89
  - Pick multiple actions when appropriate — concurrent work is encouraged.
@@ -5,4 +5,4 @@ Reference material and documentation sources for this project.
5
5
  Add URLs, papers, API docs, or other reference material here.
6
6
  The maintain-library workflow will process these into `library/` documents.
7
7
 
8
- - https://en.wikipedia.org/wiki/Fizz_buzz
8
+ - https://github.com/xn-intenton-z2a/repository0
@@ -14,7 +14,7 @@
14
14
  "author": "",
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
- "@xn-intenton-z2a/agentic-lib": "^7.1.59"
17
+ "@xn-intenton-z2a/agentic-lib": "^7.1.61"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@vitest/coverage-v8": "^4.0.18",