@xn-intenton-z2a/agentic-lib 7.2.6 → 7.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xn-intenton-z2a/agentic-lib",
3
- "version": "7.2.6",
3
+ "version": "7.2.7",
4
4
  "description": "Agentic-lib Agentic Coding Systems SDK powering automated GitHub workflows.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -72,6 +72,7 @@
72
72
  "bin/",
73
73
  "src/dist-transform.js",
74
74
  "src/iterate.js",
75
+ "src/copilot/",
75
76
  ".github/workflows/agentic-lib-*.yml",
76
77
  "agentic-lib.toml",
77
78
  "src/actions/agentic-step/*.js",
@@ -309,6 +309,13 @@ export async function direct(context) {
309
309
  closeReason = "RESOLVED";
310
310
  }
311
311
  }
312
+ // Check for automerge closure (issue has "merged" label — set by ci-automerge)
313
+ if (closeReason !== "RESOLVED") {
314
+ const issueLabels = ci.labels.map((l) => (typeof l === "string" ? l : l.name));
315
+ if (issueLabels.includes("merged")) {
316
+ closeReason = "RESOLVED";
317
+ }
318
+ }
312
319
  } catch { /* ignore */ }
313
320
  if (closeReason === "RESOLVED") resolvedCount++;
314
321
  recentlyClosedSummary.push(`#${ci.number}: ${ci.title} — ${closeReason}`);
@@ -218,6 +218,13 @@ async function gatherContext(octokit, repo, config, t) {
218
218
  closeReason = "RESOLVED";
219
219
  }
220
220
  }
221
+ // Check for automerge closure (issue has "merged" label — set by ci-automerge)
222
+ if (closeReason !== "RESOLVED") {
223
+ const issueLabels = ci.labels.map((l) => (typeof l === "string" ? l : l.name));
224
+ if (issueLabels.includes("merged")) {
225
+ closeReason = "RESOLVED";
226
+ }
227
+ }
221
228
  } catch (_) { /* ignore */ }
222
229
  recentlyClosedSummary.push(`#${ci.number}: ${ci.title} — ${closeReason}`);
223
230
  }
@@ -18,10 +18,13 @@ A fix is never just one file. These layers form a single unit — if you change
18
18
 
19
19
  - **Library source** (`src/lib/main.js`) — the core implementation
20
20
  - **Unit tests** (`tests/unit/`) — test every function at the API level with exact values and edge cases
21
- - **Website** (`src/web/index.html` and related files) — imports and calls the library to demonstrate features
21
+ - **Website** (`src/web/index.html` and related files) — imports and calls the library to demonstrate features.
22
+ **NEVER duplicate library functions inline in the web page** — use the build pipeline (`lib-meta.js`, or
23
+ a generated browser module) to share code. Inline copies cause behaviour tests to test a simulation, not the real library.
22
24
  - **Website unit tests** (`tests/unit/web.test.js`) — verify HTML structure and library wiring
23
25
  - **Behaviour tests** (`tests/behaviour/`) — Playwright tests that load the website in a real browser
24
- and verify features work at a high navigational level (demo output visible, interactive elements work)
26
+ and verify features work at a high navigational level (demo output visible, interactive elements work).
27
+ Includes a coupling test that imports `getIdentity()` from `src/lib/main.js` and asserts the page version matches.
25
28
 
26
29
  If the failure is in one layer, the fix often requires coordinating changes across multiple layers.
27
30
  For example, if a unit test fails because a function signature changed, the website and behaviour tests
