@xn-intenton-z2a/agentic-lib 7.4.8 → 7.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/{src → .github}/agents/agent-apply-fix.md +10 -0
  2. package/{src → .github}/agents/agent-director.md +10 -0
  3. package/{src → .github}/agents/agent-discovery.md +8 -0
  4. package/{src → .github}/agents/agent-discussion-bot.md +9 -0
  5. package/{src → .github}/agents/agent-issue-resolution.md +12 -0
  6. package/{src → .github}/agents/agent-iterate.md +8 -0
  7. package/{src → .github}/agents/agent-maintain-features.md +8 -0
  8. package/{src → .github}/agents/agent-maintain-library.md +7 -0
  9. package/{src → .github}/agents/agent-review-issue.md +8 -0
  10. package/{src → .github}/agents/agent-supervisor.md +9 -0
  11. package/.github/workflows/agentic-lib-test.yml +4 -2
  12. package/.github/workflows/agentic-lib-workflow.yml +70 -26
  13. package/README.md +5 -7
  14. package/agentic-lib.toml +16 -38
  15. package/bin/agentic-lib.js +49 -60
  16. package/package.json +3 -4
  17. package/src/actions/agentic-step/action.yml +1 -1
  18. package/src/actions/agentic-step/copilot.js +0 -5
  19. package/src/actions/agentic-step/index.js +8 -1
  20. package/src/actions/agentic-step/logging.js +14 -2
  21. package/src/actions/agentic-step/tasks/direct.js +86 -65
  22. package/src/actions/agentic-step/tasks/discussions.js +198 -264
  23. package/src/actions/agentic-step/tasks/enhance-issue.js +84 -33
  24. package/src/actions/agentic-step/tasks/fix-code.js +111 -57
  25. package/src/actions/agentic-step/tasks/maintain-features.js +69 -52
  26. package/src/actions/agentic-step/tasks/maintain-library.js +57 -19
  27. package/src/actions/agentic-step/tasks/resolve-issue.js +43 -18
  28. package/src/actions/agentic-step/tasks/review-issue.js +117 -117
  29. package/src/actions/agentic-step/tasks/supervise.js +140 -151
  30. package/src/actions/agentic-step/tasks/transform.js +106 -258
  31. package/src/copilot/agents.js +2 -2
  32. package/src/copilot/config.js +2 -18
  33. package/src/copilot/{hybrid-session.js → copilot-session.js} +39 -7
  34. package/src/copilot/github-tools.js +514 -0
  35. package/src/copilot/guards.js +1 -1
  36. package/src/copilot/session.js +0 -141
  37. package/src/copilot/tools.js +4 -0
  38. package/src/iterate.js +1 -1
  39. package/src/scripts/push-to-logs.sh +1 -1
  40. package/src/seeds/zero-SCREENSHOT_INDEX.png +0 -0
  41. package/src/seeds/zero-package.json +1 -1
  42. package/src/agents/agentic-lib.yml +0 -66
  43. package/src/copilot/context.js +0 -457
  44. package/src/mcp/server.js +0 -830
  45. /package/{src → .github}/agents/agent-ready-issue.md +0 -0
@@ -2,13 +2,41 @@
2
2
  // Copyright (C) 2025-2026 Polycode Limited
3
3
  // tasks/maintain-features.js — Feature lifecycle management
4
4
  //
5
- // Reviews existing features, creates new ones from mission/library analysis,
6
- // prunes completed/irrelevant features, and ensures quality.
5
+ // Uses runCopilotSession with lean prompts: the model reads feature files,
6
+ // library docs, and issues via tools to maintain the feature set.
7
7
 
