@kitschpatrol/chagent 0.2.0 → 0.4.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 +14 -2
- package/dist/lib/bumpp.d.ts +1 -1
- package/dist/lib/bumpp.js +1 -1
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.js +2 -2
- package/dist/{lib-DTp3sA-Q.js → lib-Byxdn2st.js} +1 -1
- package/dist/{review-BwgeC2dB.js → review-DftIAoEl.js} +102 -40
- package/dist/{types-COQP1I5w.d.ts → types-B6FOOkTQ.d.ts} +9 -1
- package/package.json +1 -1
- package/readme.md +27 -20
package/dist/bin/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as
|
|
3
|
-
import { n as assertAmendable, t as amendRelease } from "../lib-
|
|
2
|
+
import { C as maxTimeoutMinutes, S as defaults, b as setLogger, d as rootChangelogs, i as generateChangelog, l as agentNames, n as previewChangelog, r as reviewChangelog, s as writeChangelog, t as assertReviewTerminal, u as efforts, w as version, x as defaultModels } from "../review-DftIAoEl.js";
|
|
3
|
+
import { n as assertAmendable, t as amendRelease } from "../lib-Byxdn2st.js";
|
|
4
4
|
import { t as createProgressLine } from "../progress-DQtRnH1N.js";
|
|
5
5
|
import { createLogger } from "lognow";
|
|
6
6
|
import path from "node:path";
|
|
@@ -109,6 +109,16 @@ try {
|
|
|
109
109
|
default: false,
|
|
110
110
|
description: "Amend a verified unpublished release commit and matching local tags",
|
|
111
111
|
type: "boolean"
|
|
112
|
+
}).option("root-changelog", {
|
|
113
|
+
choices: rootChangelogs,
|
|
114
|
+
defaultDescription: defaults.rootChangelog,
|
|
115
|
+
description: "Monorepo root changelog: a summary of package releases, the root package alone, or none",
|
|
116
|
+
type: "string"
|
|
117
|
+
}).option("skip-package", {
|
|
118
|
+
array: true,
|
|
119
|
+
defaultDescription: "none",
|
|
120
|
+
description: "Workspace package directory glob to leave without a changelog; repeatable",
|
|
121
|
+
type: "string"
|
|
112
122
|
}).option("github", {
|
|
113
123
|
defaultDescription: String(defaults.github),
|
|
114
124
|
description: "Enrich Git evidence using gh when available; disable with --no-github",
|
|
@@ -148,7 +158,9 @@ try {
|
|
|
148
158
|
reporter.report(message);
|
|
149
159
|
},
|
|
150
160
|
rebuild: args.rebuild,
|
|
161
|
+
rootChangelog: args.rootChangelog,
|
|
151
162
|
signal: controller.signal,
|
|
163
|
+
skipPackage: args.skipPackage,
|
|
152
164
|
timeout: args.timeout
|
|
153
165
|
}));
|
|
154
166
|
try {
|
package/dist/lib/bumpp.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as GenerateOptions } from "../types-
|
|
1
|
+
import { u as GenerateOptions } from "../types-B6FOOkTQ.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 {
|
|
1
|
+
import { _ as git, h as localDate, i as generateChangelog, r as reviewChangelog, s as writeChangelog, t as assertReviewTerminal } from "../review-DftIAoEl.js";
|
|
2
2
|
import { t as createProgressLine } from "../progress-DQtRnH1N.js";
|
|
3
3
|
|
|
4
4
|
// src/lib/bumpp.ts
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as RunSettings, a as ChangeSet, b as loadConfig, c as Evidence, d as GenerationRequest, f as GenerationResponse, g as RootChangelog, 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 Config, y as defineConfig } from "../types-B6FOOkTQ.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;
|
|
@@ -28,4 +28,4 @@ export declare function reviewChangelog(plan: ChangelogPlan, options?: {
|
|
|
28
28
|
* writes.
|
|
29
29
|
*/
|
|
30
30
|
export declare function writeChangelog(plan: ChangelogPlan): Promise<string[]>;
|
|
31
|
-
export { type AgentAdapter, type AgentName, type Category, type Change, type ChangeSet, type ChangelogPlan, type Config, type Effort, type Evidence, type FileEdit, type GenerateOptions, type GenerationRequest, type GenerationResponse, type ReleaseContext, type ReleaseRequest, type ReleaseSummary, type RunSettings, defineConfig, loadConfig };
|
|
31
|
+
export { type AgentAdapter, type AgentName, type Category, type Change, type ChangeSet, type ChangelogPlan, type Config, type Effort, type Evidence, type FileEdit, type GenerateOptions, type GenerationRequest, type GenerationResponse, type ReleaseContext, type ReleaseRequest, type ReleaseSummary, type RootChangelog, type RunSettings, defineConfig, loadConfig };
|
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
|
|
2
|
-
import { t as amendRelease } from "../lib-
|
|
1
|
+
import { a as defineConfig, b as setLogger, c as createAgent, i as generateChangelog, n as previewChangelog, o as loadConfig, r as reviewChangelog, s as writeChangelog } from "../review-DftIAoEl.js";
|
|
2
|
+
import { t as amendRelease } from "../lib-Byxdn2st.js";
|
|
3
3
|
|
|
4
4
|
export { amendRelease, createAgent, defineConfig, generateChangelog, loadConfig, previewChangelog, reviewChangelog, setLogger, writeChangelog };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as git, f as LOCKFILE_PATH, g as manifestAt, m as isVersionOnlyChange, p as inspectRepository, s as writeChangelog, v as isAncestor, y as runCommand } from "./review-DftIAoEl.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.
|
|
18
|
+
var version = "0.4.0";
|
|
19
19
|
var engines = { "node": "^24.16.0 || >=26.3.0" };
|
|
20
20
|
|
|
21
21
|
// src/lib/defaults.ts
|
|
@@ -25,6 +25,7 @@ const defaults = {
|
|
|
25
25
|
effort: "xhigh",
|
|
26
26
|
github: true,
|
|
27
27
|
maxInputChars: 8e4,
|
|
28
|
+
rootChangelog: "summary",
|
|
28
29
|
timeout: 20
|
|
29
30
|
};
|
|
30
31
|
const defaultModels = {
|
|
@@ -229,8 +230,11 @@ async function readBlobs(root, objects) {
|
|
|
229
230
|
}
|
|
230
231
|
|
|
231
232
|
// src/lib/repository.ts
|
|
232
|
-
/**
|
|
233
|
-
|
|
233
|
+
/**
|
|
234
|
+
* Matches package manager lockfile paths. Excludes npm-shrinkwrap.json, which
|
|
235
|
+
* is published and constrains consumers.
|
|
236
|
+
*/
|
|
237
|
+
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;
|
|
234
238
|
const TAG_DECORATION = "tag: refs/tags/";
|
|
235
239
|
const GITHUB_REMOTE = /github\.com[:\/]([^\/]+\/[^\/]+?)(?:\.git)?$/v;
|
|
236
240
|
const manifestSchema$1 = z.object({
|
|
@@ -483,21 +487,15 @@ function pnpmWorkspaceGlobs(text) {
|
|
|
483
487
|
if (!workspace.success) throw new Error(`Invalid pnpm-workspace.yaml:\n${z.prettifyError(workspace.error)}`, { cause: workspace.error });
|
|
484
488
|
return workspace.data?.packages;
|
|
485
489
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
const workspaceText = await readOptional(path.join(root, "pnpm-workspace.yaml"));
|
|
489
|
-
if (workspaceText !== void 0) patterns = pnpmWorkspaceGlobs(workspaceText) ?? patterns;
|
|
490
|
-
patterns = config.packages ?? patterns;
|
|
490
|
+
/** Find directories matching globs, as POSIX paths relative to the root. */
|
|
491
|
+
async function matchDirectories(root, patterns, exclude = []) {
|
|
491
492
|
const directories = [];
|
|
492
|
-
const
|
|
493
|
-
const negative = patterns.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1));
|
|
494
|
-
const matches = glob(positive, {
|
|
493
|
+
const matches = glob(patterns, {
|
|
495
494
|
cwd: root,
|
|
496
495
|
exclude: [
|
|
497
496
|
"**/node_modules/**",
|
|
498
497
|
"**/.git/**",
|
|
499
|
-
...
|
|
500
|
-
...config.exclude ?? []
|
|
498
|
+
...exclude
|
|
501
499
|
],
|
|
502
500
|
withFileTypes: true
|
|
503
501
|
});
|
|
@@ -505,6 +503,27 @@ async function workspacePackages(root, manifest, config) {
|
|
|
505
503
|
if (!entry.isDirectory()) continue;
|
|
506
504
|
directories.push(path.relative(root, path.join(entry.parentPath, entry.name)).split(path.sep).join("/"));
|
|
507
505
|
}
|
|
506
|
+
return directories;
|
|
507
|
+
}
|
|
508
|
+
/** Mark the packages that remain boundaries but get no changelog. */
|
|
509
|
+
async function markSkipped(root, packages, patterns) {
|
|
510
|
+
const skipped = /* @__PURE__ */ new Set();
|
|
511
|
+
for (const pattern of patterns) {
|
|
512
|
+
const matches = (await matchDirectories(root, [pattern])).filter((directory) => packages.some((pkg) => pkg.directory === directory));
|
|
513
|
+
if (matches.length === 0) throw new Error(`skipPackage pattern "${pattern}" matches no workspace package directory. Use a directory glob relative to the repository root, such as "packages/docs"; rootChangelog controls the root changelog.`);
|
|
514
|
+
for (const directory of matches) skipped.add(directory);
|
|
515
|
+
}
|
|
516
|
+
return packages.map((pkg) => skipped.has(pkg.directory) ? {
|
|
517
|
+
...pkg,
|
|
518
|
+
skipped: true
|
|
519
|
+
} : pkg);
|
|
520
|
+
}
|
|
521
|
+
async function workspacePackages(root, manifest, config) {
|
|
522
|
+
let patterns = Array.isArray(manifest?.workspaces) ? manifest.workspaces : manifest?.workspaces?.packages ?? [];
|
|
523
|
+
const workspaceText = await readOptional(path.join(root, "pnpm-workspace.yaml"));
|
|
524
|
+
if (workspaceText !== void 0) patterns = pnpmWorkspaceGlobs(workspaceText) ?? patterns;
|
|
525
|
+
patterns = config.packages ?? patterns;
|
|
526
|
+
const directories = await matchDirectories(root, patterns.filter((pattern) => !pattern.startsWith("!")), [...patterns.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1)), ...config.exclude ?? []]);
|
|
508
527
|
const packages = [];
|
|
509
528
|
for (const directory of directories.toSorted()) {
|
|
510
529
|
if (!(await realpath(path.join(root, directory))).startsWith(root + path.sep)) throw new Error(`Workspace package escapes the repository: ${directory}`);
|
|
@@ -512,7 +531,11 @@ async function workspacePackages(root, manifest, config) {
|
|
|
512
531
|
if (text === void 0 && config.packages === void 0) continue;
|
|
513
532
|
packages.push(packageInfo(text === void 0 ? void 0 : parseManifest(text, `${directory}/package.json`), directory, path.posix.basename(directory)));
|
|
514
533
|
}
|
|
515
|
-
return packages;
|
|
534
|
+
return markSkipped(root, packages, config.skipPackage ?? []);
|
|
535
|
+
}
|
|
536
|
+
/** Select the workspace packages that get changelogs of their own. */
|
|
537
|
+
function describedPackages(repository) {
|
|
538
|
+
return repository.packages.filter((pkg) => pkg.skipped !== true);
|
|
516
539
|
}
|
|
517
540
|
async function taggedReleases(repository, pkg, config) {
|
|
518
541
|
const candidates = [];
|
|
@@ -628,7 +651,11 @@ runtime compatibility, installation and distribution, documentation and examples
|
|
|
628
651
|
dependencies, builds, tests, CI, and release tooling. Include meaningful maintenance
|
|
629
652
|
even when it is the only change in a release. Group routine runtime dependency
|
|
630
653
|
updates into a useful entry, and group development dependency/tooling refreshes
|
|
631
|
-
instead of listing every version.
|
|
654
|
+
instead of listing every version. A dependency change is a change to a declared
|
|
655
|
+
requirement, such as a version range in a manifest, workspace catalog, or
|
|
656
|
+
override. Lockfiles only record resolved versions: omit refreshed resolutions and
|
|
657
|
+
transitive updates that appear only in a lockfile, even when the same commit
|
|
658
|
+
changes other files. Identify important packages or migrations when
|
|
632
659
|
the evidence supports them. Classify dependency and tooling changes by their
|
|
633
660
|
actual impact: fixing runtime compatibility for consumers is Fixed, while fixing
|
|
634
661
|
compatibility of contributor tooling alone is Maintenance. Documentation
|
|
@@ -703,8 +730,9 @@ Before returning, check coverage across behavior/API, compatibility, dependencie
|
|
|
703
730
|
documentation, build/test/CI, and distribution. Do not drop a change just because
|
|
704
731
|
its commit is called chore, docs, build, or dependency updates. Return an empty
|
|
705
732
|
changes array only when the evidence is entirely release-version bookkeeping,
|
|
706
|
-
generated artifacts, formatting noise, or
|
|
707
|
-
entries. Do not pad the result with
|
|
733
|
+
lockfile-only resolution changes, generated artifacts, formatting noise, or
|
|
734
|
+
changes already covered by existing entries. Do not pad the result with
|
|
735
|
+
unsupported changes.`;
|
|
708
736
|
/** Invalidates cached drafts whenever the instructions change. */
|
|
709
737
|
const instructionVersion = hash(instructions);
|
|
710
738
|
|
|
@@ -727,6 +755,11 @@ const efforts = [
|
|
|
727
755
|
"xhigh",
|
|
728
756
|
"max"
|
|
729
757
|
];
|
|
758
|
+
const rootChangelogs = [
|
|
759
|
+
"summary",
|
|
760
|
+
"package",
|
|
761
|
+
"none"
|
|
762
|
+
];
|
|
730
763
|
|
|
731
764
|
// src/lib/agents.ts
|
|
732
765
|
const CONTROL_CHARACTERS = /\p{Cc}/v;
|
|
@@ -1054,6 +1087,13 @@ const configSchema = z.strictObject({
|
|
|
1054
1087
|
model: z.string().min(1).optional(),
|
|
1055
1088
|
/** Workspace directory globs, overriding manifest workspace discovery. */
|
|
1056
1089
|
packages: z.array(z.string()).optional(),
|
|
1090
|
+
/**
|
|
1091
|
+
* What a monorepo's root changelog records: a summary of package releases,
|
|
1092
|
+
* the root package's own changes, or nothing.
|
|
1093
|
+
*/
|
|
1094
|
+
rootChangelog: z.enum(rootChangelogs).optional(),
|
|
1095
|
+
/** Workspace package directory globs that get no changelog of their own. */
|
|
1096
|
+
skipPackage: z.array(z.string()).optional(),
|
|
1057
1097
|
/** Templates containing {version} and optionally {name}. */
|
|
1058
1098
|
tagPatterns: z.array(z.string().includes("{version}")).optional(),
|
|
1059
1099
|
/** Minutes each agent session may run. */
|
|
@@ -1065,6 +1105,7 @@ const resolvedConfigSchema = configSchema.required({
|
|
|
1065
1105
|
effort: true,
|
|
1066
1106
|
github: true,
|
|
1067
1107
|
maxInputChars: true,
|
|
1108
|
+
rootChangelog: true,
|
|
1068
1109
|
timeout: true
|
|
1069
1110
|
}).transform(({ timeout, ...config }) => ({
|
|
1070
1111
|
...config,
|
|
@@ -1315,15 +1356,19 @@ const GENERATED_DIRECTORY = /^(?:build|changelog|coverage|dist)(?:\/|$)/iv;
|
|
|
1315
1356
|
const GENERATED_ANYWHERE = /(?:^|\/)(?:changelog(?:\.md)?$|node_modules(?:\/|$))/iv;
|
|
1316
1357
|
/**
|
|
1317
1358
|
* Generated output lives at the top of a package, so `packages/build` and
|
|
1318
|
-
* `src/dist` remain source.
|
|
1359
|
+
* `src/dist` remain source. Lockfiles record resolved versions, not declared
|
|
1360
|
+
* dependency ranges.
|
|
1319
1361
|
*/
|
|
1320
1362
|
function ignored(file, packages) {
|
|
1321
1363
|
const owner = packages.findLast(({ directory }) => file.startsWith(`${directory}/`));
|
|
1322
1364
|
const relative = owner === void 0 ? file : file.slice(owner.directory.length + 1);
|
|
1323
|
-
return GENERATED_ANYWHERE.test(file) || GENERATED_DIRECTORY.test(relative);
|
|
1365
|
+
return LOCKFILE_PATH.test(file) || GENERATED_ANYWHERE.test(file) || GENERATED_DIRECTORY.test(relative);
|
|
1324
1366
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1367
|
+
/**
|
|
1368
|
+
* A package's directory and, transitively, those of the workspace packages it
|
|
1369
|
+
* depends on at a ref.
|
|
1370
|
+
*/
|
|
1371
|
+
async function packageScope(repository, pkg, ref) {
|
|
1327
1372
|
const directories = /* @__PURE__ */ new Set([pkg.directory]);
|
|
1328
1373
|
const visit = async (current) => {
|
|
1329
1374
|
const manifest = await manifestAt(repository.root, current.directory, ref);
|
|
@@ -1349,11 +1394,7 @@ function manifestObjects(files, before, after) {
|
|
|
1349
1394
|
return files.flatMap((file) => isManifest(file) ? [`${before}:${file}`, `${after}:${file}`] : []);
|
|
1350
1395
|
}
|
|
1351
1396
|
function hasSourceChanges(files, commit, blobs) {
|
|
1352
|
-
|
|
1353
|
-
if (LOCKFILE_PATH.test(file)) continue;
|
|
1354
|
-
if (!isManifest(file) || !isVersionOnlyEdit(blobs.get(`${commit}^:${file}`), blobs.get(`${commit}:${file}`))) return true;
|
|
1355
|
-
}
|
|
1356
|
-
return files.length > 0 && files.every((file) => LOCKFILE_PATH.test(file));
|
|
1397
|
+
return files.some((file) => !isManifest(file) || !isVersionOnlyEdit(blobs.get(`${commit}^:${file}`), blobs.get(`${commit}:${file}`)));
|
|
1357
1398
|
}
|
|
1358
1399
|
/** Read commits and their changed files from one process. */
|
|
1359
1400
|
async function commitsInRange(root, range, paths) {
|
|
@@ -1397,7 +1438,7 @@ async function commitsInRange(root, range, paths) {
|
|
|
1397
1438
|
/** Collect source changes, excluding generated notes and release-only commits. */
|
|
1398
1439
|
async function collectEvidence(repository, pkg, release, excludeDirectories = []) {
|
|
1399
1440
|
const { packages, root } = repository;
|
|
1400
|
-
const paths = [...(await
|
|
1441
|
+
const paths = [...(pkg.directory === "." ? ["."] : await packageScope(repository, pkg, release.ref)).map((directory) => `:(literal)${directory}`), ...excludeDirectories.map((directory) => `:(exclude,literal)${directory}`)];
|
|
1401
1442
|
const range = release.previousRef === void 0 ? release.ref : `${release.previousRef}..${release.ref}`;
|
|
1402
1443
|
const from = release.previousRef ?? await git(root, [
|
|
1403
1444
|
"hash-object",
|
|
@@ -1637,7 +1678,7 @@ function researchEvidence(evidence, limit) {
|
|
|
1637
1678
|
async function prepareRelease(context, pkg, release, existingEntries, mode, from, onlyRepositoryFiles = false) {
|
|
1638
1679
|
const { options, repository } = context;
|
|
1639
1680
|
options.signal?.throwIfAborted();
|
|
1640
|
-
const excludeDirectories =
|
|
1681
|
+
const excludeDirectories = await excludedPackages(context, pkg, release.ref, onlyRepositoryFiles);
|
|
1641
1682
|
return {
|
|
1642
1683
|
evidence: await collectEvidence(repository, pkg, from === void 0 ? release : {
|
|
1643
1684
|
...release,
|
|
@@ -1654,10 +1695,24 @@ async function prepareRelease(context, pkg, release, existingEntries, mode, from
|
|
|
1654
1695
|
from: from ?? release.previousRef,
|
|
1655
1696
|
to: release.ref
|
|
1656
1697
|
},
|
|
1657
|
-
scope: pkg.directory === "." && repository.packages.length > 0 ? "repository" : "package",
|
|
1698
|
+
scope: pkg.directory === "." && repository.packages.length > 0 && options.rootChangelog === "summary" ? "repository" : "package",
|
|
1658
1699
|
version: release.version
|
|
1659
1700
|
};
|
|
1660
1701
|
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Select the package directories a release leaves out: every package when
|
|
1704
|
+
* describing repository files alone, skipped packages in a whole-repository
|
|
1705
|
+
* summary, or, when the root is a package itself, those it does not depend on.
|
|
1706
|
+
*/
|
|
1707
|
+
async function excludedPackages(context, pkg, ref, onlyRepositoryFiles) {
|
|
1708
|
+
const { options, repository } = context;
|
|
1709
|
+
const directories = repository.packages.map((item) => item.directory);
|
|
1710
|
+
if (onlyRepositoryFiles || directories.length === 0) return directories;
|
|
1711
|
+
if (pkg.directory !== ".") return [];
|
|
1712
|
+
if (options.rootChangelog === "summary") return repository.packages.filter((item) => item.skipped === true).map((item) => item.directory);
|
|
1713
|
+
const scope = new Set(await packageScope(repository, pkg, ref));
|
|
1714
|
+
return directories.filter((directory) => !scope.has(directory));
|
|
1715
|
+
}
|
|
1661
1716
|
function cacheKey(context, release) {
|
|
1662
1717
|
const { effort, maxInputChars, model } = context.options;
|
|
1663
1718
|
return JSON.stringify({
|
|
@@ -2071,7 +2126,7 @@ function aggregateShared(context, prepared, results, exception, quiet) {
|
|
|
2071
2126
|
const rootEdit = plan.files.find((file) => path.dirname(file.path) === repository.root);
|
|
2072
2127
|
if (rootEdit === void 0) return;
|
|
2073
2128
|
const additions = prepared.summaries.map((summary) => ({
|
|
2074
|
-
changes: aggregateChanges(repository, plan.releases.filter((item) => item.version === summary.version && !quiet.has(`${item.changelogPath}@${item.version}`))),
|
|
2129
|
+
changes: aggregateChanges(repository, plan.releases.filter((item) => item.version === summary.version && item.ref === summary.ref && !quiet.has(`${item.changelogPath}@${item.version}`))),
|
|
2075
2130
|
existingBody: parseChangelog(rootEdit.after).sections.find((section) => section.version === summary.version)?.body,
|
|
2076
2131
|
release: prepared.releases.find((item) => item.version === summary.version)
|
|
2077
2132
|
}));
|
|
@@ -2084,11 +2139,11 @@ async function generateWorkspace(context) {
|
|
|
2084
2139
|
const shared = rootReleases.some((release) => release.version !== "Unreleased");
|
|
2085
2140
|
const rootFile = await findChangelog(repository.root);
|
|
2086
2141
|
const rootContent = options.rebuild ? void 0 : rootFile.content;
|
|
2087
|
-
const rootMissing = shared ?
|
|
2088
|
-
const needsPackageHistory = rootMissing === void 0 || rootMissing.
|
|
2089
|
-
const recordedAtRoot = (summary) => rootMissing === void 0 ? rootContent !== void 0 && missingPackageSummaries([summary], rootContent).length === 0 :
|
|
2142
|
+
const rootMissing = shared ? missingReleases(rootReleases, rootContent) : void 0;
|
|
2143
|
+
const needsPackageHistory = rootMissing === void 0 || rootMissing.some((release) => release.version !== "Unreleased");
|
|
2144
|
+
const recordedAtRoot = (summary) => rootMissing === void 0 ? rootContent !== void 0 && missingPackageSummaries([summary], rootContent).length === 0 : rootMissing.every((release) => release.version !== summary.version || release.ref !== summary.ref);
|
|
2090
2145
|
const packages = [];
|
|
2091
|
-
for (const pkg of repository
|
|
2146
|
+
for (const pkg of describedPackages(repository)) packages.push(await preparePackage(context, pkg, needsPackageHistory, recordedAtRoot));
|
|
2092
2147
|
const root = shared ? await preparePackage(context, repository.rootPackage) : void 0;
|
|
2093
2148
|
const independent = shared ? void 0 : await prepareIndependent(context, packages.flatMap((pkg) => pkg.summaries));
|
|
2094
2149
|
const requests = [...packages, ...root === void 0 ? [] : [root]].flatMap((pkg) => pkg.work.map((item) => item.request));
|
|
@@ -2122,6 +2177,7 @@ async function generateChangelog(options = {}) {
|
|
|
2122
2177
|
log.debug(`Resolved options: ${JSON.stringify(config)}`);
|
|
2123
2178
|
const repository = await inspectRepository(root, config);
|
|
2124
2179
|
log.debug(`Repository ${root} at ${repository.head}: ${repository.tags.length} reachable tags, ${repository.packages.length} workspace packages.`);
|
|
2180
|
+
if (config.rootChangelog === "none" && describedPackages(repository).length === 0) throw new Error("The root changelog is set to \"none\", but no workspace packages remain to describe, so there is no changelog to generate. Choose another root changelog mode, or configure packages that are not skipped.");
|
|
2125
2181
|
const agent = typeof selected === "object" ? selected : createAgent(config.agent);
|
|
2126
2182
|
const plan = {
|
|
2127
2183
|
checkpoints: [],
|
|
@@ -2149,14 +2205,20 @@ async function generateChangelog(options = {}) {
|
|
|
2149
2205
|
plan,
|
|
2150
2206
|
repository
|
|
2151
2207
|
};
|
|
2152
|
-
if (repository.packages.length ===
|
|
2153
|
-
|
|
2154
|
-
const results = await generateReleases(context, prepared.work.map((item) => item.request));
|
|
2155
|
-
plan.releases.push(...renderPackage(context, prepared, results));
|
|
2156
|
-
} else await generateWorkspace(context);
|
|
2208
|
+
if (repository.packages.length > 0 && config.rootChangelog === "summary") await generateWorkspace(context);
|
|
2209
|
+
else await generatePackages(context);
|
|
2157
2210
|
if (rebuild) keepEmptyRebuilds(plan);
|
|
2158
2211
|
return plan;
|
|
2159
2212
|
}
|
|
2213
|
+
/** Generate each changelog on its own, with no root summary. */
|
|
2214
|
+
async function generatePackages(context) {
|
|
2215
|
+
const { options, plan, repository } = context;
|
|
2216
|
+
const packages = options.rootChangelog === "none" ? describedPackages(repository) : [repository.rootPackage, ...describedPackages(repository)];
|
|
2217
|
+
const prepared = [];
|
|
2218
|
+
for (const pkg of packages) prepared.push(await preparePackage(context, pkg));
|
|
2219
|
+
const results = await generateReleases(context, prepared.flatMap((item) => item.work.map((work) => work.request)));
|
|
2220
|
+
for (const item of prepared) plan.releases.push(...renderPackage(context, item, results));
|
|
2221
|
+
}
|
|
2160
2222
|
/** A rebuild that finds nothing to record must not erase an existing changelog. */
|
|
2161
2223
|
function keepEmptyRebuilds(plan) {
|
|
2162
2224
|
plan.files = plan.files.filter((edit) => {
|
|
@@ -2317,4 +2379,4 @@ async function reviewChangelog(plan, options = {}) {
|
|
|
2317
2379
|
}
|
|
2318
2380
|
}
|
|
2319
2381
|
|
|
2320
|
-
export {
|
|
2382
|
+
export { maxTimeoutMinutes as C, defaults as S, git as _, defineConfig as a, setLogger as b, createAgent as c, rootChangelogs as d, LOCKFILE_PATH as f, manifestAt as g, localDate as h, generateChangelog as i, agentNames as l, isVersionOnlyChange as m, previewChangelog as n, loadConfig as o, inspectRepository as p, reviewChangelog as r, writeChangelog as s, assertReviewTerminal as t, efforts as u, isAncestor as v, version as w, defaultModels as x, runCommand as y };
|
|
@@ -17,6 +17,12 @@ declare const configSchema: z.ZodObject<{
|
|
|
17
17
|
maxInputChars: z.ZodOptional<z.ZodNumber>;
|
|
18
18
|
model: z.ZodOptional<z.ZodString>;
|
|
19
19
|
packages: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
20
|
+
rootChangelog: z.ZodOptional<z.ZodEnum<{
|
|
21
|
+
summary: "summary";
|
|
22
|
+
package: "package";
|
|
23
|
+
none: "none";
|
|
24
|
+
}>>;
|
|
25
|
+
skipPackage: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
20
26
|
tagPatterns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
21
27
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
22
28
|
}, z.core.$strict>;
|
|
@@ -29,8 +35,10 @@ declare const categories: readonly ["Added", "Changed", "Deprecated", "Removed",
|
|
|
29
35
|
type Category = (typeof categories)[number];
|
|
30
36
|
declare const agentNames: readonly ["codex", "claude"];
|
|
31
37
|
declare const efforts: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
38
|
+
declare const rootChangelogs: readonly ["summary", "package", "none"];
|
|
32
39
|
type AgentName = (typeof agentNames)[number];
|
|
33
40
|
type Effort = (typeof efforts)[number];
|
|
41
|
+
type RootChangelog = (typeof rootChangelogs)[number];
|
|
34
42
|
type Change = {
|
|
35
43
|
breaking: boolean;
|
|
36
44
|
category: Category;
|
|
@@ -159,4 +167,4 @@ type ChangelogPlan = {
|
|
|
159
167
|
releases: ReleaseSummary[];
|
|
160
168
|
root: string;
|
|
161
169
|
};
|
|
162
|
-
export {
|
|
170
|
+
export { RunSettings as _, ChangeSet as a, loadConfig as b, Evidence as c, GenerationRequest as d, GenerationResponse as f, RootChangelog as g, ReleaseSummary as h, Change as i, FileEdit as l, ReleaseRequest as m, AgentName as n, ChangelogPlan as o, ReleaseContext as p, Category as r, Effort as s, AgentAdapter as t, GenerateOptions as u, Config as v, defineConfig as y };
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -132,24 +132,26 @@ Usage:
|
|
|
132
132
|
chagent
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
-
| Option | Description
|
|
136
|
-
| ----------------------- |
|
|
137
|
-
| `--cwd` | Repository directory
|
|
138
|
-
| `--agent` | Installed agent CLI
|
|
139
|
-
| `--model` | Agent model
|
|
140
|
-
| `--effort` | Reasoning effort
|
|
141
|
-
| `--interactive`<br>`-i` | Review a diff and optionally edit before writing
|
|
142
|
-
| `--editor` | Editor command for interactive review; configure GUI editors to wait
|
|
143
|
-
| `--dry-run` | Print a diff without applying changes
|
|
144
|
-
| `--rebuild` | Reconstruct all history, replacing existing release text
|
|
145
|
-
| `--amend` | Amend a verified unpublished release commit and matching local tags
|
|
146
|
-
| `--
|
|
147
|
-
| `--
|
|
148
|
-
| `--
|
|
149
|
-
| `--
|
|
150
|
-
| `--
|
|
151
|
-
| `--
|
|
152
|
-
| `--
|
|
135
|
+
| Option | Description | Type | Default |
|
|
136
|
+
| ----------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------- | --------------------------------------------- |
|
|
137
|
+
| `--cwd` | Repository directory | `string` | current directory |
|
|
138
|
+
| `--agent` | Installed agent CLI | `"codex"` `"claude"` | `codex` |
|
|
139
|
+
| `--model` | Agent model | `string` | gpt-6.1-sol (Codex); claude-opus-5-5 (Claude) |
|
|
140
|
+
| `--effort` | Reasoning effort | `"low"` `"medium"` `"high"` `"xhigh"` `"max"` | `xhigh` |
|
|
141
|
+
| `--interactive`<br>`-i` | Review a diff and optionally edit before writing | `boolean` | `false` |
|
|
142
|
+
| `--editor` | Editor command for interactive review; configure GUI editors to wait | `string` | git var GIT_EDITOR |
|
|
143
|
+
| `--dry-run` | Print a diff without applying changes | `boolean` | `false` |
|
|
144
|
+
| `--rebuild` | Reconstruct all history, replacing existing release text | `boolean` | `false` |
|
|
145
|
+
| `--amend` | Amend a verified unpublished release commit and matching local tags | `boolean` | `false` |
|
|
146
|
+
| `--root-changelog` | Monorepo root changelog: a summary of package releases, the root package alone, or none | `"summary"` `"package"` `"none"` | `summary` |
|
|
147
|
+
| `--skip-package` | Workspace package directory glob to leave without a changelog; repeatable | `array` | `none` |
|
|
148
|
+
| `--github` | Enrich Git evidence using gh when available; disable with --no-github | `boolean` | `true` |
|
|
149
|
+
| `--cache` | Cache drafts and incremental state; disable with --no-cache | `boolean` | `true` |
|
|
150
|
+
| `--config` | Configuration file path | `string` | auto-detected from repository root |
|
|
151
|
+
| `--timeout` | Agent timeout in minutes | `number` | `20` |
|
|
152
|
+
| `--verbose` | Log commands, cache decisions, and session details to stderr | `boolean` | `false` |
|
|
153
|
+
| `--help`<br>`-h` | Show help | `boolean` | |
|
|
154
|
+
| `--version`<br>`-v` | Show version number | `boolean` | |
|
|
153
155
|
|
|
154
156
|
<!-- /cli-help -->
|
|
155
157
|
|
|
@@ -246,6 +248,10 @@ For an existing post-bump workflow, `chagent --amend` explicitly amends an unpub
|
|
|
246
248
|
|
|
247
249
|
Workspace declarations in `pnpm-workspace.yaml` or `package.json` determine the packages to process. Each current workspace package gets its own changelog. The root contains repository highlights and links to package releases.
|
|
248
250
|
|
|
251
|
+
Use the `rootChangelog` option or `--root-changelog` flag to choose what the root changelog records. The default, `summary`, works as described above. `package` treats the root as a package of its own, for repositories whose main package lives at the root: its changelog describes the root's files and the workspace packages it depends on in full detail, without changes from other packages or links to their releases. `none` writes only the package changelogs. After changing the mode of an existing root changelog, run with `--rebuild` to reconstruct its history in the new form.
|
|
252
|
+
|
|
253
|
+
Use `skipPackage` directory globs, or repeat `--skip-package`, to leave workspace packages such as documentation sites or examples without changelogs. A skipped package is left out of the root changelog, and its changes are not counted among the root's own, but packages that depend on it still include its relevant changes. To count a directory's changes as the root's instead, `exclude` it from workspace discovery. A pattern that matches no workspace package is an error.
|
|
254
|
+
|
|
249
255
|
For other monorepo layouts, configure `packages` directory globs. These directories do not need npm manifests; their Git tags identify releases, using the directory basename as the package name.
|
|
250
256
|
|
|
251
257
|
Shared-version repositories use repository versions at the root. Independently versioned repositories use package-qualified headings such as `@example/package@1.2.0`. Repository infrastructure changes appear in the root's `Unreleased` section. Explicit workspace dependencies contribute relevant source context to dependent packages. Dependency and tooling maintenance belongs under `Maintenance`. `build`, `coverage`, and `dist` directories at the top of a package count as generated output and are excluded, so a package named `packages/build` is still treated as source.
|
|
@@ -266,6 +272,7 @@ export default defineConfig({
|
|
|
266
272
|
github: true,
|
|
267
273
|
maxInputChars: 80_000,
|
|
268
274
|
model: 'gpt-6.1-sol',
|
|
275
|
+
rootChangelog: 'summary',
|
|
269
276
|
timeout: 20,
|
|
270
277
|
})
|
|
271
278
|
```
|
|
@@ -274,7 +281,7 @@ Importing `defineConfig` requires chagent in the repository's own dependencies.
|
|
|
274
281
|
|
|
275
282
|
Also supported: `chagent.config.mjs`, `chagent.config.js`, `chagent.config.json`, or a `chagent` key in `package.json`. CLI flags override configured values. `--config` selects a specific file.
|
|
276
283
|
|
|
277
|
-
Use `packages` and `exclude` directory globs to customize workspace discovery. Use `tagPatterns` to select unusual release tag conventions. Templates support `{name}` and `{version}`; defaults recognize `v1.2.3`, `1.2.3`, `@scope/package@1.2.3`, and `@scope/package/v1.2.3`.
|
|
284
|
+
Use `packages` and `exclude` directory globs to customize workspace discovery, and `skipPackage` to leave discovered packages without changelogs. Use `tagPatterns` to select unusual release tag conventions. Templates support `{name}` and `{version}`; defaults recognize `v1.2.3`, `1.2.3`, `@scope/package@1.2.3`, and `@scope/package/v1.2.3`.
|
|
278
285
|
|
|
279
286
|
## Background
|
|
280
287
|
|
|
@@ -284,7 +291,7 @@ Chagent reads reachable version tags and commit history regardless of the projec
|
|
|
284
291
|
|
|
285
292
|
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.
|
|
286
293
|
|
|
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.
|
|
294
|
+
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.
|
|
288
295
|
|
|
289
296
|
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.
|
|
290
297
|
|