bunset 0.0.2 → 0.0.3

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/README.md CHANGED
@@ -33,11 +33,12 @@ bunset [options]
33
33
  | `--all` | Update all workspace packages |
34
34
  | `--changed` | Update only changed packages (default for workspaces) |
35
35
  | `--no-commit` | Do not commit changes to git (commits by default) |
36
- | `--tag` | Tag the commit with new version (default) |
36
+ | `--no-tag` | Tag the commit with new version (default) |
37
37
  | `--per-package-tags` | Use `pkg@1.2.3` tags instead of prefixed tags |
38
- | `--tag-prefix` | Tag prefix (default: `v`, e.g. `v1.2.3`) |
38
+ | `--tag-prefix` | Tag prefix (auto-detected from last tag, or `v` if no tags) |
39
39
  | `--sections` | Comma-separated changelog sections (default: `feat,fix,perf`) |
40
40
  | `--dry-run` | Preview changes without writing files, committing, or tagging |
41
+ | `--debug` | Show detailed inclusion/exclusion reasoning (implies `--dry-run`) |
41
42
  | `--no-filter-by-package` | Include all commits in every package changelog (monorepo) |
42
43
 
43
44
  When bump type or scope flags are omitted, interactive prompts will ask.
@@ -87,21 +88,33 @@ Only sections listed in `--sections` (or the `sections` config option) appear in
87
88
 
88
89
  ### Config File
89
90
 
90
- Place a `.bunset.toml` in your project root to set persistent defaults so you don't have to pass the same flags every time:
91
+ Place a `.bunset.toml` in your project root to set persistent defaults so you don't have to pass the same flags every time. All fields are optional. CLI flags always take priority over config values.
91
92
 
92
93
  ```toml
93
- bump = "patch" # "patch" | "minor" | "major"
94
- scope = "changed" # "all" | "changed"
95
- commit = true
96
- tag = true
97
- per-package-tags = false
98
- sections = ["feat", "fix", "perf"] # changelog sections and order
99
- dry-run = false # preview without writing
100
- filter-by-package = true # per-package commit filtering (monorepo)
101
- tag-prefix = "v" # prefix for version tags
94
+ bump = "patch" # "patch" | "minor" | "major"
95
+ scope = "changed" # "all" | "changed"
96
+ commit = true # auto-commit (default: true)
97
+ tag = true # create git tags (default: true)
98
+ per-package-tags = false # pkg@version tags (monorepo)
99
+ tag-prefix = "v" # tag prefix (default: auto-detect)
100
+ sections = ["feat", "fix", "perf"] # changelog sections and order
101
+ dry-run = false # preview without writing
102
+ debug = false # detailed reasoning (implies dry-run)
103
+ filter-by-package = true # per-package filtering (monorepo)
102
104
  ```
103
105
 
104
- All fields are optional. CLI flags always take priority over config values. If `bump` or `scope` is set in config, the interactive prompt for that option is skipped.
106
+ | Key | Type | Default | Description |
107
+ |-----|------|---------|-------------|
108
+ | `bump` | `string` | _(prompt)_ | Version bump type: `"patch"`, `"minor"`, or `"major"`. Skips the interactive prompt when set. |
109
+ | `scope` | `string` | _(prompt)_ | Package scope: `"all"` or `"changed"`. Skips the interactive prompt when set (monorepo only). |
110
+ | `commit` | `boolean` | `true` | Whether to auto-commit the version bump and changelog changes. |
111
+ | `tag` | `boolean` | `true` | Whether to create git tags for released versions. |
112
+ | `per-package-tags` | `boolean` | `false` | Use `pkg@1.2.3` tags instead of prefixed tags. In a monorepo, packages with no matching commits are skipped entirely. |
113
+ | `tag-prefix` | `string` | _(auto)_ | Prefix for version tags. Auto-detected from the last git tag when not set (falls back to `"v"` if no tags exist). Set to `""` for bare version numbers, or e.g. `"project-v"` for `project-v1.2.3`. |
114
+ | `sections` | `string[]` | `["feat", "fix", "perf"]` | Which commit types to include in the changelog and in what order. Accepts any recognized type keyword. |
115
+ | `dry-run` | `boolean` | `false` | Preview all changes without writing files, committing, or tagging. |
116
+ | `debug` | `boolean` | `false` | Show detailed inclusion/exclusion reasoning. Implies `dry-run`. |
117
+ | `filter-by-package` | `boolean` | `true` | In a monorepo, only include commits that touched files within each package. Disable with `false` to include all commits in every changelog. |
105
118
 
