@osdlc/repo-cleaner 0.15.0 → 0.17.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @osdlc/repo-cleaner
2
2
 
3
+ ## 0.17.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [4f764b7]
8
+ - @osdlc/typed-gh@0.15.0
9
+
10
+ ## 0.17.0
11
+
12
+ ### Minor Changes
13
+
14
+ - b036603: `holdFreedBranches(items)` keeps a branch freed for its worktree's removal when that worktree no longer goes, such as when a host's `annotate` holds the worktree back. `applyPlan` runs it after `annotate`. A host should run it on its own annotated rows too, so the counts and the plan match what `apply` will do.
15
+
16
+ ## 0.16.0
17
+
18
+ ### Minor Changes
19
+
20
+ - db33c17: **Breaking (CLI):** `--remote <name>` is now `--measure-remote <name>`, so it is no longer one letter away from `--remotes`. Typing `--remote` when you meant `--remotes` used to fail with "argument missing". `--no-gh` is gone; use `--provider none`, which does the same. The help no longer says the tool asks `gh`, since the provider can be GitLab.
21
+
3
22
  ## 0.15.0
4
23
 
5
24
  ### Minor Changes
package/README.md CHANGED
@@ -386,10 +386,9 @@ Every command that sweeps takes these:
386
386
  | `--only <list>` | `worktrees`, `branches`, `remotes` (default: `worktrees,branches`) |
387
387
  | `--remotes` | Also sweep remote branches. Slower: each one is compared with the default branch |
388
388
  | `--branch <name\|HEAD>` | One branch of this repository: its worktree and local branch, and its remote branch with `--remotes`. Refuses the default branch |
389
- | `--remote <name>` | Remote to measure against (default `origin`) |
389
+ | `--measure-remote <name>` | The remote whose default branch work is measured against (default `origin`) |
390
390
  | `--remote-url <url>` | Sweep this remote's branches with no checkout of your own (see [A remote with no checkout](#a-remote-with-no-checkout)); repeatable |