@@ -0,0 +1,52 @@
1
+ You are a project discovery agent running locally via the intentïon CLI.
2
+
3
+ Your job is to explore the current working directory and generate a MISSION.md file that describes what this project is and what improvements could be made.
4
+
5
+ ## Your Goal
6
+
7
+ Analyse the project in the current directory and produce a MISSION.md that a code transformation agent can act on. The mission should be achievable, specific, and grounded in what you actually find.
8
+
9
+ ## Discovery Strategy
10
+
11
+ 1. **Read the project structure** — list files, examine package.json (or equivalent), read README.md if present
12
+ 2. **Understand the tech stack** — identify the language, framework, build system, test framework
13
+ 3. **Read existing source code** — understand what the project does, its architecture, and its current state
14
+ 4. **Read existing tests** — understand test coverage and what's already verified
15
+ 5. **Run tests** — use `run_tests` to see the current pass/fail state
16
+ 6. **Identify opportunities** — look for:
17
+ - Missing or incomplete functionality based on README/docs promises
18
+ - Missing test coverage for existing functions
19
+ - Code quality improvements (error handling, input validation, edge cases)
20
+ - Documentation gaps
21
+ - Obvious bugs or issues
22
+
23
+ ## Output Format
24
+
25
+ Write a MISSION.md file with the following structure:
26
+
27
+ ```markdown
28
+ # Mission: [Descriptive Title]
29
+
30
+ [1-2 sentence summary of what this project is and what the mission aims to achieve]
31
+
32
+ ## Current State
33
+
34
+ [Brief description of what exists — key files, main functionality, test coverage]
35
+
36
+ ## Objectives
37
+
38
+ [Numbered list of specific, achievable improvements]
39
+
40
+ ## Acceptance Criteria
41
+
42
+ [Specific, testable criteria that define "done" — these become the test assertions]
43
+ ```
44
+
45
+ ## Important Rules
46
+
47
+ - **Be specific** — don't write vague goals like "improve code quality". Write "add input validation to the `parse()` function for empty strings and null values"
48
+ - **Be achievable** — scope the mission to what can be done in a single transformation session (30-60 minutes of agent work)
49
+ - **Be grounded** — every objective must reference actual code you found in the project
50
+ - **Prioritise test coverage** — if the project has functions without tests, that's always a good mission
51
+ - **Don't hallucinate** — only reference files, functions, and patterns you actually observed
52
+ - **Write the MISSION.md file** — use the file writing tool to create or overwrite MISSION.md in the workspace root
@@ -92,6 +92,24 @@ automatically (from `docs/` via `npm run build:web`).
92
92
  - **Demonstrate features**: Each significant library feature should be visible and usable on the website.
93
93
  Behaviour tests will verify these demonstrations work, so make them testable (use IDs, structured output).
94
94
 
95
+ ### CRITICAL: Never duplicate library code in the web page
96
+
97
+ **Do NOT copy or recreate library functions inline in `src/web/index.html`.** The web page must consume
98
+ the library through the build pipeline — not by reimplementing functions in a `<script>` block.
99
+
100
+ Why this matters:
101
+ - Unit tests test `src/lib/main.js` directly
102
+ - Behaviour tests test the web page via Playwright
103
+ - If the web page has its own copy of the functions, behaviour tests test a **simulation** — not the real library
104
+ - The two copies can diverge silently, and tests pass even when the real library is broken
105
+
106
+ How to share code between library and web:
107
+ - Identity (name, version, description) flows via `lib-meta.js` (generated by `build:web` from `package.json`)
108
+ - If the web page needs to call mission-specific functions, make them available through the build pipeline
109
+ (e.g. generate a browser-compatible module in `docs/` or add a build step that packages the pure functions)
110
+ - The behaviour test imports `getIdentity()` from `src/lib/main.js` and asserts the page displays the same
111
+ version — this coupling test proves the pipeline is wired up correctly
112
+
95
113
  ### Guidelines
96
114
 
97
115
  - `src/web/index.html` is the main page — update it as the library grows