106
119
  ## Testing
107
120
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunset",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "module": "src/index.ts",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -16,9 +16,10 @@ Options:
16
16
  --no-commit Do not commit the version bump and changelog
17
17
  --no-tag Do not create git tags for released versions
18
18
  --per-package-tags Use package-scoped tags (pkg@version) instead of prefixed
19
- --tag-prefix <str> Tag prefix (default: "v", e.g. v1.2.3)
19
+ --tag-prefix <str> Tag prefix (auto-detected from last tag, or "v" if no tags)
20
20
  --sections <list> Comma-separated changelog sections (default: feat,fix,perf)
21
21
  --dry-run Preview changes without writing files, committing, or tagging
22
+ --debug Show detailed inclusion/exclusion reasoning (implies --dry-run)
22
23
  --no-filter-by-package
23
24
  Include all commits in every package changelog (monorepo)
24
25
  --help, -h Show this help message
@@ -50,9 +51,21 @@ Commit format:
50
51
  Only sections listed in --sections (or config) are included in the changelog.
51
52
  Default sections: feat, fix, perf.
52
53
 
53
- Config:
54
+ Config file (.bunset.toml):
54
55
  Place a .bunset.toml in your project root to set persistent defaults.
55
- CLI flags always override config values. See README for format.`);
56
+ All fields are optional. CLI flags always override config values.
57
+
58
+ Example:
59
+ bump = "patch" # "patch" | "minor" | "major"
60
+ scope = "changed" # "all" | "changed"
61
+ commit = true # auto-commit (default: true)
62
+ tag = true # create git tags (default: true)
63
+ per-package-tags = false # pkg@version tags (monorepo)
64
+ tag-prefix = "v" # tag prefix (default: auto-detect)
65
+ sections = ["feat", "fix", "perf"] # changelog sections and order
66
+ dry-run = false # preview without writing
67
+ debug = false # detailed reasoning (implies dry-run)
68
+ filter-by-package = true # per-package filtering (monorepo)`);
56
69
  }
57
70
 
