@kitschpatrol/chagent 0.1.0 → 0.3.0

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/dist/bin/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { C as version, S as maxTimeoutMs, b as defaultModels, i as generateChangelog, l as agentNames, n as previewChangelog, r as reviewChangelog, s as writeChangelog, t as assertReviewTerminal, u as efforts, x as defaults, y as setLogger } from "../review-LYSADnul.js";
3
- import { n as assertAmendable, t as amendRelease } from "../lib-CXMRRbJf.js";
2
+ import { C as version, S as maxTimeoutMinutes, b as defaultModels, i as generateChangelog, l as agentNames, n as previewChangelog, r as reviewChangelog, s as writeChangelog, t as assertReviewTerminal, u as efforts, x as defaults, y as setLogger } from "../review-ClBbl31P.js";
3
+ import { n as assertAmendable, t as amendRelease } from "../lib-advgvAk1.js";
4
4
  import { t as createProgressLine } from "../progress-DQtRnH1N.js";
5
5
  import { createLogger } from "lognow";
6
6
  import path from "node:path";
@@ -122,15 +122,15 @@ try {
122
122
  description: "Configuration file path",
123
123
  type: "string"
124
124
  }).option("timeout", {
125
- defaultDescription: String(defaults.timeoutMs / 1e3),
126
- description: "Agent timeout in seconds",
125
+ defaultDescription: String(defaults.timeout),
126
+ description: "Agent timeout in minutes",
127
127
  type: "number"
128
128
  }).option("verbose", {
129
129
  default: false,
130
130
  description: "Log commands, cache decisions, and session details to stderr",
131
131
  type: "boolean"
132
132
  }).check((args) => {
133
- if (args.timeout !== void 0 && (!Number.isFinite(args.timeout) || args.timeout <= 0 || args.timeout * 1e3 > 2147483647)) throw new Error(`--timeout must be a positive number of seconds, up to ${Math.floor(maxTimeoutMs / 1e3)}.`);
133
+ if (args.timeout !== void 0 && (!Number.isFinite(args.timeout) || args.timeout <= 0 || args.timeout > 35791.39411666667)) throw new Error(`--timeout must be a positive number of minutes, up to ${Math.floor(maxTimeoutMinutes)}.`);
134
134
  return true;
135
135
  }), async (args) => {
136
136
  if (!args.dryRun && args.interactive) assertReviewTerminal();
@@ -149,7 +149,7 @@ try {
149
149
  },
150
150
  rebuild: args.rebuild,
151
151
  signal: controller.signal,
152
- timeoutMs: args.timeout === void 0 ? void 0 : args.timeout * 1e3
152
+ timeout: args.timeout
153
153
  }));