8
- import { existsSync } from "fs";
9
- import { runCopilotTask, readOptionalFile, scanDirectory, formatPathsSection, extractFeatureSummary, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
8
+ import * as core from "@actions/core";
9
+ import { existsSync, readdirSync, statSync } from "fs";
10
+ import { join } from "path";
11
+ import { readOptionalFile, formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
12
+ import { runCopilotSession } from "../../../copilot/copilot-session.js";
13
+ import { createGitHubTools } from "../../../copilot/github-tools.js";
10
14
  import { checkWipLimit } from "../safety.js";
11
15
 
16
+ /**
17
+ * Build a file listing summary (names + sizes, not content).
18
+ */
19
+ function buildFileListing(dirPath, extension) {
20
+ if (!dirPath || !existsSync(dirPath)) return [];
21
+ try {
22
+ const files = readdirSync(dirPath, { recursive: true });
23
+ return files
24
+ .filter((f) => String(f).endsWith(extension))
25
+ .map((f) => {
26
+ const fullPath = join(dirPath, String(f));
27
+ try {
28
+ const stat = statSync(fullPath);
29
+ return `${f} (~${Math.round(stat.size / 40)} lines, ${stat.size} bytes)`;
30
+ } catch {
31
+ return String(f);
32
+ }
33
+ })
34
+ .slice(0, 30);
35
+ } catch {
36
+ return [];
37
+ }
38
+ }
39
+
12
40
  /**
13
41
  * Maintain the feature set — create, update, or prune feature files.
14
42
  *
@@ -16,7 +44,7 @@ import { checkWipLimit } from "../safety.js";
16
44
  * @returns {Promise<Object>} Result with outcome, tokensUsed, model
17
45
  */
18
46
  export async function maintainFeatures(context) {
19
- const { config, instructions, writablePaths, model, octokit, repo } = context;
47
+ const { config, instructions, writablePaths, model, octokit, repo, logFilePath, screenshotFilePath } = context;
20
48
  const t = config.tuning || {};
21
49
 
22
50
  // Check mission-complete signal (skip in maintenance mode)
@@ -31,35 +59,9 @@ export async function maintainFeatures(context) {
31
59
  }
32
60
 
33
61
  const mission = readOptionalFile(config.paths.mission.path);
34
- const featuresPath = config.paths.features.path;
35
62
  const featureLimit = config.paths.features.limit;
36
- const features = scanDirectory(featuresPath, ".md", { fileLimit: t.featuresScan || 10 });
37
-
38
- // Sort features: incomplete (has unchecked items) first, then by name
39
- features.sort((a, b) => {
40
- const aIncomplete = /- \[ \]/.test(a.content) ? 0 : 1;
41
- const bIncomplete = /- \[ \]/.test(b.content) ? 0 : 1;
42
- return aIncomplete - bIncomplete || a.name.localeCompare(b.name);
43
- });
44
- const libraryDocs = scanDirectory(config.paths.library.path, ".md", {
45
- contentLimit: t.documentSummary || 1000,
46
- });
47
-
48
- // Filter closed issues to only those created after the most recent init
49
- const initTimestamp = config.init?.timestamp || null;
50
- const initEpoch = initTimestamp ? new Date(initTimestamp).getTime() : 0;
51
-
52
- const { data: closedIssuesRaw } = await octokit.rest.issues.listForRepo({
53
- ...repo,
54
- state: "closed",
55
- per_page: t.issuesScan || 20,
56
- sort: "updated",
57
- direction: "desc",
58
- });
59
- const closedIssues = initEpoch > 0
60
- ? closedIssuesRaw.filter((i) => new Date(i.created_at).getTime() >= initEpoch)
61
- : closedIssuesRaw;
62
-
63
+ const featureFiles = buildFileListing(config.paths.features.path, ".md");
64
+ const libraryFiles = buildFileListing(config.paths.library.path, ".md");
63
65
  const agentInstructions = instructions || "Maintain the feature set by creating, updating, or pruning features.";
64
66
 
65
67
  const prompt = [
@@ -69,17 +71,16 @@ export async function maintainFeatures(context) {
69
71
  "## Mission",
70
72
  mission,
71
73
  "",
72
- `## Current Features (${features.length}/${featureLimit} max)`,
73
- ...features.map((f) => `### ${f.name}\n${f.content}`),
74
- "",
75
- libraryDocs.length > 0 ? `## Library Documents (${libraryDocs.length})` : "",
76
- ...libraryDocs.map((d) => `### ${d.name}\n${d.content}`),
74
+ `## Current Features (${featureFiles.length}/${featureLimit} max)`,
75
+ featureFiles.length > 0 ? featureFiles.join(", ") : "none",
77
76
  "",
78
- `## Recently Closed Issues (${closedIssues.length}${initTimestamp ? `, since init ${initTimestamp}` : ""})`,
79
- ...closedIssues.slice(0, Math.floor((t.issuesScan || 20) / 2)).map((i) => `- #${i.number}: ${i.title}`),
77
+ `## Library Documents (${libraryFiles.length})`,
78
+ libraryFiles.length > 0 ? libraryFiles.join(", ") : "none",
80
79
  "",
81
80
  "## Your Task",
82
- `1. Review each existing feature if it is already implemented or irrelevant, delete it.`,
81
+ "Use read_file to read each feature file and evaluate it.",
82
+ "Use list_issues to see closed issues that indicate completed features.",
83
+ "1. Review each existing feature — if it is already implemented or irrelevant, delete it.",
83
84
  `2. If there are fewer than ${featureLimit} features, create new features aligned with the mission.`,
84
85
  "3. Ensure each feature has clear, testable acceptance criteria.",
85
86
  "",
@@ -90,23 +91,39 @@ export async function maintainFeatures(context) {
90
91
  "- Feature files must be markdown with a descriptive filename (e.g. HTTP_SERVER.md)",
91
92
  ].join("\n");
92
93
 
93
- const { content: resultContent, tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
94
+ const systemPrompt =
95
+ "You are a feature lifecycle manager. Create, update, and prune feature specification files to keep the project focused on its mission." +
96
+ NARRATIVE_INSTRUCTION;
97
+
98
+ const createTools = (defineTool, _wp, logger) => {
99
+ return createGitHubTools(octokit, repo, defineTool, logger);
100
+ };
101
+
102
+ const attachments = [];
103
+ if (logFilePath) attachments.push({ type: "file", path: logFilePath });
104
+ if (screenshotFilePath) attachments.push({ type: "file", path: screenshotFilePath });
105
+
106
+ const result = await runCopilotSession({
107
+ workspacePath: process.cwd(),
94
108
  model,
95
- systemMessage:
96
- "You are a feature lifecycle manager. Create, update, and prune feature specification files to keep the project focused on its mission." + NARRATIVE_INSTRUCTION,
97
- prompt,
98
- writablePaths,
99
109
  tuning: t,
110
+ agentPrompt: systemPrompt,
111
+ userPrompt: prompt,
112
+ writablePaths,
113
+ createTools,
114
+ attachments,
115
+ excludedTools: ["dispatch_workflow", "close_issue", "label_issue", "post_discussion_comment", "run_tests"],
116
+ logger: { info: core.info, warning: core.warning, error: core.error, debug: core.debug },
100
117
  });
101
118
 
102
119
  return {
103
120
  outcome: "features-maintained",
104
- tokensUsed,
105
- inputTokens,
106
- outputTokens,
107
- cost,
121
+ tokensUsed: result.tokensIn + result.tokensOut,
122
+ inputTokens: result.tokensIn,
123
+ outputTokens: result.tokensOut,
124
+ cost: 0,
108
125
  model,
109
- details: `Maintained features (${features.length} existing, limit ${featureLimit})`,
110
- narrative: extractNarrative(resultContent, `Maintained ${features.length} features (limit ${featureLimit}).`),
126
+ details: `Maintained features (${featureFiles.length} existing, limit ${featureLimit})`,
127
+ narrative: result.narrative || extractNarrative(result.agentMessage, `Maintained ${featureFiles.length} features (limit ${featureLimit}).`),
111
128
  };
112
129
  }
@@ -2,12 +2,38 @@
2
2
  // Copyright (C) 2025-2026 Polycode Limited
3
3
  // tasks/maintain-library.js — Library and knowledge management
4
4
  //
5
- // Crawls SOURCES.md, updates library documents, maintains the knowledge base
6
- // that provides context for feature generation and issue resolution.
5
+ // Uses runCopilotSession with lean prompts: the model reads sources, library docs,
6
+ // and web content via tools to maintain the knowledge base.
7
7
 
8
8
  import * as core from "@actions/core";
9
- import { existsSync } from "fs";
10
- import { runCopilotTask, readOptionalFile, scanDirectory, formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
9
+ import { existsSync, readdirSync, statSync } from "fs";
10
+ import { join } from "path";
11
+ import { readOptionalFile, formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
12
+ import { runCopilotSession } from "../../../copilot/copilot-session.js";
13
+
14
+ /**
15
+ * Build a file listing summary (names + sizes, not content).
16
+ */
17
+ function buildFileListing(dirPath, extension) {
18
+ if (!dirPath || !existsSync(dirPath)) return [];
19
+ try {
20
+ const files = readdirSync(dirPath, { recursive: true });
21
+ return files
22
+ .filter((f) => String(f).endsWith(extension))
23
+ .map((f) => {
24
+ const fullPath = join(dirPath, String(f));
25
+ try {
26
+ const stat = statSync(fullPath);
27
+ return `${f} (~${Math.round(stat.size / 40)} lines, ${stat.size} bytes)`;
28
+ } catch {
29
+ return String(f);
30
+ }
31
+ })
32
+ .slice(0, 30);
33
+ } catch {
34
+ return [];
35
+ }
36
+ }
11
37
 
12
38
  /**
13
39
  * Maintain the library of knowledge documents from source URLs.
@@ -16,7 +42,7 @@ import { runCopilotTask, readOptionalFile, scanDirectory, formatPathsSection, ex
16
42
  * @returns {Promise<Object>} Result with outcome, tokensUsed, model
17
43
  */
18
44
  export async function maintainLibrary(context) {
19
- const { config, instructions, writablePaths, model } = context;
45
+ const { config, instructions, writablePaths, model, logFilePath, screenshotFilePath } = context;
20
46
  const t = config.tuning || {};
21
47
 
22
48
  // Check mission-complete signal (skip in maintenance mode)
@@ -32,7 +58,7 @@ export async function maintainLibrary(context) {
32
58
 
33
59
  const libraryPath = config.paths.library.path;
34
60
  const libraryLimit = config.paths.library.limit;
35
- const libraryDocs = scanDirectory(libraryPath, ".md", { contentLimit: t.documentSummary || 500 });
61
+ const libraryFiles = buildFileListing(libraryPath, ".md");
36
62
 
37
63
  const agentInstructions = instructions || "Maintain the library by updating documents from sources.";
38
64
 
@@ -66,10 +92,11 @@ export async function maintainLibrary(context) {
66
92
  "## Sources",
67
93
  sources,
68
94
  "",
69
- `## Current Library Documents (${libraryDocs.length}/${libraryLimit} max)`,
70
- ...libraryDocs.map((d) => `### ${d.name}\n${d.content}`),
95
+ `## Current Library Documents (${libraryFiles.length}/${libraryLimit} max)`,
96
+ libraryFiles.length > 0 ? libraryFiles.join(", ") : "none",
71
97
  "",
72
98
  "## Your Task",
99
+ "Use read_file to read existing library documents.",
73
100
  "1. Read each URL in SOURCES.md and extract technical content.",
74
101
  "2. Create or update library documents based on the source content.",
75
102
  "3. Remove library documents that no longer have corresponding sources.",
@@ -81,28 +108,39 @@ export async function maintainLibrary(context) {
81
108
  ].join("\n");
82
109
  }
83
110
 
84
- const { content: resultContent, tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
111
+ const systemPrompt =
112
+ "You are a knowledge librarian. Maintain a library of technical documents extracted from web sources." +
113
+ NARRATIVE_INSTRUCTION;
114
+
115
+ const attachments = [];
116
+ if (logFilePath) attachments.push({ type: "file", path: logFilePath });
117
+ if (screenshotFilePath) attachments.push({ type: "file", path: screenshotFilePath });
118
+
119
+ const result = await runCopilotSession({
120
+ workspacePath: process.cwd(),
85
121
  model,
86
- systemMessage:
87
- "You are a knowledge librarian. Maintain a library of technical documents extracted from web sources." + NARRATIVE_INSTRUCTION,
88
- prompt,
89
- writablePaths,
90
122
  tuning: t,
123
+ agentPrompt: systemPrompt,
124
+ userPrompt: prompt,
125
+ writablePaths,
126
+ attachments,
127
+ excludedTools: ["dispatch_workflow", "close_issue", "label_issue", "post_discussion_comment", "run_tests"],
128
+ logger: { info: core.info, warning: core.warning, error: core.error, debug: core.debug },
91
129
  });
92
130
 
93
131
  const outcomeLabel = hasUrls ? "library-maintained" : "sources-discovered";
94
132
  const detailsMsg = hasUrls
95
- ? `Maintained library (${libraryDocs.length} docs, limit ${libraryLimit})`
133
+ ? `Maintained library (${libraryFiles.length} docs, limit ${libraryLimit})`
96
134
  : `Discovered sources for SOURCES.md from mission`;
97
135
 
98
136
  return {
99
137
  outcome: outcomeLabel,
100
- tokensUsed,
101
- inputTokens,
102
- outputTokens,
103
- cost,
138
+ tokensUsed: result.tokensIn + result.tokensOut,
139
+ inputTokens: result.tokensIn,
140
+ outputTokens: result.tokensOut,
141
+ cost: 0,
104
142
  model,
105
143
  details: detailsMsg,
106
- narrative: extractNarrative(resultContent, detailsMsg),
144
+ narrative: result.narrative || extractNarrative(result.agentMessage, detailsMsg),
107
145
  };
108
146
  }
@@ -2,12 +2,14 @@
2
2
  // Copyright (C) 2025-2026 Polycode Limited
3
3
  // tasks/resolve-issue.js — Issue → code → PR
4
4
  //
5
- // Given an issue number, reads the issue, generates code using the Copilot SDK,
6
- // validates with tests, and creates a PR.
5
+ // Uses runCopilotSession with lean prompts: the model reads source files,
6
+ // writes code, and runs tests via tools.
7
7
 
8
8
  import * as core from "@actions/core";
9
9
  import { checkAttemptLimit, checkWipLimit, isIssueResolved } from "../safety.js";
10
- import { runCopilotTask, readOptionalFile, formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
10
+ import { readOptionalFile, formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
11
+ import { runCopilotSession } from "../../../copilot/copilot-session.js";
12
+ import { createGitHubTools, createGitTools } from "../../../copilot/github-tools.js";
11
13
 
12
14
  /**
13
15
  * Resolve a GitHub issue by generating code and creating a PR.
@@ -16,7 +18,7 @@ import { runCopilotTask, readOptionalFile, formatPathsSection, extractNarrative,
16
18
  * @returns {Promise<Object>} Result with outcome, prNumber, tokensUsed, model
17
19
  */
18
20
  export async function resolveIssue(context) {
19
- const { octokit, repo, config, issueNumber, instructions, writablePaths, testCommand, model } = context;
21
+ const { octokit, repo, config, issueNumber, instructions, writablePaths, testCommand, model, logFilePath, screenshotFilePath } = context;
20
22
 
21
23
  if (!issueNumber) {
22
24
  throw new Error("resolve-issue task requires issue-number input");
@@ -57,7 +59,6 @@ export async function resolveIssue(context) {
57
59
 
58
60
  const contributing = readOptionalFile(config.paths.contributing.path);
59
61
  const agentInstructions = instructions || "Resolve the GitHub issue by writing code that satisfies the requirements.";
60
- const readOnlyPaths = config.readOnlyPaths;
61
62
 
62
63
  const prompt = [
63
64
  "## Instructions",
@@ -71,34 +72,58 @@ export async function resolveIssue(context) {
71
72
  comments.length > 0 ? "## Issue Comments" : "",
72
73
  ...comments.map((c) => `**${c.user.login}:** ${c.body}`),
73
74
  "",
74
- formatPathsSection(writablePaths, readOnlyPaths, config),
75
+ "## Your Task",
76
+ "Read the source files you need (use read_file tool).",
77
+ "Write code to resolve this issue, then run run_tests to verify.",
78
+ "",
79
+ formatPathsSection(writablePaths, config.readOnlyPaths, config),
75
80
  "",
76
81
  "## Constraints",
77
- `- Run \`${testCommand}\` to validate your changes`,
82
+ `- Run \`${testCommand}\` via run_tests to validate your changes`,
83
+ "- Use read_file to read source files (don't guess at contents)",
78
84
  contributing ? `\n## Contributing Guidelines\n${contributing}` : "",
79
85
  ].join("\n");
80
86
 
81
87
  const t = config.tuning || {};
82
- const { content: resultContent, tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
88
+ const systemPrompt =
89
+ `You are an autonomous coding agent resolving GitHub issue #${issueNumber}. Write clean, tested code. Only modify files listed under "Writable" paths. Read-only paths are for context only.` +
90
+ NARRATIVE_INSTRUCTION;
91
+
92
+ const createTools = (defineTool, _wp, logger) => {
93
+ const ghTools = createGitHubTools(octokit, repo, defineTool, logger);
94
+ const gitTools = createGitTools(defineTool, logger);
95
+ return [...ghTools, ...gitTools];
96
+ };
97
+
98
+ const attachments = [];
99
+ if (logFilePath) attachments.push({ type: "file", path: logFilePath });
100
+ if (screenshotFilePath) attachments.push({ type: "file", path: screenshotFilePath });
101
+
102
+ const result = await runCopilotSession({
103
+ workspacePath: process.cwd(),
83
104
  model,
84
- systemMessage: `You are an autonomous coding agent resolving GitHub issue #${issueNumber}. Write clean, tested code. Only modify files listed under "Writable" paths. Read-only paths are for context only.` + NARRATIVE_INSTRUCTION,
85
- prompt,
86
- writablePaths,
87
105
  tuning: t,
106
+ agentPrompt: systemPrompt,
107
+ userPrompt: prompt,
108
+ writablePaths,
109
+ createTools,
110
+ attachments,
111
+ excludedTools: ["dispatch_workflow", "close_issue", "label_issue", "post_discussion_comment"],
112
+ logger: { info: core.info, warning: core.warning, error: core.error, debug: core.debug },
88
113
  });
89
114
 
90
- core.info(`Copilot SDK response received (${tokensUsed} tokens)`);
115
+ core.info(`Issue resolution completed (${result.tokensIn + result.tokensOut} tokens)`);
91
116
 
92
117
  return {
93
118
  outcome: "code-generated",
94
119
  prNumber: null,
95
- tokensUsed,
96
- inputTokens,
97
- outputTokens,
98
- cost,
120
+ tokensUsed: result.tokensIn + result.tokensOut,
121
+ inputTokens: result.tokensIn,
122
+ outputTokens: result.tokensOut,
123
+ cost: 0,
99
124
  model,
100
125
  commitUrl: null,
101
- details: `Generated code for issue #${issueNumber}: ${resultContent.substring(0, 200)}`,
102
- narrative: extractNarrative(resultContent, `Generated code for issue #${issueNumber}.`),
126
+ details: `Generated code for issue #${issueNumber}: ${(result.agentMessage || "").substring(0, 200)}`,
127
+ narrative: result.narrative || extractNarrative(result.agentMessage, `Generated code for issue #${issueNumber}.`),
103
128
  };
104
129
  }