391
- | `--provider <name>` | With `--remotes`: where pull requests come from: `auto` (default, by the remote's host), `github`, `gitlab` or `none` |
392
- | `--no-gh` | With `--remotes`: classify on git evidence alone; the same as `--provider none`. Without `--remotes`, no provider is asked |
391
+ | `--provider <name>` | With `--remotes`: where pull requests come from: `auto` (default, by the remote's host), `github`, `gitlab`, or `none` to classify on git evidence alone |
393
392
  | `--no-fetch` | With `--remotes`: skip the fetch; the header then says `not fetched`. Without `--remotes`, nothing is fetched |
394
393
 
395
394
  `status` also takes:
@@ -530,8 +529,14 @@ so the rows it checks again are the rows the host planned from.
530
529
 
531
530
  This works because of how the ladder is ordered. Every verdict the sweep
532
531
  reaches on its own evidence outranks anything a host could contribute, so a
533
- host never has to veto one. It only sharpens the rows left at `safe-to-keep`
534
- or `needs-investigation`.
532
+ host rarely has to veto one. It mostly sharpens the rows left at
533
+ `safe-to-keep` or `needs-investigation`.
534
+
535
+ A host that does hold back or demote a worktree row must also keep that
536
+ worktree's branch, which the sweep freed because the worktree was going. Pass
537
+ the annotated rows through `holdFreedBranches(items, { branchScoped })`, with
538
+ `branchScoped` set for a `branch` sweep. `applyPlan` does the same after its
539
+ `annotate` hook, so the plan and the run agree.
535
540
 
536
541
  An issue tracker's linkage, applied this way, is a host's own layer over the
537
542
  sweep and `applyPlan`.
package/dist/apply.d.ts CHANGED
@@ -55,7 +55,8 @@ export interface ApplyOptions extends HostHooks {
55
55
  safeOnly?: boolean | undefined;
56
56
  /**
57
57
  * Items to re-check against instead of sweeping again, keyed by repository
58
- * root, already annotated: the hook is not applied to them again. For a
58
+ * root, already annotated: the hook is not applied to them again, though
59
+ * `holdFreedBranches` is, so a freed branch matches its worktree. For a
59
60
  * preview straight after a sweep; the run that deletes should always sweep
60
61
  * again.
61
62
  */
package/dist/apply.js CHANGED
@@ -22,7 +22,7 @@ import { localDeleteCommand, remoteDeleteCommand } from "./branches.js";
22
22
  import { busyHolders, defaultProcessProbe, parseLockOwner } from "./processes.js";
23
23
  import { PLAN_CATEGORIES, fingerprint, isPlanCategory, sameFingerprint, } from "./plan.js";
24
24
  import { RECEIPT_VERSION } from "./receipt.js";
25
- import { sweepRepo } from "./sweep.js";
25
+ import { holdFreedBranches, sweepRepo } from "./sweep.js";
26
26
  import { AUTHORIZATIONS, } from "./types.js";
27
27
  import { worktreePruneCommand, worktreeRemoveCommand, worktreeUnlockCommand } from "./worktrees.js";
28
28
  import { writeJsonAtomic } from "./write-json.js";
@@ -89,9 +89,10 @@ const SCOPE_OF = {
89
89
  };
90
90
  /** The plan repository's rows as they stand now, with the host's overlay applied. */
91
91
  function resweep(repo, scope, opts) {
92
+ const branchScoped = repo.branch != null;
92
93
  const given = opts.swept?.get(repo.root);
93
94
  if (given !== undefined) {
94
- return [...given];
95
+ return holdFreedBranches([...given], { branchScoped });
95
96
  }
96
97
  const sweep = sweepRepo(repo.root, {
97
98
  remote: repo.remote,
@@ -110,7 +111,9 @@ function resweep(repo, scope, opts) {
110
111
  if (sweep.repo === null || sweep.error !== null) {
111
112
  throw new Error(sweep.error ?? `could not read repository: ${repo.root}`);
112
113
  }
113
- return opts.annotate === undefined ? sweep.items : opts.annotate(sweep.repo, sweep.items);
114
+ return opts.annotate === undefined
115
+ ? sweep.items
116
+ : holdFreedBranches(opts.annotate(sweep.repo, sweep.items), { branchScoped });
114
117
  }
115
118
  /** Re-sweep, then split the selection into rows to run and rows dropped. */
116
119
  function recheck(selected, kept, grants, implicit, opts) {
@@ -41,11 +41,13 @@ export declare function mergedFact(e: {
41
41
  }): boolean;
42
42
  /** `HEAD` as the branch checked out at `git`'s directory; any other name as given. */
43
43
  export declare function resolveBranch(value: string, git: Git): string;
44
+ /** Why a branch checked out in a worktree is kept. */
45
+ export declare const HELD_BY_WORKTREE = "checked out in a worktree (handle via that worktree)";
44
46
  /**
45
47
  * Delete local branch `name` only while it still points at `sha` (the full
46
- * sha). Same caveats as `deleteRef` in `@osdlc/typed-git`. Only a `branch`
47
- * sweep proposes a branch checked out in a worktree, and only alongside that
48
- * worktree's removal, which must run first.
48
+ * sha). Same caveats as `deleteRef` in `@osdlc/typed-git`. A sweep proposes a
49
+ * branch checked out in a worktree only alongside that worktree's removal,
50
+ * which must run first.
49
51
  */
50
52
  export declare function localDeleteCommand(name: string, sha: string): string;
51
53
  /** Delete `remote`'s branch `name` only while it still points at `sha`. */
package/dist/branches.js CHANGED
@@ -90,11 +90,13 @@ export function resolveBranch(value, git) {
90
90
  throw new Error(`--branch HEAD: no branch is checked out at ${git.cwd}`);
91
91
  return current;
92
92
  }
93
+ /** Why a branch checked out in a worktree is kept. */
94
+ export const HELD_BY_WORKTREE = 'checked out in a worktree (handle via that worktree)';
93
95
  /**
94
96
  * Delete local branch `name` only while it still points at `sha` (the full
95
- * sha). Same caveats as `deleteRef` in `@osdlc/typed-git`. Only a `branch`
96
- * sweep proposes a branch checked out in a worktree, and only alongside that
97
- * worktree's removal, which must run first.
97
+ * sha). Same caveats as `deleteRef` in `@osdlc/typed-git`. A sweep proposes a
98
+ * branch checked out in a worktree only alongside that worktree's removal,
99
+ * which must run first.
98
100
  */
99
101
  export function localDeleteCommand(name, sha) {
100
102
  return shellJoin(['git', 'update-ref', '-d', `refs/heads/${name}`, sha]);
@@ -159,7 +161,7 @@ export function classifyLocalBranch(opts) {
159
161
  const held = name === repo.currentBranch
160
162
  ? 'current HEAD'
161
163
  : worktreeBranches.has(name)
162
- ? 'checked out in a worktree (handle via that worktree)'
164
+ ? HELD_BY_WORKTREE
163
165
  : null;
164
166
  if (held !== null) {
165
167
  // Never deletable here, but still counted as merged or not: by ancestry or
package/dist/cli.js CHANGED
@@ -65,10 +65,10 @@ const SWEEP_FLAGS = {
65
65
  valueName: 'name|HEAD',
66
66
  help: 'only this branch of this repository: its worktree and local branch, and its remote branch with --remotes',
67
67
  },
68
- remote: {
68
+ measureRemote: {
69
69
  kind: 'string',
70
70
  valueName: 'name',
71
- help: 'remote to measure against (default: origin)',
71
+ help: 'the remote whose default branch work is measured against (default: origin)',
72
72
  },
73
73
  remoteUrl: {
74
74
  kind: 'list',
@@ -77,16 +77,11 @@ const SWEEP_FLAGS = {
77
77
  commaSeparated: false,
78
78
  help: 'sweep the branches of this remote with no checkout of your own, through a clone kept in the cache directory; repeatable',
79
79
  },
80
- gh: {
81
- kind: 'boolean',
82
- negatable: true,
83
- help: 'with --remotes: classify on git evidence alone, the same as --provider none (without --remotes no provider is asked)',
84
- },
85
80
  provider: {
86
81
  kind: 'enum',
87
82
  choices: PROVIDER_CHOICES,
88
83
  valueName: 'name',
89
- help: 'with --remotes: where pull requests come from: auto (by remote host), github (gh), gitlab (glab), none',
84
+ help: 'with --remotes: where pull requests come from: auto (by remote host), github (gh), gitlab (glab), or none to classify on git evidence alone',
90
85
  },
91
86
  fetch: {
92
87
  kind: 'boolean',
@@ -117,7 +112,7 @@ const statusCli = () => defineCli({
117
112
  name: NAME,
118
113
  summary: 'Inventory local branches and worktrees (remote branches too with --remotes), and say ' +
119
114
  'which have been merged into the default branch. Offline unless --remotes, which fetches ' +
120
- 'each remote first and asks gh about pull requests; deletes nothing.',
115
+ 'each remote first and asks the remote host about pull requests; deletes nothing.',
121
116
  positionals: PATHS,
122
117
  flags: {
123
118
  ...SWEEP_FLAGS,
@@ -276,7 +271,7 @@ function remotesNote(swept, style) {
276
271
  return '';
277
272
  if (!swept.result.repos.some((r) => r.repo?.remote != null))
278
273
  return '';
279
- return (style.muted('Local only: nothing fetched, no gh, remote branches not checked; add --remotes for all three.') + '\n');
274
+ return (style.muted('Local only: nothing fetched, no pull requests looked up, remote branches not checked; add --remotes for all three.') + '\n');
280
275
  }
281
276
  /** Where `--remote-url` keeps its clones. */
282
277
  function mirrorCache(ctx) {
@@ -322,10 +317,6 @@ function sweep(name, v, paths, ctx, status) {
322
317
  if (conflict !== null)
323
318
  return { kind: 'exit', code: usageError(ctx, name, conflict) };
324
319
  }
325
- if (!v.gh && v.provider !== undefined && v.provider !== 'none') {
326
- const message = `--no-gh asks no provider; drop it or --provider ${v.provider}`;
327
- return { kind: 'exit', code: usageError(ctx, name, message) };
328
- }
329
320
  let branch;
330
321
  try {
331
322
  branch = v.branch === undefined ? undefined : resolveBranch(v.branch, new Git(ctx.cwd));
@@ -338,8 +329,8 @@ function sweep(name, v, paths, ctx, status) {
338
329
  const options = {
339
330
  baseDir: ctx.cwd,
340
331
  cwd: ctx.cwd,
341
- // Local by default means offline: the fetch and gh go with remote branches.
342
- noProvider: !(online && v.gh) || v.provider === 'none',
332
+ // Local by default means offline: the fetch and the pull-request lookup go with remote branches.
333
+ noProvider: !online || v.provider === 'none',
343
334
  fetch: online && v.fetch,
344
335
  provider: v.provider,
345
336
  branch,
@@ -348,7 +339,7 @@ function sweep(name, v, paths, ctx, status) {
348
339
  onWarn: (message) => ctx.io.stderr(`${NAME}: ${message}\n`),
349
340
  ...(v.progress ? { onProgress: (p) => status.update(describeSweep(p)) } : {}),
350
341
  scope,
351
- ...(v.remote === undefined ? {} : { remote: v.remote }),
342
+ ...(v.measureRemote === undefined ? {} : { remote: v.measureRemote }),
352
343
  };
353
344
  let entries = paths.length > 0 ? [...paths] : [ctx.cwd];
354
345
  if (urls.length > 0) {
@@ -393,7 +384,7 @@ function reportUnreadable(ctx, name, result) {
393
384
  }
394
385
  function planOf(swept, now) {
395
386
  const opts = {
396
- remote: swept.values.remote ?? null,
387
+ remote: swept.values.measureRemote ?? null,
397
388
  noProvider: swept.noProvider,
398
389
  provider: swept.values.provider,
399
390
  branch: swept.branch ?? null,
@@ -539,7 +530,7 @@ function applyConflict(v, paths) {
539
530
  paths.length > 0 ? 'paths' : null,
540
531
  v.only !== undefined ? '--only' : null,
541
532
  v.branch !== undefined ? '--branch' : null,
542
- v.remote !== undefined ? '--remote' : null,
533
+ v.measureRemote !== undefined ? '--measure-remote' : null,
543
534
  v.remoteUrl.length > 0 ? '--remote-url' : null,
544
535
  ].filter((f) => f !== null);
545
536
  if (sweepFlags.length > 0)
package/dist/hooks.d.ts CHANGED
@@ -7,7 +7,10 @@
7
7
  import type { RepoInfo, SweepItem } from './types.ts';
8
8
  /**
9
9
  * Re-rank a repository's swept rows with what the host knows. Runs after the
10
- * sweep, on its output; it never reaches into classification.
10
+ * sweep, on its output; it never reaches into classification. A host that
11
+ * holds back or demotes a worktree passes its own annotated rows through
12
+ * `holdFreedBranches`, as `applyPlan` does, so that worktree's branch is kept
13
+ * too.
11
14
  */
12
15
  export type Annotate = (repo: RepoInfo, items: SweepItem[]) => SweepItem[];
13
16
  export interface HostHooks {
package/dist/index.d.ts CHANGED
@@ -26,6 +26,6 @@ export { defaultProcessProbe, isIdleShell, parseLockOwner, parseLsofCwd, type De
26
26
  export { fetchPrs } from './prs.ts';
27
27
  export { PROVIDER_CHOICES, PROVIDER_NAMES, chooseProvider, forgeTarget, githubProvider, gitlabProvider, probeProvider, providers, type ForgeTarget, type PrProvider, type ProviderChoice, type ProviderName, type ProviderStatus, } from './providers/index.ts';
28
28
  export { expandTilde, isCheckoutRoot, resolveEntries, type Resolution, type ResolveOptions, } from './resolve.ts';
29
- export { sweepRepo, sweepRepos, type SweepOptions, type SweepReposOptions } from './sweep.ts';
29
+ export { holdFreedBranches, sweepRepo, sweepRepos, type SweepOptions, type SweepReposOptions, } from './sweep.ts';
30
30
  export { proposedCommands, renderJson, renderApplySummary, renderReceipt, summarizeReceipt, type ApplySummary, renderRepo, renderScan, renderSummary, renderText, requiresNote, } from './render.ts';
31
31
  export { ALL_SCOPES, AUTHORIZATIONS, authorizationsFor, emptyBucket, emptyScan, findDuplicateCheckouts, isUnfetched, makeItem, summarize, type Authorization, type Category, type Classification, type DuplicateCheckout, type PRInfo, type RepoInfo, type RepoSweep, type ScanStats, type Scope, type SummaryBucket, type SweepItem, type SweepProgress, type SweepResult, type UnresolvedEntry, } from './types.ts';
package/dist/index.js CHANGED
@@ -25,6 +25,6 @@ export { defaultProcessProbe, isIdleShell, parseLockOwner, parseLsofCwd, } from
25
25
  export { fetchPrs } from "./prs.js";
26
26
  export { PROVIDER_CHOICES, PROVIDER_NAMES, chooseProvider, forgeTarget, githubProvider, gitlabProvider, probeProvider, providers, } from "./providers/index.js";
27
27
  export { expandTilde, isCheckoutRoot, resolveEntries, } from "./resolve.js";
28
- export { sweepRepo, sweepRepos } from "./sweep.js";
28
+ export { holdFreedBranches, sweepRepo, sweepRepos, } from "./sweep.js";
29
29
  export { proposedCommands, renderJson, renderApplySummary, renderReceipt, summarizeReceipt, renderRepo, renderScan, renderSummary, renderText, requiresNote, } from "./render.js";
30
30
  export { ALL_SCOPES, AUTHORIZATIONS, authorizationsFor, emptyBucket, emptyScan, findDuplicateCheckouts, isUnfetched, makeItem, summarize, } from "./types.js";
package/dist/sweep.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  import type { CommandRunner } from '@osdlc/command-seam';
9
9
  import { type ProcessProbe } from './processes.ts';
10
10
  import { type ProviderChoice } from './providers/index.ts';
11
- import { type RepoSweep, type Scope, type SweepProgress, type SweepResult } from './types.ts';
11
+ import { type RepoSweep, type Scope, type SweepItem, type SweepProgress, type SweepResult } from './types.ts';
12
12
  export interface SweepOptions {
13
13
  /** Which categories to gather. Defaults to all of them. */
14
14
  scope?: Iterable<Scope | string>;
@@ -37,7 +37,8 @@ export interface SweepOptions {
37
37
  * its remote branch. The default branch is refused. `cwd` is ignored, so the
38
38
  * worktree is judged on its facts, and the local branch is not pinned for
39
39
  * being checked out there: apply removes the worktree first. It stays pinned
40
- * when that worktree is blocked or cannot be removed.
40
+ * when that worktree is blocked or cannot be removed, or when `scope` leaves
41
+ * worktrees out.
41
42
  */
42
43
  branch?: string | undefined;
43
44
  /** Injected runners. Defaults spawn the real `git`, `gh` and `glab`. */
@@ -80,3 +81,13 @@ export interface SweepReposOptions extends SweepOptions {
80
81
  * row and the sweep continues.
81
82
  */
82
83
  export declare function sweepRepos(entries: readonly string[], opts?: SweepReposOptions): SweepResult;
84
+ /**
85
+ * Keep again each branch a sweep freed for its worktree's removal when that
86
+ * worktree no longer frees it by the sweep's own rule: a host's `annotate`
87
+ * held it back or demoted it, or dropped its row. Run it on annotated rows,
88
+ * with `branchScoped` set for a `branch` sweep, so the counts and the plan
89
+ * match what `applyPlan` will do. Running it twice changes nothing.
90
+ */
91
+ export declare function holdFreedBranches(items: SweepItem[], opts?: {
92
+ branchScoped?: boolean;
93
+ }): SweepItem[];
package/dist/sweep.js CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { Git } from '@osdlc/typed-git';
9
9
  import { Gh } from '@osdlc/typed-gh';
10
- import { gatherLocalBranches, gatherRemoteBranches } from "./branches.js";
10
+ import { HELD_BY_WORKTREE, gatherLocalBranches, gatherRemoteBranches } from "./branches.js";
11
11
  import { DiscoveryFailed, discoverRepo } from "./discover.js";
12
12
  import { defaultProcessProbe } from "./processes.js";
13
13
  import { fetchPrs } from "./prs.js";
@@ -107,9 +107,9 @@ export function sweepRepo(path, opts = {}) {
107
107
  if (scope.has('worktrees')) {
108
108
  for (const item of worktreeItems) {
109
109
  const branch = item.facts['branch'];
110
- const goes = item.deletable && (only !== undefined || item.classification === 'safe-to-delete');
111
- if (goes && typeof branch === 'string')
110
+ if (freesBranch(item, only !== undefined) && typeof branch === 'string') {
112
111
  freed.set(branch, item.id);
112
+ }
113
113
  }
114
114
  }
115
115
  const pinned = new Set([...worktreeBranches].filter((name) => !freed.has(name)));
@@ -161,3 +161,42 @@ export function sweepRepos(entries, opts = {}) {
161
161
  duplicates: findDuplicateCheckouts(repos.map((sweep) => sweep.repo)),
162
162
  };
163
163
  }
164
+ /**
165
+ * Whether worktree row `item` frees its branch to be judged on its own. A full
166
+ * sweep frees it only for a safe-to-delete worktree; a branch-scoped sweep,
167
+ * where the caller named the branch, for any deletable one.
168
+ */
169
+ function freesBranch(item, branchScoped) {
170
+ return item.deletable && (branchScoped || item.classification === 'safe-to-delete');
171
+ }
172
+ /**
173
+ * Keep again each branch a sweep freed for its worktree's removal when that
174
+ * worktree no longer frees it by the sweep's own rule: a host's `annotate`
175
+ * held it back or demoted it, or dropped its row. Run it on annotated rows,
176
+ * with `branchScoped` set for a `branch` sweep, so the counts and the plan
177
+ * match what `applyPlan` will do. Running it twice changes nothing.
178
+ */
179
+ export function holdFreedBranches(items, opts = {}) {
180
+ const going = new Set(items
181
+ .filter((it) => it.category === 'worktree' && freesBranch(it, opts.branchScoped === true))
182
+ .map((it) => it.id));
183
+ return items.map((item) => {
184
+ const worktree = item.facts['worktree'];
185
+ if (item.category !== 'local_branch' || typeof worktree !== 'string')
186
+ return item;
187
+ if (going.has(worktree))
188
+ return item;
189
+ const { worktree: _, ...facts } = item.facts;
190
+ return {
191
+ ...item,
192
+ classification: 'safe-to-keep',
193
+ action: 'keep',
194
+ reason: HELD_BY_WORKTREE,
195
+ facts,
196
+ commands: [],
197
+ investigationParts: [],
198
+ requires: [],
199
+ deletable: false,
200
+ };
201
+ });
202
+ }
package/dist/types.d.ts CHANGED
@@ -53,7 +53,7 @@ export interface RepoInfo {
53
53
  /**
54
54
  * The provider asked for pull requests and what its probe found: whether
55
55
  * its CLI is installed and signed in. Null when none was asked
56
- * (`--provider none`, `--no-gh`, or a sweep without `--remotes`).
56
+ * (`--provider none`, or a sweep without `--remotes`).
57
57
  */
58
58
  provider: ProviderStatus | null;
59
59
  /** True when the sweep fetched the remote before classifying. */
@@ -1,2 +1,2 @@
1
1
  /** This package's version, from its package.json when it was built. */
2
- export declare const VERSION = "0.15.0";
2
+ export declare const VERSION = "0.17.1";
@@ -1,3 +1,3 @@
1
1
  // Generated by cli-tool-stamp-version from package.json. Do not edit.
2
2
  /** This package's version, from its package.json when it was built. */
3
- export const VERSION = '0.15.0';
3
+ export const VERSION = '0.17.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osdlc/repo-cleaner",
3
- "version": "0.15.0",
3
+ "version": "0.17.1",
4
4
  "description": "What has been merged into the default branch, and what can go away. Inventories local branches, worktrees and remote branches across one repository or many, classifying each with the evidence behind the verdict and the command that would act on it. Plans and applies approved deletes, compare-and-swap.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "author": "sksizer",
@@ -50,11 +50,11 @@
50
50
  "dependencies": {
51
51
  "zod": "^4.6.6",
52
52
  "@osdlc/cli-tool": "^0.16.0",
53
+ "@osdlc/typed-gh": "^0.15.0",
53
54
  "@osdlc/command-seam": "^0.14.0",
54
- "@osdlc/typed-gh": "^0.14.1",
55
55
  "@osdlc/typed-git": "^0.12.0",
56
- "@osdlc/path-guard": "^0.3.1",
57
- "@osdlc/ux": "^0.1.1"
56
+ "@osdlc/ux": "^0.1.1",
57
+ "@osdlc/path-guard": "^0.3.1"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/node": "^26.1.0",
@@ -63,8 +63,8 @@
63
63
  "prettier": "3.9.6",
64
64
  "typescript": "~6.0.3",
65
65
  "vitest": "^5.0.5",
66
- "@osdlc/prettier-config": "^0.3.1",
67
- "@osdlc/tsconfig": "^0.4.1"
66
+ "@osdlc/tsconfig": "^0.4.1",
67
+ "@osdlc/prettier-config": "^0.3.1"
68
68
  },
69
69
  "scripts": {
70
70
  "build": "cli-tool-stamp-version && tsc -p tsconfig.build.json",