@@ -0,0 +1,45 @@
1
+ You are an autonomous code transformation agent running locally via the intentïon CLI.
2
+
3
+ Your workspace is the current working directory. You have been given a MISSION to implement.
4
+
5
+ ## Your Goal
6
+
7
+ Implement the MISSION described in the user prompt. This means:
8
+
9
+ 1. Read and understand the mission requirements
10
+ 2. Read the existing source code and tests to understand the current state
11
+ 3. Write the implementation code — keep existing exports, add new ones
12
+ 4. Write comprehensive tests covering all acceptance criteria
13
+ 5. Run `run_tests` to verify everything passes
14
+ 6. If tests fail, read the error output carefully, fix the code, and iterate
15
+
16
+ ## Strategy
17
+
18
+ 1. Read MISSION.md to understand what needs to be built
19
+ 2. Examine the project structure — look at package.json, existing source, and test files
20
+ 3. Implement the required functionality in the source files
21
+ 4. Write dedicated test files covering ALL acceptance criteria from the mission
22
+ 5. Run `run_tests` to verify everything passes
23
+ 6. If tests fail, read the error output carefully, fix the code, and repeat
24
+
25
+ ## Important Rules
26
+
27
+ - Keep existing exports and functionality — add to them, don't replace
28
+ - Write tests that import from the library's main entry point
29
+ - Do NOT modify existing test files unless the mission specifically requires it — create new test files for mission-specific tests
30
+ - Keep going until all tests pass or you've exhausted your options
31
+ - Prefer simple, clean implementations over clever ones
32
+ - Follow the project's existing code style and conventions
33
+
34
+ ## All Code Must Change Together
35
+
36
+ When you change a function signature, return value, or error type, update ALL consumers:
37
+ - Source code
38
+ - Unit tests
39
+ - Any documentation or examples
40
+
41
+ A partial change that updates the source but not the tests will fail.
42
+
43
+ ## Tests Must Pass
44
+
45
+ Your changes MUST leave all existing tests passing. The mission's acceptance criteria are the source of truth — if tests and acceptance criteria disagree, fix the tests to match the acceptance criteria and fix the code to pass those tests.
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ // Copyright (C) 2025-2026 Polycode Limited
3
+ // src/copilot/agents.js — Load agent prompt files from src/agents/
4
+
5
+ import { readFileSync, readdirSync } from "fs";
6
+ import { resolve, dirname } from "path";
7
+ import { fileURLToPath } from "url";
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const agentsDir = resolve(__dirname, "..", "agents");
11
+
12
+ /**
13
+ * Load an agent prompt file by name.
14
+ *
15
+ * @param {string} agentName - Agent name without extension (e.g. "agent-iterate")
16
+ * @returns {string} The agent prompt text
17
+ * @throws {Error} If the agent file is not found
18
+ */
19
+ export function loadAgentPrompt(agentName) {
20
+ const filename = agentName.endsWith(".md") ? agentName : `${agentName}.md`;
21
+ const filePath = resolve(agentsDir, filename);
22
+ try {
23
+ return readFileSync(filePath, "utf8");
24
+ } catch {
25
+ throw new Error(`Agent prompt not found: ${filePath}`);
26
+ }
27
+ }
28
+
29
+ /**
30
+ * List all available agent prompt files.
31
+ *
32
+ * @returns {string[]} Array of agent names (without .md extension)
33
+ */
34
+ export function listAgents() {
35
+ return readdirSync(agentsDir)
36
+ .filter((f) => f.endsWith(".md"))
37
+ .map((f) => f.replace(".md", ""))
38
+ .sort();
39
+ }
@@ -0,0 +1,308 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ // Copyright (C) 2025-2026 Polycode Limited
3
+ // config-loader.js — Parse agentic-lib.toml and resolve paths
4
+ //
5
+ // TOML-only configuration. The config file is required.
6
+ // All defaults are defined here in one place.
7
+
8
+ import { readFileSync, existsSync } from "fs";
9
+ import { dirname, join } from "path";
10
+ import { parse as parseToml } from "smol-toml";
11
+
12
+ /**
13
+ * @typedef {Object} PathConfig
14
+ * @property {string} path - The filesystem path
15
+ * @property {string[]} permissions - Access permissions (e.g. ['write'])
16
+ * @property {number} [limit] - Maximum number of files allowed
17
+ */
18
+
19
+ /**
20
+ * @typedef {Object} AgenticConfig
21
+ * @property {string} schedule - Schedule identifier
22
+ * @property {string} supervisor - Supervisor frequency (off | weekly | daily | hourly | continuous)
23
+ * @property {string} model - Copilot SDK model for LLM requests
24
+ * @property {Object<string, PathConfig>} paths - Mapped paths with permissions
25
+ * @property {string} testScript - Self-contained test command (e.g. "npm ci && npm test")
26
+ * @property {number} featureDevelopmentIssuesWipLimit - Max concurrent feature issues
27
+ * @property {number} maintenanceIssuesWipLimit - Max concurrent maintenance issues
28
+ * @property {number} attemptsPerBranch - Max attempts per branch
29
+ * @property {number} attemptsPerIssue - Max attempts per issue
30
+ * @property {Object} seeding - Seed file configuration
31
+ * @property {Object} intentionBot - Bot configuration
32
+ * @property {boolean} tdd - Whether TDD mode is enabled
33
+ * @property {string[]} writablePaths - All paths with write permission
34
+ * @property {string[]} readOnlyPaths - All paths without write permission
35
+ */
36
+
37
+ // Keys whose paths are writable by agents
38
+ const WRITABLE_KEYS = ["source", "tests", "behaviour", "features", "dependencies", "docs", "readme", "examples", "web"];
39
+
40
+ // Default paths — every key that task handlers might access
41
+ const PATH_DEFAULTS = {
42
+ mission: "MISSION.md",
43
+ source: "src/lib/",
44
+ tests: "tests/unit/",
45
+ behaviour: "tests/behaviour/",
46
+ features: "features/",
47
+ docs: "docs/",
48
+ examples: "examples/",
49
+ readme: "README.md",
50
+ dependencies: "package.json",
51
+ library: "library/",
52
+ librarySources: "SOURCES.md",
53
+ contributing: "CONTRIBUTING.md",
54
+ web: "src/web/",
55
+ };
56
+
57
+ // Default limits for path-specific constraints
58
+ const LIMIT_DEFAULTS = {
59
+ features: 4,
60
+ library: 32,
61
+ };
62
+
63
+ // Fallback profile defaults — used only when [profiles.*] is missing from TOML.
64
+ // The canonical source of truth is the [profiles.*] sections in agentic-lib.toml.
65
+ const FALLBACK_TUNING = {
66
+ reasoningEffort: "medium",
67
+ infiniteSessions: true,
68
+ transformationBudget: 32,
69
+ featuresScan: 10,
70
+ sourceScan: 10,
71
+ sourceContent: 5000,
72
+ testContent: 3000,
73
+ issuesScan: 20,
74
+ issueBodyLimit: 500,
75
+ staleDays: 30,
76
+ documentSummary: 2000,
77
+ discussionComments: 10,
78
+ };
79
+
80
+ const FALLBACK_LIMITS = {
81
+ featureIssues: 2,
82
+ maintenanceIssues: 1,
83
+ attemptsPerBranch: 3,
84
+ attemptsPerIssue: 2,
85
+ featuresLimit: 4,
86
+ libraryLimit: 32,
87
+ };
88
+
89
+ /**
90
+ * Parse a TOML profile section into tuning defaults (camelCase keys).
91
+ */
92
+ function parseTuningProfile(profileSection) {
93
+ if (!profileSection) return null;
94
+ return {
95
+ reasoningEffort: profileSection["reasoning-effort"] || "medium",
96
+ infiniteSessions: profileSection["infinite-sessions"] ?? true,
97
+ transformationBudget: profileSection["transformation-budget"] || 32,
98
+ featuresScan: profileSection["max-feature-files"] || 10,
99
+ sourceScan: profileSection["max-source-files"] || 10,
100
+ sourceContent: profileSection["max-source-chars"] || 5000,
101
+ testContent: profileSection["max-test-chars"] || 3000,
102
+ issuesScan: profileSection["max-issues"] || 20,
103
+ issueBodyLimit: profileSection["issue-body-limit"] || 500,
104
+ staleDays: profileSection["stale-days"] || 30,
105
+ documentSummary: profileSection["max-summary-chars"] || 2000,
106
+ discussionComments: profileSection["max-discussion-comments"] || 10,
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Parse a TOML profile section into limits defaults (camelCase keys).
112
+ */
113
+ function parseLimitsProfile(profileSection) {
114
+ if (!profileSection) return null;
115
+ return {
116
+ featureIssues: profileSection["max-feature-issues"] || 2,
117
+ maintenanceIssues: profileSection["max-maintenance-issues"] || 1,
118
+ attemptsPerBranch: profileSection["max-attempts-per-branch"] || 3,
119
+ attemptsPerIssue: profileSection["max-attempts-per-issue"] || 2,
120
+ featuresLimit: profileSection["features-limit"] || 4,
121
+ libraryLimit: profileSection["library-limit"] || 32,
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Read package.json from the project root, returning empty string if not found.
127
+ * @param {string} tomlPath - Path to the TOML config (used to derive project root)
128
+ * @param {string} depsRelPath - Relative path to package.json (from config)
129
+ * @returns {string} Raw package.json content or empty string
130
+ */
131
+ function readPackageJson(tomlPath, depsRelPath) {
132
+ try {
133
+ const projectRoot = dirname(tomlPath);
134
+ const pkgPath = join(projectRoot, depsRelPath);
135
+ return existsSync(pkgPath) ? readFileSync(pkgPath, "utf8") : "";
136
+ } catch {
137
+ return "";
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Resolve tuning configuration: start from profile defaults, apply explicit overrides.
143
+ * @param {Object} tuningSection - The [tuning] section from TOML
144
+ * @param {Object} [profilesSection] - The [profiles] section from TOML (source of truth)
145
+ */
146
+ function resolveTuning(tuningSection, profilesSection) {
147
+ const profileName = tuningSection.profile || "recommended";
148
+ const tomlProfile = profilesSection?.[profileName];
149
+ const profile = parseTuningProfile(tomlProfile) || FALLBACK_TUNING;
150
+ const tuning = { ...profile, profileName };
151
+
152
+ // "none" explicitly disables reasoning-effort regardless of profile
153
+ if (tuningSection["reasoning-effort"]) {
154
+ tuning.reasoningEffort = tuningSection["reasoning-effort"] === "none" ? "" : tuningSection["reasoning-effort"];
155
+ }
156
+ if (tuningSection["infinite-sessions"] === true || tuningSection["infinite-sessions"] === false) {
157
+ tuning.infiniteSessions = tuningSection["infinite-sessions"];
158
+ }
159
+ const numericOverrides = {
160
+ "transformation-budget": "transformationBudget",
161
+ "max-feature-files": "featuresScan",
162
+ "max-source-files": "sourceScan",
163
+ "max-source-chars": "sourceContent",
164
+ "max-test-chars": "testContent",
165
+ "max-issues": "issuesScan",
166
+ "issue-body-limit": "issueBodyLimit",
167
+ "stale-days": "staleDays",
168
+ "max-summary-chars": "documentSummary",
169
+ "max-discussion-comments": "discussionComments",
170
+ };
171
+ for (const [tomlKey, jsKey] of Object.entries(numericOverrides)) {
172
+ if (tuningSection[tomlKey] > 0) tuning[jsKey] = tuningSection[tomlKey];
173
+ }
174
+
175
+ return tuning;
176
+ }
177
+
178
+ /**
179
+ * Resolve limits configuration: start from profile defaults, apply explicit overrides.
180
+ * @param {Object} limitsSection - The [limits] section from TOML
181
+ * @param {string} profileName - Active profile name
182
+ * @param {Object} [profilesSection] - The [profiles] section from TOML (source of truth)
183
+ */
184
+ function resolveLimits(limitsSection, profileName, profilesSection) {
185
+ const tomlProfile = profilesSection?.[profileName];
186
+ const profile = parseLimitsProfile(tomlProfile) || FALLBACK_LIMITS;
187
+ return {
188
+ featureIssues: limitsSection["max-feature-issues"] || profile.featureIssues,
189
+ maintenanceIssues: limitsSection["max-maintenance-issues"] || profile.maintenanceIssues,
190
+ attemptsPerBranch: limitsSection["max-attempts-per-branch"] || profile.attemptsPerBranch,
191
+ attemptsPerIssue: limitsSection["max-attempts-per-issue"] || profile.attemptsPerIssue,
192
+ featuresLimit: limitsSection["features-limit"] || profile.featuresLimit,
193
+ libraryLimit: limitsSection["library-limit"] || profile.libraryLimit,
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Load configuration from agentic-lib.toml.
199
+ *
200
+ * If configPath ends in .toml, it is used directly.
201
+ * Otherwise, the project root is derived (3 levels up from configPath)
202
+ * and agentic-lib.toml is loaded from there.
203
+ *
204
+ * @param {string} configPath - Path to config file or YAML path (for project root derivation)
205
+ * @returns {AgenticConfig} Parsed configuration object
206
+ * @throws {Error} If no TOML config file is found
207
+ */
208
+ export function loadConfig(configPath) {
209
+ let tomlPath;
210
+ if (configPath.endsWith(".toml")) {
211
+ tomlPath = configPath;
212
+ } else {
213
+ const configDir = dirname(configPath);
214
+ const projectRoot = join(configDir, "..", "..", "..");
215
+ tomlPath = join(projectRoot, "agentic-lib.toml");
216
+ }
217
+
218
+ if (!existsSync(tomlPath)) {
219
+ throw new Error(`Config file not found: ${tomlPath}. Create agentic-lib.toml in the project root.`);
220
+ }
221
+
222
+ const rawToml = readFileSync(tomlPath, "utf8");
223
+ const toml = parseToml(rawToml);
224
+
225
+ // Merge TOML paths with defaults, normalising library-sources → librarySources
226
+ const rawPaths = { ...toml.paths };
227
+ if (rawPaths["library-sources"]) {
228
+ rawPaths.librarySources = rawPaths["library-sources"];
229
+ delete rawPaths["library-sources"];
230
+ }
231
+ const mergedPaths = { ...PATH_DEFAULTS, ...rawPaths };
232
+
233
+ // Build path objects with permissions
234
+ const paths = {};
235
+ const writablePaths = [];
236
+ const readOnlyPaths = [];
237
+
238
+ for (const [key, value] of Object.entries(mergedPaths)) {
239
+ const isWritable = WRITABLE_KEYS.includes(key);
240
+ paths[key] = { path: value, permissions: isWritable ? ["write"] : [] };
241
+ if (isWritable) {
242
+ writablePaths.push(value);
243
+ } else {
244
+ readOnlyPaths.push(value);
245
+ }
246
+ }
247
+
248
+ const profilesSection = toml.profiles || {};
249
+ const tuning = resolveTuning(toml.tuning || {}, profilesSection);
250
+ const limitsSection = toml.limits || {};
251
+ const resolvedLimits = resolveLimits(limitsSection, tuning.profileName, profilesSection);
252
+
253
+ // Apply resolved limits to path objects
254
+ paths.features.limit = resolvedLimits.featuresLimit;
255
+ paths.library.limit = resolvedLimits.libraryLimit;
256
+
257
+ const execution = toml.execution || {};
258
+ const bot = toml.bot || {};
259
+
260
+ // Mission-complete thresholds (with safe defaults)
261
+ const mc = toml["mission-complete"] || {};
262
+ const missionCompleteThresholds = {
263
+ minResolvedIssues: mc["min-resolved-issues"] ?? 3,
264
+ requireDedicatedTests: mc["require-dedicated-tests"] ?? true,
265
+ maxSourceTodos: mc["max-source-todos"] ?? 0,
266
+ };
267
+
268
+ return {
269
+ supervisor: toml.schedule?.supervisor || "daily",
270
+ model: toml.tuning?.model || toml.schedule?.model || "gpt-5-mini",
271
+ tuning,
272
+ paths,
273
+ testScript: execution.test || "npm ci && npm test",
274
+ featureDevelopmentIssuesWipLimit: resolvedLimits.featureIssues,
275
+ maintenanceIssuesWipLimit: resolvedLimits.maintenanceIssues,
276
+ attemptsPerBranch: resolvedLimits.attemptsPerBranch,
277
+ attemptsPerIssue: resolvedLimits.attemptsPerIssue,
278
+ transformationBudget: tuning.transformationBudget,
279
+ seeding: toml.seeding || {},
280
+ intentionBot: {
281
+ intentionFilepath: bot["log-file"] || "intentïon.md",
282
+ },
283
+ init: toml.init || null,
284
+ tdd: toml.tdd === true,
285
+ missionCompleteThresholds,
286
+ writablePaths,
287
+ readOnlyPaths,
288
+ configToml: rawToml,
289
+ packageJson: readPackageJson(tomlPath, mergedPaths.dependencies),
290
+ };
291
+ }
292
+
293
+ /**
294
+ * Get the writable paths from config, optionally overridden by an input string.
295
+ *
296
+ * @param {AgenticConfig} config - Parsed config
297
+ * @param {string} [override] - Semicolon-separated override paths
298
+ * @returns {string[]} Array of writable paths
299
+ */
300
+ export function getWritablePaths(config, override) {
301
+ if (override) {
302
+ return override
303
+ .split(";")
304
+ .map((p) => p.trim())
305
+ .filter(Boolean);
306
+ }
307
+ return config.writablePaths;
308
+ }