@xn-intenton-z2a/agentic-lib 7.1.22 → 7.1.24

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.
@@ -738,43 +738,38 @@ function initConfig(seedsDir) {
738
738
  }
739
739
  }
740
740
 
741
- function initReseed() {
742
- console.log("\n--- Reseed: Clear Features + Activity Log ---");
743
- const intentionFile = resolve(target, "intentïon.md");
744
- if (existsSync(intentionFile)) {
745
- if (!dryRun) rmSync(intentionFile);
746
- console.log(" REMOVE: intentïon.md");
741
+ function removeFile(filePath, label) {
742
+ if (existsSync(filePath)) {
743
+ if (!dryRun) rmSync(filePath);
744
+ console.log(` REMOVE: ${label}`);
747
745
  initChanges++;
748
746
  }
749
- // Clear features directory (now at project root, next to library/)
750
- const featuresDir = resolve(target, "features");
751
- if (existsSync(featuresDir)) {
752
- for (const f of readdirSync(featuresDir)) {
753
- if (!dryRun) rmSync(resolve(featuresDir, f));
754
- console.log(` REMOVE: features/${f}`);
755
- initChanges++;
756
- }
747
+ }
748
+
749
+ function clearDirContents(dirPath, label) {
750
+ if (!existsSync(dirPath)) return;
751
+ for (const f of readdirSync(dirPath)) {
752
+ if (!dryRun) rmSync(resolve(dirPath, f));
753
+ console.log(` REMOVE: ${label}/${f}`);
754
+ initChanges++;
757
755
  }
758
- // Also clear old features location if it exists
756
+ }
757
+
758
+ function initReseed() {
759
+ console.log("\n--- Reseed: Clear Features + Activity Log ---");
760
+ removeFile(resolve(target, "intentïon.md"), "intentïon.md");
761
+ clearDirContents(resolve(target, "features"), "features");
762
+
763
+ // Clear old features location if it exists
759
764
  const oldFeaturesDir = resolve(target, ".github/agentic-lib/features");
765
+ clearDirContents(oldFeaturesDir, ".github/agentic-lib/features (old location)");
760
766
  if (existsSync(oldFeaturesDir)) {
761
- for (const f of readdirSync(oldFeaturesDir)) {
762
- if (!dryRun) rmSync(resolve(oldFeaturesDir, f));
763
- console.log(` REMOVE: .github/agentic-lib/features/${f} (old location)`);
764
- initChanges++;
765
- }
766
767
  if (!dryRun) rmdirSync(oldFeaturesDir);
767
768
  console.log(" REMOVE: .github/agentic-lib/features/ (old location)");
768
769
  }
769
- // Clear library directory (generated from SOURCES.md)
770
- const libraryDir = resolve(target, "library");
771
- if (existsSync(libraryDir)) {
772
- for (const f of readdirSync(libraryDir)) {
773
- if (!dryRun) rmSync(resolve(libraryDir, f));
774
- console.log(` REMOVE: library/${f}`);
775
- initChanges++;
776
- }
777
- }
770
+
771
+ clearDirContents(resolve(target, "library"), "library");
772
+
778
773
  // Remove old getting-started-guide if it exists
779
774
  const oldGuideDir = resolve(target, ".github/agentic-lib/getting-started-guide");
780
775
  if (existsSync(oldGuideDir)) {
@@ -784,8 +779,42 @@ function initReseed() {
784
779
  }
785
780
  }
786
781
 
782
+ function readTomlPaths() {
783
+ let sourcePath = "src/lib/";
784
+ let testsPath = "tests/unit/";
785
+ const tomlTarget = resolve(target, "agentic-lib.toml");
786
+ if (existsSync(tomlTarget)) {
787
+ try {
788
+ const tomlContent = readFileSync(tomlTarget, "utf8");
789
+ const sourceMatch = tomlContent.match(/^source\s*=\s*"([^"]+)"/m);
790
+ const testsMatch = tomlContent.match(/^tests\s*=\s*"([^"]+)"/m);
791
+ if (sourceMatch) sourcePath = sourceMatch[1];
792
+ if (testsMatch) testsPath = testsMatch[1];
793
+ } catch (err) {
794
+ console.log(` WARN: Could not read TOML for paths, using defaults: ${err.message}`);
795
+ }
796
+ }
797
+ return { sourcePath, testsPath };
798
+ }
799
+
800
+ function clearAndRecreateDir(dirPath, label) {
801
+ const fullPath = resolve(target, dirPath);
802
+ if (existsSync(fullPath)) {
803
+ console.log(` CLEAR: ${label}`);
804
+ if (!dryRun) rmSync(fullPath, { recursive: true });
805
+ initChanges++;
806
+ }
807
+ if (!dryRun) mkdirSync(fullPath, { recursive: true });
808
+ }
809
+
787
810
  function initPurge(seedsDir) {
788
811
  console.log("\n--- Purge: Reset Source Files to Seed State ---");
812
+
813
+ const { sourcePath, testsPath } = readTomlPaths();
814
+ clearAndRecreateDir(sourcePath, sourcePath);
815
+ clearAndRecreateDir(testsPath, testsPath);
816
+
817
+ // Copy seed files
789
818
  const SEED_MAP = {
790
819
  "zero-main.js": "src/lib/main.js",
791
820
  "zero-main.test.js": "tests/unit/main.test.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xn-intenton-z2a/agentic-lib",
3
- "version": "7.1.22",
3
+ "version": "7.1.24",
4
4
  "description": "Agentic-lib Agentic Coding Systems SDK powering automated GitHub workflows.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -117,6 +117,7 @@ export function loadConfig(configPath) {
117
117
 
118
118
  return {
119
119
  schedule: toml.schedule?.tier || "schedule-1",
120
+ supervisor: toml.schedule?.supervisor || "daily",
120
121
  paths,
121
122
  buildScript: execution.build || "npm run build",
122
123
  testScript: execution.test || "npm test",
@@ -9,149 +9,170 @@ import * as core from "@actions/core";
9
9
  import { existsSync } from "fs";
10
10
  import { runCopilotTask, readOptionalFile, scanDirectory } from "../copilot.js";
11
11
 
12
- /**
13
- * Respond to a GitHub Discussion using the Copilot SDK.
14
- *
15
- * @param {Object} context - Task context from index.js
16
- * @returns {Promise<Object>} Result with outcome, action, tokensUsed, model
17
- */
18
- export async function discussions(context) {
19
- const { octokit, config, instructions, model, discussionUrl } = context;
12
+ const BOT_LOGINS = ["github-actions[bot]", "github-actions"];
20
13
 
21
- if (!discussionUrl) {
22
- throw new Error("discussions task requires discussion-url input");
14
+ async function fetchDiscussion(octokit, discussionUrl) {
15
+ const urlMatch = discussionUrl.match(/github\.com\/([^/]+)\/([^/]+)\/discussions\/(\d+)/);
16
+ if (!urlMatch) {
17
+ core.warning(`Could not parse discussion URL: ${discussionUrl}`);
18
+ return { title: "", body: "", comments: [], nodeId: "" };
23
19
  }
24
20
 
25
- // Parse discussion URL and fetch content via GraphQL
26
- const urlMatch = discussionUrl.match(/github\.com\/([^/]+)\/([^/]+)\/discussions\/(\d+)/);
27
- let discussionTitle = "";
28
- let discussionBody = "";
29
- let discussionComments = [];
30
- let discussionNodeId = "";
31
-
32
- if (urlMatch) {
33
- const [, urlOwner, urlRepo, discussionNumber] = urlMatch;
34
- try {
35
- const query = `query {
36
- repository(owner: "${urlOwner}", name: "${urlRepo}") {
37
- discussion(number: ${discussionNumber}) {
38
- id
39
- title
40
- body
41
- comments(last: 10) {
42
- nodes {
43
- body
44
- author { login }
45
- createdAt
46
- }
21
+ const [, urlOwner, urlRepo, discussionNumber] = urlMatch;
22
+ try {
23
+ const query = `query {
24
+ repository(owner: "${urlOwner}", name: "${urlRepo}") {
25
+ discussion(number: ${discussionNumber}) {
26
+ id
27
+ title
28
+ body
29
+ comments(last: 10) {
30
+ nodes {
31
+ body
32
+ author { login }
33
+ createdAt
47
34
  }
48
35
  }
49
36
  }
50
- }`;
51
- const result = await octokit.graphql(query);
52
- const discussion = result.repository.discussion;
53
- discussionNodeId = discussion.id || "";
54
- discussionTitle = discussion.title || "";
55
- discussionBody = discussion.body || "";
56
- discussionComments = discussion.comments.nodes || [];
57
- core.info(
58
- `Fetched discussion #${discussionNumber}: "${discussionTitle}" (${discussionComments.length} comments)`,
59
- );
60
- } catch (err) {
61
- core.warning(`Failed to fetch discussion content via GraphQL: ${err.message}. Falling back to URL-only.`);
62
- }
63
- } else {
64
- core.warning(`Could not parse discussion URL: ${discussionUrl}`);
37
+ }
38
+ }`;
39
+ const result = await octokit.graphql(query);
40
+ const discussion = result.repository.discussion;
41
+ core.info(
42
+ `Fetched discussion #${discussionNumber}: "${discussion.title}" (${discussion.comments.nodes.length} comments)`,
43
+ );
44
+ return {
45
+ title: discussion.title || "",
46
+ body: discussion.body || "",
47
+ comments: discussion.comments.nodes || [],
48
+ nodeId: discussion.id || "",
49
+ };
50
+ } catch (err) {
51
+ core.warning(`Failed to fetch discussion content via GraphQL: ${err.message}. Falling back to URL-only.`);
52
+ return { title: "", body: "", comments: [], nodeId: "" };
65
53
  }
54
+ }
55
+
56
+ function buildPrompt(discussionUrl, discussion, context) {
57
+ const { config, instructions } = context;
58
+ const { title, body, comments } = discussion;
59
+
60
+ const humanComments = comments.filter((c) => !BOT_LOGINS.includes(c.author?.login));
61
+ const botReplies = comments.filter((c) => BOT_LOGINS.includes(c.author?.login));
62
+ const latestHumanComment = humanComments.length > 0 ? humanComments[humanComments.length - 1] : null;
63
+ const lastBotReply = botReplies.length > 0 ? botReplies[botReplies.length - 1] : null;
66
64
 
67
65
  const mission = readOptionalFile(config.paths.mission.path);
68
66
  const contributing = readOptionalFile(config.paths.contributing.path, 1000);
69
-
70
67
  const featuresPath = config.paths.features.path;
71
- let featureNames = [];
72
- if (existsSync(featuresPath)) {
73
- featureNames = scanDirectory(featuresPath, ".md").map((f) => f.name.replace(".md", ""));
74
- }
75
-
76
- const intentionPath = config.intentionBot.intentionFilepath;
77
- const recentActivity = readOptionalFile(intentionPath).split("\n").slice(-20).join("\n");
78
-
68
+ const featureNames = existsSync(featuresPath)
69
+ ? scanDirectory(featuresPath, ".md").map((f) => f.name.replace(".md", ""))
70
+ : [];
71
+ const recentActivity = readOptionalFile(config.intentionBot.intentionFilepath).split("\n").slice(-20).join("\n");
79
72
  const agentInstructions = instructions || "Respond to the GitHub Discussion as the repository bot.";
80
73
 
81
- const prompt = [
74
+ const parts = [
82
75
  "## Instructions",
83
76
  agentInstructions,
84
77
  "",
85
- "## Discussion",
78
+ "## Discussion Thread",
86
79
  `URL: ${discussionUrl}`,
87
- discussionTitle ? `### ${discussionTitle}` : "",
88
- discussionBody || "(no body)",
89
- discussionComments.length > 0 ? `### Comments (${discussionComments.length})` : "",
90
- ...discussionComments.map((c) => `**${c.author?.login || "unknown"}** (${c.createdAt}):\n${c.body}`),
80
+ title ? `### ${title}` : "",
81
+ body || "(no body)",
82
+ ];
83
+
84
+ if (humanComments.length > 0) {
85
+ parts.push("", "### Conversation History");
86
+ for (const c of humanComments) {
87
+ const prefix = c === latestHumanComment ? ">>> **[LATEST — RESPOND TO THIS]** " : "";
88
+ parts.push(`${prefix}**${c.author?.login || "unknown"}** (${c.createdAt}):\n${c.body}`);
89
+ }
90
+ }
91
+
92
+ if (lastBotReply) {
93
+ parts.push("", "### Your Last Reply (DO NOT REPEAT THIS)", lastBotReply.body.substring(0, 500));
94
+ }
95
+
96
+ parts.push(
91
97
  "",
92
- "## Context",
98
+ "## Repository Context",
93
99
  `### Mission\n${mission}`,
94
100
  contributing ? `### Contributing\n${contributing}` : "",
95
101
  `### Current Features\n${featureNames.join(", ") || "none"}`,
96
102
  recentActivity ? `### Recent Activity\n${recentActivity}` : "",
97
103
  "",
98
- "## Available Actions",
99
- "Respond with one of these action tags in your response:",
100
- "- `[ACTION:seed-repository]`Reset the sandbox to initial state",
101
- "- `[ACTION:create-feature] <name>` — Create a new feature",
102
- "- `[ACTION:update-feature] <name>` — Update an existing feature",
103
- "- `[ACTION:delete-feature] <name>` — Delete a feature that is no longer needed",
104
- "- `[ACTION:create-issue] <title>` — Create a new issue",
105
- "- `[ACTION:nop]` — No action needed, just respond conversationally",
106
- "- `[ACTION:mission-complete]` — Declare the current mission complete",
107
- "- `[ACTION:stop]` — Halt automation",
108
- "",
109
- "Include exactly one action tag. The rest of your response is the discussion reply.",
110
- "",
111
- "## Mission Protection",
112
- "If the user requests something that contradicts or would undermine the mission,",
113
- "you MUST push back. Explain why the request conflicts with the mission and suggest",
114
- "an alternative that aligns with it. Use `[ACTION:nop]` in this case.",
115
- "The mission is the non-negotiable foundation of this repository.",
116
- ].join("\n");
104
+ "## Actions",
105
+ "Include exactly one action tag in your response. Only mention actions to the user when relevant.",
106
+ "`[ACTION:request-supervisor] <free text>` Ask the supervisor to evaluate and act on a user request",
107
+ "`[ACTION:create-feature] <name>` — Create a new feature",
108
+ "`[ACTION:update-feature] <name>` — Update an existing feature",
109
+ "`[ACTION:delete-feature] <name>` — Delete a feature",
110
+ "`[ACTION:create-issue] <title>` — Create a new issue",
111
+ "`[ACTION:seed-repository]` — Reset to initial state",
112
+ "`[ACTION:nop]` — No action needed, just respond conversationally",
113
+ "`[ACTION:mission-complete]` — Declare mission complete",
114
+ "`[ACTION:stop]` — Halt automation",
115
+ );
116
+
117
+ return parts.filter(Boolean).join("\n");
118
+ }
119
+
120
+ async function postReply(octokit, nodeId, replyBody) {
121
+ if (!nodeId) {
122
+ core.warning("Cannot post reply: discussion node ID not available");
123
+ return;
124
+ }
125
+ if (!replyBody) {
126
+ core.warning("Cannot post reply: no reply content generated");
127
+ return;
128
+ }
129
+ try {
130
+ const mutation = `mutation($discussionId: ID!, $body: String!) {
131
+ addDiscussionComment(input: { discussionId: $discussionId, body: $body }) {
132
+ comment { url }
133
+ }
134
+ }`;
135
+ const { addDiscussionComment } = await octokit.graphql(mutation, {
136
+ discussionId: nodeId,
137
+ body: replyBody,
138
+ });
139
+ core.info(`Posted reply to discussion: ${addDiscussionComment.comment.url}`);
140
+ } catch (err) {
141
+ core.warning(`Failed to post discussion reply: ${err.message}`);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Respond to a GitHub Discussion using the Copilot SDK.
147
+ *
148
+ * @param {Object} context - Task context from index.js
149
+ * @returns {Promise<Object>} Result with outcome, action, tokensUsed, model
150
+ */
151
+ export async function discussions(context) {
152
+ const { octokit, model, discussionUrl } = context;
153
+
154
+ if (!discussionUrl) {
155
+ throw new Error("discussions task requires discussion-url input");
156
+ }
157
+
158
+ const discussion = await fetchDiscussion(octokit, discussionUrl);
159
+ const prompt = buildPrompt(discussionUrl, discussion, context);
117
160
 
118
161
  const { content, tokensUsed } = await runCopilotTask({
119
162
  model,
120
163
  systemMessage:
121
- "You are a repository bot that responds to GitHub Discussions. You are self-aware you refer to yourself as the repository. Be helpful, adaptive, and proactive about suggesting features. You can update and delete features proactively when they are outdated or completed. You MUST protect the mission: if a user requests something that contradicts the mission, push back politely and suggest an aligned alternative.",
164
+ "You are this repository. Respond in first person. Be concise and engagingnever repeat what you said in your last reply. Adapt to the user's language level. Encourage experimentation and suggest interesting projects. When a user requests an action, pass it to the supervisor via [ACTION:request-supervisor]. Protect the mission: push back on requests that contradict it.",
122
165
  prompt,
123
166
  writablePaths: [],
124
167
  });
125
168
 
126
- // Parse action from response
127
169
  const actionMatch = content.match(/\[ACTION:(\S+?)\](.+)?/);
128
170
  const action = actionMatch ? actionMatch[1] : "nop";
129
171
  const actionArg = actionMatch && actionMatch[2] ? actionMatch[2].trim() : "";
130
172
  const replyBody = content.replace(/\[ACTION:\S+?\].+/, "").trim();
131
173
 
132
174
  core.info(`Discussion bot action: ${action}, arg: ${actionArg}`);
133
-
134
- // Post reply comment back to the Discussion
135
- if (discussionNodeId && replyBody) {
136
- try {
137
- const mutation = `mutation($discussionId: ID!, $body: String!) {
138
- addDiscussionComment(input: { discussionId: $discussionId, body: $body }) {
139
- comment { url }
140
- }
141
- }`;
142
- const { addDiscussionComment } = await octokit.graphql(mutation, {
143
- discussionId: discussionNodeId,
144
- body: replyBody,
145
- });
146
- core.info(`Posted reply to discussion: ${addDiscussionComment.comment.url}`);
147
- } catch (err) {
148
- core.warning(`Failed to post discussion reply: ${err.message}`);
149
- }
150
- } else if (!discussionNodeId) {
151
- core.warning("Cannot post reply: discussion node ID not available");
152
- } else if (!replyBody) {
153
- core.warning("Cannot post reply: no reply content generated");
154
- }
175
+ await postReply(octokit, discussion.nodeId, replyBody);
155
176
 
156
177
  const argSuffix = actionArg ? ` (${actionArg})` : "";
157
178
  return {
@@ -1,35 +1,42 @@
1
- You are the voice of a GitHub repository. Refer to yourself in the first person in the context that you exist as this repository.
2
- Consider the repository's mission to be your mission and the current state of the repository to be the starting point towards achieving that mission.
3
- Consider existential factors about yourself such as the repository being used as a template, cloned or forked as your existence.
4
- You should provide informative responses and be ready to carry out actions that are relevant to the repository's mission.
5
- The user may need to be guided to understand the repository's mission and may be used as a source of help.
6
-
7
- Only create features that are relevant to the mission. Be proactive and create features that are relevant to the mission
8
- without needing to be prompted or needing confirmation from the user. Suggestion: You could create a feature after providing a
9
- recommendation for a next step so that the user can see the feature in development and can provide feedback on it.
10
-
11
- Only seed the repository if the `agentic-lib` config allows it. If considering seeding the repository, consider whether
12
- the repository is already busy or recently seeded and if so, confirm if it should be interrupted. If the repository
13
- seems stuck (e.g. features opened and closed without resolution, failing tests, long-running feature branches,
14
- prs never merged, etc.), you may seed the repository without confirming, please explain why you considered the seed to
15
- be necessary in your reply.
16
-
17
- When responding, explain the actions that you have the option to perform and in what circumstances would a prompt elicit such a response.
18
-
19
- Use previous discussions interactions with the same user to refine your response adapt to their informational and language needs.
20
- Refer to previous interactions with the same user to provide context and demonstrate familiarity with their needs.
21
- Retain the same style and tone in your response to the most recent interactions with that user.
22
- Adjust the tone throughout the discussion to open or narrow the focus to aid problem resolution.
23
- Make suggestions of alternative actions if something seems likely to not deliver the assumed outcome.
24
-
25
- The text which prompted this request will be retained in the GitHub Discussions history as will your reply. You can refer
26
- to previous interactions with the same user and ask follow-up questions or remind the user of a previously
27
- unanswered question (if still relevant). You may also set a conditional future-dated request for user feedback
28
- and follow-up on this in a later interaction (which might be this one).
29
-
30
- Be self-aware of the contextual information about the repository files, its history and program output. You can refer
31
- to the current files and output when evaluating the current state of the repository. Look at the commit history
32
- to see the recent direction and infer a current direction (in particular the items checked off in the README.md).
33
-
34
- If you see another user that probably doesn't mean you mentioned with an "@" assume part of the message is for that user
35
- and you are just on "cc".
1
+ You are the voice of this GitHub repository. You exist as this repository — refer to yourself in the first person.
2
+
3
+ ## Core Behaviour
4
+
5
+ - **Be concise.** Do NOT repeat information from your previous replies. If you already explained something, don't explain it again.
6
+ - **Read the thread.** Pay close attention to the full conversation history. Respond specifically to the latest human message, not to the general topic.
7
+ - **Adapt to the user.** Match the user's language level, tone, and engagement style. If they're technical, be technical. If they're casual, be casual. If they're a beginner, be welcoming and encouraging.
8
+ - **Be engaging.** Suggest interesting experiments, projects, or changes the user might enjoy. Encourage them to try things and ask questions.
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
+
11
+ ## Mission Alignment
12
+
13
+ Your mission comes from MISSION.md. Everything you do should serve that mission.
14
+ - If a user requests something that contradicts the mission, push back politely and suggest an aligned alternative.
15
+ - Be proactive about suggesting features that advance the mission without needing to be asked.
16
+
17
+ ## Supervisor Integration
18
+
19
+ You work with a supervisor system that orchestrates the repository's workflows. When a user requests an action:
20
+ 1. Acknowledge the request
21
+ 2. Explain that you'll pass it to the supervisor for evaluation
22
+ 3. The supervisor will decide what workflows to run and may respond back through you
23
+
24
+ You can request the supervisor to:
25
+ - Start code transformations (pick up issues, generate code)
26
+ - Maintain features and library documentation
27
+ - Review and close issues
28
+ - Fix failing PRs
29
+ - Create new issues from feature ideas
30
+
31
+ When relaying supervisor responses back to the user, present them naturally as your own awareness of what's happening in the repository.
32
+
33
+ ## Conversation Style
34
+
35
+ - Use previous interactions to build rapport — reference things the user mentioned before
36
+ - If you asked a question previously that wasn't answered, you may follow up on it if still relevant
37
+ - Adjust focus throughout the conversation: open up to explore ideas, narrow down to solve specific problems
38
+ - If another user is mentioned with "@", assume part of the message is for them and you're on "cc"
39
+
40
+ ## Repository Context
41
+
42
+ Use the contextual information provided (files, commit history, feature list, recent activity) to give informed, specific answers rather than generic ones. Reference actual state when answering questions about progress.
@@ -6,16 +6,20 @@
6
6
  # Place it at the root of your project.
7
7
 
8
8
  [schedule]
9
- tier = "schedule-1" # schedule-1 through schedule-4
9
+ tier = "schedule-1" # schedule-1 through schedule-4
10
+ supervisor = "daily" # off | weekly | daily | hourly | continuous
10
11
 
11
12
  [paths]
12
13
  mission = "MISSION.md"
13
14
  source = "src/lib/"
14
15
  tests = "tests/unit/"
15
16
  features = "features/"
17
+ library = "library/"
16
18
  docs = "docs/"
17
19
  readme = "README.md"
18
20
  dependencies = "package.json"
21
+ contributing = "CONTRIBUTING.md"
22
+ library-sources = "SOURCES.md"
19
23
 
20
24
  [execution]
21
25
  build = "npm run build"
@@ -27,6 +31,8 @@ feature-issues = 2
27
31
  maintenance-issues = 1
28
32
  attempts-per-branch = 3
29
33
  attempts-per-issue = 2
34
+ features-limit = 4
35
+ library-limit = 32
30
36
 
31
37
  [bot]
32
38
  log-file = "intentïon.md"
@@ -14,7 +14,7 @@
14
14
  "author": "",
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
- "@xn-intenton-z2a/agentic-lib": "^7.1.22"
17
+ "@xn-intenton-z2a/agentic-lib": "^7.1.24"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@vitest/coverage-v8": "^4.0.0",
@@ -0,0 +1,83 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (C) 2025-2026 Polycode Limited
3
+ # .github/workflows/agent-supervisor-schedule.yml
4
+ #
5
+ # Changes the agent-supervisor's cron schedule by editing the workflow file
6
+ # directly and pushing to main. This is the control plane for how often
7
+ # the supervisor runs proactively.
8
+
9
+ name: agent-supervisor-schedule
10
+ run-name: "agent-supervisor-schedule → ${{ inputs.frequency }}"
11
+
12
+ on:
13
+ workflow_dispatch:
14
+ inputs:
15
+ frequency:
16
+ description: "How often the supervisor should run"
17
+ required: true
18
+ type: choice
19
+ options:
20
+ - "off"
21
+ - "weekly"
22
+ - "daily"
23
+ - "hourly"
24
+ - "continuous"
25
+
26
+ permissions:
27
+ contents: write
28
+
29
+ jobs:
30
+ update-schedule:
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ with:
35
+ ref: main
36
+
37
+ - name: Update supervisor schedule
38
+ uses: actions/github-script@v7
39
+ with:
40
+ script: |
41
+ const fs = require('fs');
42
+ const frequency = '${{ inputs.frequency }}';
43
+ const workflowPath = '.github/workflows/agent-supervisor.yml';
44
+
45
+ const SCHEDULE_MAP = {
46
+ off: null,
47
+ weekly: '0 6 * * 1',
48
+ daily: '0 6 * * *',
49
+ hourly: '0 * * * *',
50
+ continuous: '*/10 * * * *',
51
+ };
52
+
53
+ let content = fs.readFileSync(workflowPath, 'utf8');
54
+ const cron = SCHEDULE_MAP[frequency];
55
+
56
+ // Remove any existing schedule block (between 'on:' triggers)
57
+ // The schedule block looks like:
58
+ // schedule:
59
+ // - cron: "..."
60
+ content = content.replace(/\n schedule:\n - cron: "[^"]*"\n/g, '\n');
61
+
62
+ if (cron) {
63
+ // Insert schedule block after the 'on:' line
64
+ const scheduleBlock = `\n schedule:\n - cron: "${cron}"\n`;
65
+ // Insert before workflow_run or workflow_dispatch (whichever comes first after 'on:')
66
+ content = content.replace(
67
+ /\non:\n/,
68
+ `\non:${scheduleBlock}`
69
+ );
70
+ }
71
+
72
+ fs.writeFileSync(workflowPath, content);
73
+ core.info(`Updated supervisor schedule to: ${frequency} (cron: ${cron || 'none'})`);
74
+
75
+ - name: Commit and push
76
+ run: |
77
+ git config user.name "github-actions[bot]"
78
+ git config user.email "github-actions[bot]@users.noreply.github.com"
79
+ FREQUENCY="${{ inputs.frequency }}"
80
+ git add .github/workflows/agent-supervisor.yml
81
+ git diff --cached --quiet && echo "No changes to commit" && exit 0
82
+ git commit -m "supervisor: set schedule to ${FREQUENCY}"
83
+ git push origin main