58
71
  export function resolveOptions(
@@ -74,6 +87,7 @@ export function resolveOptions(
74
87
  "dry-run": { type: "boolean", default: false },
75
88
  "filter-by-package": { type: "boolean", default: true },
76
89
  "tag-prefix": { type: "string" },
90
+ debug: { type: "boolean", default: false },
77
91
  help: { type: "boolean", short: "h", default: false },
78
92
  },
79
93
  strict: true,
@@ -100,7 +114,8 @@ export function resolveOptions(
100
114
  ?? config.sections
101
115
  ?? DEFAULT_SECTIONS;
102
116
 
103
- const dryRun = values["dry-run"] ? true : (config.dryRun ?? false);
117
+ const debug = values.debug ? true : (config.debug ?? false);
118
+ const dryRun = debug || values["dry-run"] ? true : (config.dryRun ?? false);
104
119
 
105
120
  const filterByPackage = values["filter-by-package"] === false
106
121
  ? false
@@ -108,14 +123,14 @@ export function resolveOptions(
108
123
 
109
124
  const tagPrefix = values["tag-prefix"] as string | undefined
110
125
  ?? config.tagPrefix
111
- ?? "v";
126
+ ?? null;
112
127
 
113
128
  if (bump && scope) {
114
- return { scope, bump, commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix };
129
+ return { scope, bump, commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix, debug };
115
130
  }
116
131
 
117
132
  return promptForMissing(
118
- { commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix },
133
+ { commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix, debug },
119
134
  bump,
120
135
  scope,
121
136
  isWs,
@@ -156,7 +171,8 @@ interface MergedDefaults {
156
171
  sections: CommitType[];
157
172
  dryRun: boolean;
158
173
  filterByPackage: boolean;
159
- tagPrefix: string;
174
+ tagPrefix: string | null;
175
+ debug: boolean;
160
176
  }
161
177
 
162
178
  async function promptForMissing(
package/src/config.ts CHANGED
@@ -49,6 +49,10 @@ export async function loadConfig(
49
49
  config.tagPrefix = raw["tag-prefix"];
50
50
  }
51
51
 
52
+ if (typeof raw.debug === "boolean") {
53
+ config.debug = raw.debug;
54
+ }
55
+
52
56
  if (Array.isArray(raw.sections)) {
53
57
  const sections: CommitType[] = [];
54
58
  for (const s of raw.sections) {
package/src/index.ts CHANGED
@@ -29,10 +29,41 @@ const isWs = await isWorkspace(cwd);
29
29
  const config = await loadConfig(cwd);
30
30
  const options = await resolveOptions(isWs, config);
31
31
 
32
+ const dbg = options.debug;
33
+ function debug(msg: string): void {
34
+ if (dbg) console.log(`[debug] ${msg}`);
35
+ }
36
+
37
+ if (dbg) {
38
+ console.log("--- Debug Mode (dry-run implied) ---\n");
39
+ debug(`cwd: ${cwd}`);
40
+ debug(`workspace: ${isWs}`);
41
+ debug(`config loaded: ${JSON.stringify(config)}`);
42
+ debug(`resolved options: ${JSON.stringify(options)}`);
43
+ console.log("");
44
+ }
45
+
32
46
  const allPackages = await getAllPackages(cwd);
33
47
  const lastTag = await getLastTag(cwd);
34
48
  const rawCommits = await getCommitsSince(cwd, lastTag);
35
49
 
50
+ // Resolve tagPrefix: explicit value wins, otherwise infer from last tag
51
+ let tagPrefix: string;
52
+ if (options.tagPrefix !== null) {
53
+ tagPrefix = options.tagPrefix;
54
+ debug(`tag prefix explicit: "${tagPrefix}"`);
55
+ } else if (lastTag) {
56
+ const semverMatch = lastTag.match(/\d+\.\d+\.\d+/);
57
+ tagPrefix = semverMatch ? lastTag.slice(0, semverMatch.index) : "v";
58
+ debug(`tag prefix auto-detected: "${tagPrefix}" (from tag: ${lastTag})`);
59
+ } else {
60
+ tagPrefix = "v";
61
+ debug(`tag prefix default: "${tagPrefix}" (no previous tags found)`);
62
+ }
63
+
64
+ debug(`last tag: ${lastTag ?? "(none)"}`);
65
+ debug(`raw commits since tag: ${rawCommits.length}`);
66
+
36
67
  if (rawCommits.length === 0) {
37
68
  console.log("No commits found since last tag. Nothing to do.");
38
69
  process.exit(0);
@@ -40,14 +71,33 @@ if (rawCommits.length === 0) {
40
71
 
41
72
  const parsed = rawCommits.map((c) => parseCommit(c.hash, c.message));
42
73
 
74
+ if (dbg) {
75
+ console.log("");
76
+ debug("--- Parsed commits ---");
77
+ for (const c of parsed) {
78
+ const typeStr = c.type ?? "UNRECOGNIZED";
79
+ const scopeStr = c.commitScope ? `(${c.commitScope})` : "";
80
+ const included = c.type && options.sections.includes(c.type) ? "INCLUDED" : "EXCLUDED";
81
+ debug(` ${c.hash.slice(0, 7)} ${typeStr}${scopeStr}: ${c.description} → ${included} (section: ${c.type ?? "none"})`);
82
+ }
83
+ console.log("");
84
+ }
85
+
43
86
  // In a monorepo with filtering, fetch the file list for each commit
44
87
  const shouldFilter = isWs && options.filterByPackage;
88
+ debug(`per-package filtering: ${shouldFilter ? "enabled" : "disabled"}`);
45
89
  if (shouldFilter) {
46
90
  await Promise.all(
47
91
  parsed.map(async (commit) => {
48
92
  commit.files = await getCommitFiles(cwd, commit.hash);
49
93
  }),
50
94
  );
95
+ if (dbg) {
96
+ for (const c of parsed) {
97
+ debug(` ${c.hash.slice(0, 7)} files: ${c.files.length > 0 ? c.files.join(", ") : "(none)"}`);
98
+ }
99
+ console.log("");
100
+ }
51
101
  }
52
102
 
53
103
  const globalGroups = groupCommits(parsed);
@@ -62,6 +112,8 @@ let packages =
62
112
  ? await getChangedPackages(cwd, allPackages, lastTag)
63
113
  : allPackages;
64
114
 
115
+ debug(`scope: ${options.scope}, packages to process: ${packages.map((p) => p.name).join(", ") || "(none)"}`);
116
+
65
117
  if (packages.length === 0) {
66
118
  console.log("No changed packages found. Nothing to do.");
67
119
  process.exit(0);
@@ -81,7 +133,7 @@ function packageHasChanges(groups: GroupedCommits): boolean {
81
133
  }
82
134
 
83
135
  if (options.dryRun) {
84
- console.log("--- Dry Run ---\n");
136
+ if (!dbg) console.log("--- Dry Run ---\n");
85
137
 
86
138
  const tags: string[] = [];
87
139
 
@@ -89,6 +141,24 @@ if (options.dryRun) {
89
141
  const groups = getPackageGroups(pkg, parsed);
90
142
  const hasChanges = packageHasChanges(groups);
91
143
 
144
+ if (dbg) {
145
+ debug(`--- Package: ${pkg.name} ---`);
146
+ debug(` path: ${pkg.path}`);
147
+ debug(` current version: ${pkg.version ?? "0.0.0"}`);
148
+ for (const section of options.sections) {
149
+ const commits = groups[section];
150
+ if (commits.length > 0) {
151
+ debug(` ${section}: ${commits.length} commit(s)`);
152
+ for (const c of commits) {
153
+ debug(` - ${c.hash.slice(0, 7)} ${c.description}${c.commitScope ? ` (scope: ${c.commitScope})` : ""}`);
154
+ }
155
+ } else {
156
+ debug(` ${section}: 0 commits`);
157
+ }
158
+ }
159
+ debug(` has matching commits: ${hasChanges}`);
160
+ }
161
+
92
162
  if (!hasChanges && options.perPackageTags) {
93
163
  console.log(`${pkg.name}: no matching commits, skipping.`);
94
164
  continue;
@@ -117,7 +187,7 @@ if (options.dryRun) {
117
187
  if (options.perPackageTags) {
118
188
  tags.push(`${pkg.name}@${newVersion}`);
119
189
  } else {
120
- tags.push(`${options.tagPrefix}${newVersion}`);
190
+ tags.push(`${tagPrefix}${newVersion}`);
121
191
  }
122
192
  }
123
193
  }
@@ -177,7 +247,7 @@ for (const pkg of packages) {
177
247
  if (options.perPackageTags) {
178
248
  tags.push(`${pkg.name}@${newVersion}`);
179
249
  } else {
180
- tags.push(`${options.tagPrefix}${newVersion}`);
250
+ tags.push(`${tagPrefix}${newVersion}`);
181
251
  }
182
252
  }
183
253
  }
package/src/types.ts CHANGED
@@ -21,7 +21,8 @@ export interface CliOptions {
21
21
  sections: CommitType[];
22
22
  dryRun: boolean;
23
23
  filterByPackage: boolean;
24
- tagPrefix: string;
24
+ tagPrefix: string | null;
25
+ debug: boolean;
25
26
  }
26
27
 
27
28
  export interface ParsedCommit {