@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.
- package/bin/agentic-lib.js +58 -29
- package/package.json +1 -1
- package/src/actions/agentic-step/config-loader.js +1 -0
- package/src/actions/agentic-step/tasks/discussions.js +127 -106
- package/src/agents/agent-discussion-bot.md +42 -35
- package/src/seeds/zero-agentic-lib.toml +7 -1
- package/src/seeds/zero-package.json +1 -1
- package/src/workflows/agent-supervisor-schedule.yml +83 -0
package/bin/agentic-lib.js
CHANGED
|
@@ -738,43 +738,38 @@ function initConfig(seedsDir) {
|
|
|
738
738
|
}
|
|
739
739
|
}
|
|
740
740
|
|
|
741
|
-
function
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
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
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
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
|
-
|
|
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
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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
|
@@ -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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
core.warning(`
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
74
|
+
const parts = [
|
|
82
75
|
"## Instructions",
|
|
83
76
|
agentInstructions,
|
|
84
77
|
"",
|
|
85
|
-
"## Discussion",
|
|
78
|
+
"## Discussion Thread",
|
|
86
79
|
`URL: ${discussionUrl}`,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
"##
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"",
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
164
|
+
"You are this repository. Respond in first person. Be concise and engaging — never 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
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
to the
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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"
|
|
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"
|
|
@@ -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
|