154
154
  try {
155
155
  if (args.dryRun) {
@@ -1,4 +1,4 @@
1
- import { u as GenerateOptions } from "../types-Cms1Tu5k.js";
1
+ import { u as GenerateOptions } from "../types-COQP1I5w.js";
2
2
  import { VersionBumpOptions } from "bumpp";
3
3
  type ExecuteHook = Exclude<NonNullable<VersionBumpOptions['execute']>, string>;
4
4
  export type BumppHookOptions = Omit<GenerateOptions, 'cwd' | 'release'> & {
package/dist/lib/bumpp.js CHANGED
@@ -1,4 +1,4 @@
1
- import { g as git, i as generateChangelog, m as localDate, r as reviewChangelog, s as writeChangelog, t as assertReviewTerminal } from "../review-LYSADnul.js";
1
+ import { g as git, i as generateChangelog, m as localDate, r as reviewChangelog, s as writeChangelog, t as assertReviewTerminal } from "../review-ClBbl31P.js";
2
2
  import { t as createProgressLine } from "../progress-DQtRnH1N.js";
3
3
 
4
4
  // src/lib/bumpp.ts
@@ -1,4 +1,4 @@
1
- import { _ as Config, a as ChangeSet, c as Evidence, d as GenerationRequest, f as GenerationResponse, g as RunSettings, h as ReleaseSummary, i as Change, l as FileEdit, m as ReleaseRequest, n as AgentName, o as ChangelogPlan, p as ReleaseContext, r as Category, s as Effort, t as AgentAdapter, u as GenerateOptions, v as defineConfig, y as loadConfig } from "../types-Cms1Tu5k.js";
1
+ import { _ as Config, a as ChangeSet, c as Evidence, d as GenerationRequest, f as GenerationResponse, g as RunSettings, h as ReleaseSummary, i as Change, l as FileEdit, m as ReleaseRequest, n as AgentName, o as ChangelogPlan, p as ReleaseContext, r as Category, s as Effort, t as AgentAdapter, u as GenerateOptions, v as defineConfig, y as loadConfig } from "../types-COQP1I5w.js";
2
2
  import { ILogBasic, ILogLayer } from "lognow";
3
3
  /** Create an adapter for an installed CLI using its existing authentication. */
4
4
  export declare function createAgent(name: AgentName): AgentAdapter;
package/dist/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as defineConfig, c as createAgent, i as generateChangelog, n as previewChangelog, o as loadConfig, r as reviewChangelog, s as writeChangelog, y as setLogger } from "../review-LYSADnul.js";
2
- import { t as amendRelease } from "../lib-CXMRRbJf.js";
1
+ import { a as defineConfig, c as createAgent, i as generateChangelog, n as previewChangelog, o as loadConfig, r as reviewChangelog, s as writeChangelog, y as setLogger } from "../review-ClBbl31P.js";
2
+ import { t as amendRelease } from "../lib-advgvAk1.js";
3
3
 
4
4
  export { amendRelease, createAgent, defineConfig, generateChangelog, loadConfig, previewChangelog, reviewChangelog, setLogger, writeChangelog };
@@ -1,4 +1,4 @@
1
- import { _ as isAncestor, d as LOCKFILE_PATH, f as inspectRepository, g as git, h as manifestAt, p as isVersionOnlyChange, s as writeChangelog, v as runCommand } from "./review-LYSADnul.js";
1
+ import { _ as isAncestor, d as LOCKFILE_PATH, f as inspectRepository, g as git, h as manifestAt, p as isVersionOnlyChange, s as writeChangelog, v as runCommand } from "./review-ClBbl31P.js";
2
2
  import path from "node:path";
3
3
  import { realpath } from "node:fs/promises";
4
4
 
@@ -15,7 +15,7 @@ import { isCancel, select } from "@clack/prompts";
15
15
 
16
16
  // package.json
17
17
  var name = "@kitschpatrol/chagent";
18
- var version = "0.1.0";
18
+ var version = "0.3.0";
19
19
  var engines = { "node": "^24.16.0 || >=26.3.0" };
20
20
 
21
21
  // src/lib/defaults.ts
@@ -25,14 +25,14 @@ const defaults = {
25
25
  effort: "xhigh",
26
26
  github: true,
27
27
  maxInputChars: 8e4,
28
- timeoutMs: 12e5
28
+ timeout: 20
29
29
  };
30
30
  const defaultModels = {
31
31
  claude: "claude-opus-5-5",
32
32
  codex: "gpt-6.1-sol"
33
33
  };
34
34
  /** Node.js timers fire immediately when given a longer delay. */
35
- const maxTimeoutMs = 2147483647;
35
+ const maxTimeoutMinutes = 2147483647 / 6e4;
36
36
 
37
37
  // src/lib/log.ts
38
38
  /**
@@ -119,7 +119,8 @@ async function runCommand(command, args, options = {}) {
119
119
  detached,
120
120
  env: options.env ?? process.env,
121
121
  stdio: options.inherit === true ? "inherit" : "pipe",
122
- windowsHide: true
122
+ windowsHide: true,
123
+ windowsVerbatimArguments: options.windowsVerbatimArguments
123
124
  });
124
125
  const group = detached ? child.pid : void 0;
125
126
  if (group !== void 0) trackGroup(group);
@@ -228,8 +229,11 @@ async function readBlobs(root, objects) {
228
229
  }
229
230
 
230
231
  // src/lib/repository.ts
231
- /** Matches package manager lockfile paths. */
232
- const LOCKFILE_PATH = /(?:^|\/)(?:pnpm-lock\.yaml|package-lock\.json|yarn\.lock|bun\.lockb?)$/v;
232
+ /**
233
+ * Matches package manager lockfile paths. Excludes npm-shrinkwrap.json, which
234
+ * is published and constrains consumers.
235
+ */
236
+ const LOCKFILE_PATH = /(?:^|\/)(?:pnpm-lock\.yaml|package-lock\.json|yarn\.lock|bun\.lockb?|deno\.lock|uv\.lock|poetry\.lock|pdm\.lock|Pipfile\.lock|Cargo\.lock|Gemfile\.lock|composer\.lock|go\.sum)$/v;
233
237
  const TAG_DECORATION = "tag: refs/tags/";
234
238
  const GITHUB_REMOTE = /github\.com[:\/]([^\/]+\/[^\/]+?)(?:\.git)?$/v;
235
239
  const manifestSchema$1 = z.object({
@@ -627,7 +631,11 @@ runtime compatibility, installation and distribution, documentation and examples
627
631
  dependencies, builds, tests, CI, and release tooling. Include meaningful maintenance
628
632
  even when it is the only change in a release. Group routine runtime dependency
629
633
  updates into a useful entry, and group development dependency/tooling refreshes
630
- instead of listing every version. Identify important packages or migrations when
634
+ instead of listing every version. A dependency change is a change to a declared
635
+ requirement, such as a version range in a manifest, workspace catalog, or
636
+ override. Lockfiles only record resolved versions: omit refreshed resolutions and
637
+ transitive updates that appear only in a lockfile, even when the same commit
638
+ changes other files. Identify important packages or migrations when
631
639
  the evidence supports them. Classify dependency and tooling changes by their
632
640
  actual impact: fixing runtime compatibility for consumers is Fixed, while fixing
633
641
  compatibility of contributor tooling alone is Maintenance. Documentation
@@ -702,8 +710,9 @@ Before returning, check coverage across behavior/API, compatibility, dependencie
702
710
  documentation, build/test/CI, and distribution. Do not drop a change just because
703
711
  its commit is called chore, docs, build, or dependency updates. Return an empty
704
712
  changes array only when the evidence is entirely release-version bookkeeping,
705
- generated artifacts, formatting noise, or changes already covered by existing
706
- entries. Do not pad the result with unsupported changes.`;
713
+ lockfile-only resolution changes, generated artifacts, formatting noise, or
714
+ changes already covered by existing entries. Do not pad the result with
715
+ unsupported changes.`;
707
716
  /** Invalidates cached drafts whenever the instructions change. */
708
717
  const instructionVersion = hash(instructions);
709
718
 
@@ -1055,7 +1064,8 @@ const configSchema = z.strictObject({
1055
1064
  packages: z.array(z.string()).optional(),
1056
1065
  /** Templates containing {version} and optionally {name}. */
1057
1066
  tagPatterns: z.array(z.string().includes("{version}")).optional(),
1058
- timeoutMs: z.number().positive().max(maxTimeoutMs).optional()
1067
+ /** Minutes each agent session may run. */
1068
+ timeout: z.number().positive().max(maxTimeoutMinutes).optional()
1059
1069
  });
1060
1070
  const resolvedConfigSchema = configSchema.required({
1061
1071
  agent: true,
@@ -1063,8 +1073,11 @@ const resolvedConfigSchema = configSchema.required({
1063
1073
  effort: true,
1064
1074
  github: true,
1065
1075
  maxInputChars: true,
1066
- timeoutMs: true
1067
- });
1076
+ timeout: true
1077
+ }).transform(({ timeout, ...config }) => ({
1078
+ ...config,
1079
+ timeoutMs: timeout * 6e4
1080
+ }));
1068
1081
  function invalidConfig(source, error) {
1069
1082
  return new Error(`Invalid options in ${source}:\n${z.prettifyError(error)}`, { cause: error });
1070
1083
  }
@@ -1310,12 +1323,13 @@ const GENERATED_DIRECTORY = /^(?:build|changelog|coverage|dist)(?:\/|$)/iv;
1310
1323
  const GENERATED_ANYWHERE = /(?:^|\/)(?:changelog(?:\.md)?$|node_modules(?:\/|$))/iv;
1311
1324
  /**
1312
1325
  * Generated output lives at the top of a package, so `packages/build` and
1313
- * `src/dist` remain source.
1326
+ * `src/dist` remain source. Lockfiles record resolved versions, not declared
1327
+ * dependency ranges.
1314
1328
  */
1315
1329
  function ignored(file, packages) {
1316
1330
  const owner = packages.findLast(({ directory }) => file.startsWith(`${directory}/`));
1317
1331
  const relative = owner === void 0 ? file : file.slice(owner.directory.length + 1);
1318
- return GENERATED_ANYWHERE.test(file) || GENERATED_DIRECTORY.test(relative);
1332
+ return LOCKFILE_PATH.test(file) || GENERATED_ANYWHERE.test(file) || GENERATED_DIRECTORY.test(relative);
1319
1333
  }
1320
1334
  async function scopes(repository, pkg, ref) {
1321
1335
  if (pkg.directory === ".") return ["."];
@@ -1344,11 +1358,7 @@ function manifestObjects(files, before, after) {
1344
1358
  return files.flatMap((file) => isManifest(file) ? [`${before}:${file}`, `${after}:${file}`] : []);
1345
1359
  }
1346
1360
  function hasSourceChanges(files, commit, blobs) {
1347
- for (const file of files) {
1348
- if (LOCKFILE_PATH.test(file)) continue;
1349
- if (!isManifest(file) || !isVersionOnlyEdit(blobs.get(`${commit}^:${file}`), blobs.get(`${commit}:${file}`))) return true;
1350
- }
1351
- return files.length > 0 && files.every((file) => LOCKFILE_PATH.test(file));
1361
+ return files.some((file) => !isManifest(file) || !isVersionOnlyEdit(blobs.get(`${commit}^:${file}`), blobs.get(`${commit}:${file}`)));
1352
1362
  }
1353
1363
  /** Read commits and their changed files from one process. */
1354
1364
  async function commitsInRange(root, range, paths) {
@@ -1827,7 +1837,7 @@ function releaseContext(options, pkg) {
1827
1837
  return pkg.directory === options.releaseDirectory ? options.release : void 0;
1828
1838
  }
1829
1839
  /** Resolve a package's releases and prepare requests for those it still needs. */
1830
- async function preparePackage(context, pkg, aggregateHistory = false, aggregateContent) {
1840
+ async function preparePackage(context, pkg, aggregateHistory = false, recordedAtRoot) {
1831
1841
  const { repository } = context;
1832
1842
  const file = await findChangelog(path.join(repository.root, pkg.directory));
1833
1843
  const content = context.options.rebuild ? void 0 : file.content;
@@ -1854,7 +1864,7 @@ async function preparePackage(context, pkg, aggregateHistory = false, aggregateC
1854
1864
  version: release.version
1855
1865
  };
1856
1866
  summaries.push(summary);
1857
- if (!shouldWrite && aggregateContent !== void 0 && missingPackageSummaries([summary], aggregateContent).length === 0) continue;
1867
+ if (!shouldWrite && recordedAtRoot?.(summary) === true) continue;
1858
1868
  const existingBody = (draftPromoted ? release === newest : release.version === "Unreleased") ? draft?.body : void 0;
1859
1869
  const from = existingBody === void 0 ? void 0 : await incrementalBase(context, file.path, content, release);
1860
1870
  const request = await prepareRelease(context, pkg, release, existingBody ?? "", mode, from);
@@ -2078,9 +2088,12 @@ async function generateWorkspace(context) {
2078
2088
  const rootReleases = await resolveReleases(repository, repository.rootPackage, options, releaseContext(options, repository.rootPackage));
2079
2089
  const shared = rootReleases.some((release) => release.version !== "Unreleased");
2080
2090
  const rootFile = await findChangelog(repository.root);
2081
- const needsPackageHistory = !shared || missingReleases(rootReleases, options.rebuild ? void 0 : rootFile.content).some((release) => release.version !== "Unreleased");
2091
+ const rootContent = options.rebuild ? void 0 : rootFile.content;
2092
+ const rootMissing = shared ? new Set(missingReleases(rootReleases, rootContent).map((release) => release.version)) : void 0;
2093
+ const needsPackageHistory = rootMissing === void 0 || rootMissing.values().some((version) => version !== "Unreleased");
2094
+ const recordedAtRoot = (summary) => rootMissing === void 0 ? rootContent !== void 0 && missingPackageSummaries([summary], rootContent).length === 0 : !rootMissing.has(summary.version);
2082
2095
  const packages = [];
2083
- for (const pkg of repository.packages) packages.push(await preparePackage(context, pkg, needsPackageHistory, shared || options.rebuild ? void 0 : rootFile.content));
2096
+ for (const pkg of repository.packages) packages.push(await preparePackage(context, pkg, needsPackageHistory, recordedAtRoot));
2084
2097
  const root = shared ? await preparePackage(context, repository.rootPackage) : void 0;
2085
2098
  const independent = shared ? void 0 : await prepareIndependent(context, packages.flatMap((pkg) => pkg.summaries));
2086
2099
  const requests = [...packages, ...root === void 0 ? [] : [root]].flatMap((pkg) => pkg.work.map((item) => item.request));
@@ -2231,21 +2244,23 @@ function changelogProblem(plan) {
2231
2244
  }
2232
2245
  }
2233
2246
  async function openEditor(editor, file, cwd) {
2234
- const args = process.platform === "win32" ? [
2247
+ const windows = process.platform === "win32";
2248
+ const args = windows ? [
2235
2249
  "/d",
2236
2250
  "/s",
2237
2251
  "/c",
2238
- `${editor} "${file}"`
2252
+ `"${editor} "${file}""`
2239
2253
  ] : [
2240
2254
  "-c",
2241
2255
  `${editor} "$1"`,
2242
2256
  "chagent-editor",
2243
2257
  file
2244
2258
  ];
2245
- await runCommand(process.platform === "win32" ? "cmd.exe" : "sh", args, {
2259
+ await runCommand(windows ? "cmd.exe" : "sh", args, {
2246
2260
  cwd,
2247
2261
  inherit: true,
2248
- timeoutMs: 864e5
2262
+ timeoutMs: 864e5,
2263
+ windowsVerbatimArguments: windows
2249
2264
  });
2250
2265
  }
2251
2266
  /**
@@ -2307,4 +2322,4 @@ async function reviewChangelog(plan, options = {}) {
2307
2322
  }
2308
2323
  }
2309
2324
 
2310
- export { version as C, maxTimeoutMs as S, isAncestor as _, defineConfig as a, defaultModels as b, createAgent as c, LOCKFILE_PATH as d, inspectRepository as f, git as g, manifestAt as h, generateChangelog as i, agentNames as l, localDate as m, previewChangelog as n, loadConfig as o, isVersionOnlyChange as p, reviewChangelog as r, writeChangelog as s, assertReviewTerminal as t, efforts as u, runCommand as v, defaults as x, setLogger as y };
2325
+ export { version as C, maxTimeoutMinutes as S, isAncestor as _, defineConfig as a, defaultModels as b, createAgent as c, LOCKFILE_PATH as d, inspectRepository as f, git as g, manifestAt as h, generateChangelog as i, agentNames as l, localDate as m, previewChangelog as n, loadConfig as o, isVersionOnlyChange as p, reviewChangelog as r, writeChangelog as s, assertReviewTerminal as t, efforts as u, runCommand as v, defaults as x, setLogger as y };
@@ -18,7 +18,7 @@ declare const configSchema: z.ZodObject<{
18
18
  model: z.ZodOptional<z.ZodString>;
19
19
  packages: z.ZodOptional<z.ZodArray<z.ZodString>>;
20
20
  tagPatterns: z.ZodOptional<z.ZodArray<z.ZodString>>;
21
- timeoutMs: z.ZodOptional<z.ZodNumber>;
21
+ timeout: z.ZodOptional<z.ZodNumber>;
22
22
  }, z.core.$strict>;
23
23
  type Config = z.infer<typeof configSchema>;
24
24
  /** Define optional repository configuration with TypeScript completion. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitschpatrol/chagent",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Changelog agent. CLI tool and TypeScript library to generate and update changelogs with a little help from your LLMs.",
5
5
  "keywords": [
6
6
  "changelog",
@@ -90,7 +90,7 @@
90
90
  "clean-deep": "shx rm -f pnpm-lock.yaml && git clean -fdX",
91
91
  "fix": "pnpm build && ksc fix",
92
92
  "lint": "pnpm build && ksc lint",
93
- "release": "bumpp --all --commit 'Release: %s' --execute 'node dist/bin/cli.js' && pnpm build && pnpm publish --otp $(op item get npm --otp) && brewpub --tap kitschpatrol/tap --description 'Generate and update changelogs from Git history with Codex or Claude'",
93
+ "release": "pnpm build && bumpp --all --commit 'Release: %s' --execute 'node dist/bin/cli.js' && pnpm build && pnpm publish --otp $(op item get npm --otp) && brewpub --tap kitschpatrol/tap --description 'Generate and update changelogs from Git history with Codex or Claude'",
94
94
  "test": "pnpm build && vitest run"
95
95
  }
96
96
  }
package/readme.md CHANGED
@@ -15,6 +15,10 @@
15
15
 
16
16
  <!-- /short-description -->
17
17
 
18
+ > [!WARNING]
19
+ >
20
+ > **This project is under development. It should not be considered suitable for general use until a 1.0 release.**
21
+
18
22
  ## Overview
19
23
 
20
24
  Run `chagent` in a Git repository to either create a changelog where none previously existed, or update an existing changelog to capture recent changes.
@@ -142,7 +146,7 @@ chagent
142
146
  | `--github` | Enrich Git evidence using gh when available; disable with --no-github | `boolean` | `true` |
143
147
  | `--cache` | Cache drafts and incremental state; disable with --no-cache | `boolean` | `true` |
144
148
  | `--config` | Configuration file path | `string` | auto-detected from repository root |
145
- | `--timeout` | Agent timeout in seconds | `number` | `1200` |
149
+ | `--timeout` | Agent timeout in minutes | `number` | `20` |
146
150
  | `--verbose` | Log commands, cache decisions, and session details to stderr | `boolean` | `false` |
147
151
  | `--help`<br>`-h` | Show help | `boolean` | |
148
152
  | `--version`<br>`-v` | Show version number | `boolean` | |
@@ -262,7 +266,7 @@ export default defineConfig({
262
266
  github: true,
263
267
  maxInputChars: 80_000,
264
268
  model: 'gpt-6.1-sol',
265
- timeoutMs: 1_200_000,
269
+ timeout: 20,
266
270
  })
267
271
  ```
268
272
 
@@ -280,7 +284,7 @@ Chagent reads reachable version tags and commit history regardless of the projec
280
284
 
281
285
  GitHub enrichment uses release dates and commit-associated PR information. PR lookups batch unique commits across all requested releases and packages; each release receives only its associated PRs. **GitHub release-note bodies are never generation inputs.** Changelog files and generated build output are excluded from source evidence. Existing changelog prose is supplied only to preserve edits and avoid duplicate entries.
282
286
 
283
- The agent chooses which historical source files, public interfaces, documentation, and build configuration to inspect. It reads Git snapshots at the supplied release refs instead of treating current or uncommitted files as historical evidence. `maxInputChars` bounds the starting context supplied to agents; it does not restrict which repository files they can investigate. Requests prioritize release boundaries and existing human entries, then fit optional starting evidence into the remaining budget. An individual release's existing entries are never truncated. `timeoutMs` applies to each agent session. Lockfile maintenance retains its commit intent, while pure npm version-bump bookkeeping is excluded.
287
+ The agent chooses which historical source files, public interfaces, documentation, and build configuration to inspect. It reads Git snapshots at the supplied release refs instead of treating current or uncommitted files as historical evidence. `maxInputChars` bounds the starting context supplied to agents; it does not restrict which repository files they can investigate. Requests prioritize release boundaries and existing human entries, then fit optional starting evidence into the remaining budget. An individual release's existing entries are never truncated. `timeout` sets the minutes each agent session may run. Dependency entries reflect declared version ranges; lockfile-only resolution changes and pure npm version-bump bookkeeping are excluded.
284
288
 
285
289
  Agents return structured changes with evidence references. Stray whitespace and list markers in descriptions are normalized. A response that still fails validation gets one correction request; a second failure stops the run before anything is written or cached. Application code owns the header, release boundaries, Markdown insertion, and Git writes. Agents run in the repository: Codex uses a read-only sandbox with approval requests disabled; Claude exposes read/search and Bash tools in [`dontAsk` mode](https://code.claude.com/docs/en/permissions), which allows read-only commands and denies actions requiring permission. Customizations are disabled for these invocations. Only chagent writes the reviewed changelog. Repository content is sent to the selected agent service using your existing account.
286
290