depfresh 1.0.0 → 1.1.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.9+-3178c6)](https://www.typescriptlang.org/)
6
6
  [![Node.js](https://img.shields.io/badge/Node.js-24+-339933)](https://nodejs.org/)
7
7
 
8
- Keep your dependencies fresh. Zero config, fast, monorepo-ready. Your AI agent already knows how to use this.
8
+ Keep your dependencies fresh. Taze alternative. Zero config, fast, monorepo-ready. Your AI agent already knows how to use this.
9
9
 
10
10
  ## Install
11
11
 
@@ -6,9 +6,11 @@ import 'node:fs/promises';
6
6
  import 'node:url';
7
7
  import 'defu';
8
8
  import 'pathe';
9
- import '../shared/depfresh.CHHNqHXD.mjs';
10
- import '../shared/depfresh.Dxgqypc6.mjs';
9
+ import '../shared/depfresh.CsTlbZg1.mjs';
10
+ import '../shared/depfresh.DZ8SAjFx.mjs';
11
11
  import 'ansis';
12
+ import 'node:fs';
13
+ import 'yaml';
12
14
 
13
15
  const ENUM_VALUES_BY_FLAG = {
14
16
  mode: VALID_MODES,
@@ -19,7 +21,7 @@ const ENUM_VALUES_BY_FLAG = {
19
21
  const EXIT_CODES = {
20
22
  "0": "Success (no updates found, or updates written successfully).",
21
23
  "1": "Outdated dependencies found with --fail-on-outdated and without --write.",
22
- "2": "Fatal/runtime/configuration error (including invalid enum flag values)."
24
+ "2": "Fatal/runtime/configuration error (including invalid enum flag values and runs failed by --fail-on-resolution-errors or --fail-on-no-packages)."
23
25
  };
24
26
  const WORKFLOWS = {
25
27
  checkOnly: {
@@ -45,6 +47,7 @@ const FLAG_RELATIONSHIPS = {
45
47
  execute: { requires: ["write"] },
46
48
  "verify-command": { requires: ["write"] },
47
49
  interactive: { requires: ["write"] },
50
+ "strict-post-write": { requires: ["write"] },
48
51
  "deps-only": { conflicts: ["dev-only"] },
49
52
  "dev-only": { conflicts: ["deps-only"] }
50
53
  };
@@ -66,11 +69,23 @@ const JSON_OUTPUT_SCHEMA = {
66
69
  "summary.major": "Count of major updates",
67
70
  "summary.minor": "Count of minor updates",
68
71
  "summary.patch": "Count of patch updates",
72
+ "summary.failedResolutions": "Count of dependencies that failed to resolve",
69
73
  "meta.schemaVersion": "JSON schema version (currently 1)",
70
74
  "meta.cwd": "Working directory used",
75
+ "meta.effectiveRoot": "Derived project root used for discovery and root-aware operations",
71
76
  "meta.mode": "Version range mode used",
72
77
  "meta.timestamp": "ISO 8601 timestamp",
73
- "meta.didWrite": "Whether package files were written"
78
+ "meta.hadResolutionErrors": "Whether any dependency failed to resolve",
79
+ "meta.didWrite": "Whether package files were written",
80
+ discovery: "Optional discovery diagnostics block emitted when --explain-discovery is enabled",
81
+ "discovery.inputCwd": "Original cwd requested by the user",
82
+ "discovery.effectiveRoot": "Resolved root used for discovery",
83
+ "discovery.discoveryMode": "How the effective root was determined",
84
+ profile: "Optional runtime performance diagnostics block emitted when --profile is enabled",
85
+ "profile.discoveryMs": "Time spent discovering packages and catalogs",
86
+ "profile.resolutionMs": "Time spent resolving dependencies",
87
+ "profile.postWriteMs": "Time spent in execute/install/update post-write steps",
88
+ "profile.totalMs": "Total wall-clock time for the run"
74
89
  };
75
90
  function buildFlagDefinitions(argsDef) {
76
91
  const positional = {};
@@ -2,8 +2,8 @@ import { readFile, access } from 'node:fs/promises';
2
2
  import { pathToFileURL } from 'node:url';
3
3
  import { defu } from 'defu';
4
4
  import { join } from 'pathe';
5
- import { C as ConfigError } from '../shared/depfresh.CHHNqHXD.mjs';
6
- import { c as createLogger } from '../shared/depfresh.Dxgqypc6.mjs';
5
+ import { v as validateOptions, C as ConfigError } from '../shared/depfresh.CsTlbZg1.mjs';
6
+ import { r as resolveDiscoveryContext, c as createLogger } from '../shared/depfresh.DZ8SAjFx.mjs';
7
7
 
8
8
  const DEFAULT_OPTIONS = {
9
9
  cwd: ".",
@@ -34,9 +34,14 @@ const DEFAULT_OPTIONS = {
34
34
  nodecompat: true,
35
35
  long: false,
36
36
  explain: false,
37
+ explainDiscovery: false,
38
+ profile: false,
37
39
  failOnOutdated: false,
40
+ failOnResolutionErrors: false,
41
+ failOnNoPackages: false,
38
42
  install: false,
39
- update: false
43
+ update: false,
44
+ strictPostWrite: false
40
45
  };
41
46
 
42
47
  const TS_RE = /\.[mc]?ts$/;
@@ -113,9 +118,24 @@ async function loadConfigFile(cwd) {
113
118
  return void 0;
114
119
  }
115
120
  async function resolveConfig(overrides = {}) {
116
- const cwd = overrides.cwd || process.cwd();
117
- const fileConfig = await loadConfigFile(cwd);
121
+ const requestedCwd = overrides.cwd || process.cwd();
122
+ const discovery = resolveDiscoveryContext(requestedCwd);
123
+ const fileConfig = await loadConfigFile(discovery.effectiveRoot);
118
124
  const merged = defu(overrides, fileConfig ?? {}, DEFAULT_OPTIONS);
125
+ if (overrides.include !== void 0) {
126
+ merged.include = overrides.include;
127
+ }
128
+ if (overrides.exclude !== void 0) {
129
+ merged.exclude = overrides.exclude;
130
+ }
131
+ if (overrides.ignorePaths !== void 0) {
132
+ merged.ignorePaths = overrides.ignorePaths;
133
+ }
134
+ merged.cwd = discovery.inputCwd;
135
+ merged.inputCwd = discovery.inputCwd;
136
+ merged.effectiveRoot = discovery.effectiveRoot;
137
+ merged.discoveryMode = discovery.discoveryMode;
138
+ validateOptions(merged);
119
139
  const logger = createLogger(merged.loglevel);
120
140
  logger.debug("Config resolved:", JSON.stringify(merged, null, 2));
121
141
  return merged;
@@ -1,19 +1,21 @@
1
- import { c as check } from '../shared/depfresh.CTatBHRg.mjs';
2
- export { d as detectPackageManager } from '../shared/depfresh.CTatBHRg.mjs';
1
+ import { c as check } from '../shared/depfresh.BsXBwpPg.mjs';
2
+ export { d as detectPackageManager } from '../shared/depfresh.BsXBwpPg.mjs';
3
+ import 'node:perf_hooks';
3
4
  import 'ansis';
4
- import '../shared/depfresh.Dxgqypc6.mjs';
5
+ import '../shared/depfresh.DZ8SAjFx.mjs';
5
6
  import 'node:fs';
7
+ import 'pathe';
8
+ import 'yaml';
6
9
  import 'node:os';
7
10
  import 'find-up-simple';
8
11
  import 'ini';
9
- import 'pathe';
12
+ import '../shared/depfresh.CsTlbZg1.mjs';
10
13
  import './json-output.mjs';
11
14
  import 'node:child_process';
12
- import '../shared/depfresh.CHHNqHXD.mjs';
15
+ import 'node:path';
13
16
  import 'detect-indent';
14
17
  import 'semver';
15
18
  import 'pnpm-workspace-yaml';
16
- import 'yaml';
17
19
  import 'p-limit';
18
20
  import 'undici';
19
21
  import 'better-sqlite3';
@@ -1,19 +1,21 @@
1
1
  import * as readline from 'node:readline';
2
2
  import * as semver from 'semver';
3
- import { g as getVersionPrefix, f as applyVersionPrefix, h as getDiff, t as timeDifference, s as stripAnsi, i as truncate, j as padEnd, b as colorizeVersionDiff, a as arrow, e as colorDiff } from '../shared/depfresh.CTatBHRg.mjs';
3
+ import { g as getVersionPrefix, f as applyVersionPrefix, h as getDiff, t as timeDifference, s as stripAnsi, i as truncate, j as padEnd, b as colorizeVersionDiff, a as arrow, e as colorDiff } from '../shared/depfresh.BsXBwpPg.mjs';
4
4
  import c from 'ansis';
5
- import '../shared/depfresh.Dxgqypc6.mjs';
5
+ import 'node:perf_hooks';
6
+ import '../shared/depfresh.DZ8SAjFx.mjs';
6
7
  import 'node:fs';
8
+ import 'pathe';
9
+ import 'yaml';
7
10
  import 'node:os';
8
11
  import 'find-up-simple';
9
12
  import 'ini';
10
- import 'pathe';
13
+ import '../shared/depfresh.CsTlbZg1.mjs';
11
14
  import './json-output.mjs';
12
15
  import 'node:child_process';
13
- import '../shared/depfresh.CHHNqHXD.mjs';
16
+ import 'node:path';
14
17
  import 'detect-indent';
15
18
  import 'pnpm-workspace-yaml';
16
- import 'yaml';
17
19
  import 'p-limit';
18
20
  import 'undici';
19
21
  import 'better-sqlite3';
@@ -41,6 +43,7 @@ function prepareDetailVersions(dep, explain) {
41
43
  const deprecated = pkgData.deprecated?.[version];
42
44
  const nodeEngines = pkgData.engines?.[version];
43
45
  const provenance = pkgData.provenance?.[version];
46
+ const nodeIncompat = typeof nodeEngines === "string" && !semver.satisfies(process.version, nodeEngines);
44
47
  const result = { version, diff };
45
48
  if (age) result.age = age;
46
49
  if (distTag) result.distTag = distTag;
@@ -48,7 +51,7 @@ function prepareDetailVersions(dep, explain) {
48
51
  if (nodeEngines) result.nodeEngines = nodeEngines;
49
52
  if (provenance) result.provenance = provenance;
50
53
  if (explain) {
51
- result.explain = getExplanation(diff, deprecated, provenance === "none");
54
+ result.explain = getExplanation(diff, deprecated, provenance === "none", nodeIncompat);
52
55
  }
53
56
  return result;
54
57
  });
@@ -68,6 +71,7 @@ function getExplanation(diff, deprecated, provenanceDowngrade, nodeIncompat) {
68
71
  }
69
72
  if (deprecated) parts.push("Deprecated.");
70
73
  if (provenanceDowngrade) parts.push("Provenance downgrade.");
74
+ if (nodeIncompat) parts.push("Node incompatible.");
71
75
  return parts.join(" ");
72
76
  }
73
77
  function applyVersionSelection(dep, selectedVersion) {
@@ -150,6 +154,7 @@ function enterDetail(state) {
150
154
  ...state,
151
155
  view: "detail",
152
156
  detailDep: item.dep,
157
+ detailDepIndex: item.depIndex ?? null,
153
158
  detailVersions,
154
159
  detailCursor: 0,
155
160
  detailScrollOffset: calculateScrollOffset(
@@ -166,6 +171,7 @@ function exitDetail(state) {
166
171
  ...state,
167
172
  view: "list",
168
173
  detailDep: null,
174
+ detailDepIndex: null,
169
175
  detailVersions: [],
170
176
  detailCursor: 0,
171
177
  detailScrollOffset: 0
@@ -176,16 +182,19 @@ function selectDetailVersion(state) {
176
182
  const selected = state.detailVersions[state.detailCursor];
177
183
  if (!selected) return state;
178
184
  applyVersionSelection(state.detailDep, selected.version);
179
- const selectedNames = new Set(state.selectedNames);
180
- selectedNames.add(state.detailDep.name);
185
+ const selectedDepIndices = new Set(state.selectedDepIndices);
186
+ if (state.detailDepIndex !== null) {
187
+ selectedDepIndices.add(state.detailDepIndex);
188
+ }
181
189
  return {
182
190
  ...state,
183
191
  view: "list",
184
192
  detailDep: null,
193
+ detailDepIndex: null,
185
194
  detailVersions: [],
186
195
  detailCursor: 0,
187
196
  detailScrollOffset: 0,
188
- selectedNames
197
+ selectedDepIndices
189
198
  };
190
199
  }
191
200
  function moveDetailCursor(state, delta) {
@@ -248,10 +257,11 @@ function createInitialState(updates, options = {}) {
248
257
  cursor,
249
258
  scrollOffset: 0,
250
259
  detailDep: null,
260
+ detailDepIndex: null,
251
261
  detailVersions: [],
252
262
  detailCursor: 0,
253
263
  detailScrollOffset: 0,
254
- selectedNames: /* @__PURE__ */ new Set(),
264
+ selectedDepIndices: /* @__PURE__ */ new Set(),
255
265
  termRows,
256
266
  termCols,
257
267
  explain: options.explain ?? false,
@@ -308,28 +318,24 @@ function pageMove(state, direction) {
308
318
  function toggleSelection(state) {
309
319
  if (state.view !== "list") return state;
310
320
  const item = state.items[state.cursor];
311
- if (item?.type !== "dep" || !item.dep) return state;
312
- const selectedNames = new Set(state.selectedNames);
313
- if (selectedNames.has(item.dep.name)) {
314
- selectedNames.delete(item.dep.name);
321
+ if (item?.type !== "dep" || !item.dep || item.depIndex === void 0) return state;
322
+ const selectedDepIndices = new Set(state.selectedDepIndices);
323
+ if (selectedDepIndices.has(item.depIndex)) {
324
+ selectedDepIndices.delete(item.depIndex);
315
325
  } else {
316
- selectedNames.add(item.dep.name);
326
+ selectedDepIndices.add(item.depIndex);
317
327
  }
318
- return { ...state, selectedNames };
328
+ return { ...state, selectedDepIndices };
319
329
  }
320
330
  function toggleAll(state) {
321
- const depNames = Array.from(
322
- new Set(
323
- state.items.filter(
324
- (item) => item.type === "dep" && !!item.dep
325
- ).map((item) => item.dep.name)
326
- )
327
- );
328
- if (depNames.length === 0) return state;
329
- const allSelected = depNames.every((name) => state.selectedNames.has(name));
331
+ const depIndices = state.items.filter(
332
+ (item) => item.type === "dep" && !!item.dep && item.depIndex !== void 0
333
+ ).map((item) => item.depIndex);
334
+ if (depIndices.length === 0) return state;
335
+ const allSelected = depIndices.every((depIndex) => state.selectedDepIndices.has(depIndex));
330
336
  return {
331
337
  ...state,
332
- selectedNames: allSelected ? /* @__PURE__ */ new Set() : new Set(depNames)
338
+ selectedDepIndices: allSelected ? /* @__PURE__ */ new Set() : new Set(depIndices)
333
339
  };
334
340
  }
335
341
 
@@ -447,7 +453,7 @@ function renderListView(state) {
447
453
  }
448
454
  const totalDeps = depItems.length;
449
455
  const selectedCount = depItems.reduce(
450
- (count, item) => state.selectedNames.has(item.dep.name) ? count + 1 : count,
456
+ (count, item) => item.depIndex !== void 0 && state.selectedDepIndices.has(item.depIndex) ? count + 1 : count,
451
457
  0
452
458
  );
453
459
  lines.push(fitLine(` ${selectedCount}/${totalDeps} selected`, state.termCols));
@@ -463,7 +469,7 @@ function renderListLine(state, item, nameWidth) {
463
469
  return renderListDepLine(
464
470
  item.dep,
465
471
  item.index === state.cursor,
466
- state.selectedNames.has(item.dep.name),
472
+ item.depIndex !== void 0 && state.selectedDepIndices.has(item.depIndex),
467
473
  nameWidth,
468
474
  state.termCols
469
475
  );
@@ -554,7 +560,7 @@ function countFrameLines(frame) {
554
560
  return frame.endsWith("\n") ? parts.length - 1 : parts.length;
555
561
  }
556
562
  function getSelectedUpdates(state, updates) {
557
- return updates.filter((dep) => state.selectedNames.has(dep.name));
563
+ return updates.filter((_dep, depIndex) => state.selectedDepIndices.has(depIndex));
558
564
  }
559
565
  async function createInteractiveTUI(updates, options) {
560
566
  if (updates.length === 0) return [];
@@ -586,7 +592,12 @@ async function createInteractiveTUI(updates, options) {
586
592
  output.write(eraseLines(lastFrameLineCount));
587
593
  }
588
594
  output.write(SHOW_CURSOR);
589
- input.setRawMode(false);
595
+ if (typeof input.setRawMode === "function") {
596
+ try {
597
+ input.setRawMode(false);
598
+ } catch {
599
+ }
600
+ }
590
601
  input.pause();
591
602
  };
592
603
  const finish = (result) => {
@@ -1,19 +1,18 @@
1
1
  import './config.mjs';
2
- import '../shared/depfresh.CHHNqHXD.mjs';
2
+ import '../shared/depfresh.CsTlbZg1.mjs';
3
3
  import { execSync } from 'node:child_process';
4
- import { c as createLogger } from '../shared/depfresh.Dxgqypc6.mjs';
5
- import '../shared/depfresh.CTatBHRg.mjs';
4
+ import * as semver from 'semver';
5
+ import { c as createLogger } from '../shared/depfresh.DZ8SAjFx.mjs';
6
+ import '../shared/depfresh.BsXBwpPg.mjs';
6
7
 
7
8
  function addonVSCode(pkg, changes) {
8
9
  const vscodeChange = changes.find((c) => c.name === "@types/vscode");
9
10
  if (!vscodeChange) return;
10
11
  const raw = pkg.raw;
11
- if (raw.engines && typeof raw.engines === "object") {
12
- const engines = raw.engines;
13
- if (engines.vscode) {
14
- engines.vscode = `^${vscodeChange.targetVersion}`;
15
- }
16
- }
12
+ const engines = raw.engines && typeof raw.engines === "object" ? raw.engines : {};
13
+ const version = semver.coerce(vscodeChange.targetVersion)?.version ?? vscodeChange.targetVersion;
14
+ engines.vscode = `^${version}`;
15
+ raw.engines = engines;
17
16
  }
18
17
  function createVSCodeAddon() {
19
18
  return {
@@ -41,6 +40,15 @@ function dedupeGlobalPackageRecords(records) {
41
40
  continue;
42
41
  }
43
42
  existing.managers.add(record.manager);
43
+ if (semver.valid(record.version) && semver.valid(existing.version)) {
44
+ if (semver.lt(record.version, existing.version)) {
45
+ existing.version = record.version;
46
+ }
47
+ continue;
48
+ }
49
+ if (semver.valid(record.version) && !semver.valid(existing.version)) {
50
+ existing.version = record.version;
51
+ }
44
52
  }
45
53
  const sortedEntries = [...byName.entries()].sort(([a], [b]) => a.localeCompare(b));
46
54
  const packages = sortedEntries.map(([name, value]) => ({
@@ -91,10 +99,13 @@ function parseNpmGlobalList(json) {
91
99
  if (!data.dependencies || typeof data.dependencies !== "object") {
92
100
  return [];
93
101
  }
94
- return Object.entries(data.dependencies).map(([name, info]) => ({
95
- name,
96
- version: info.version
97
- }));
102
+ return Object.entries(data.dependencies).flatMap(([name, info]) => {
103
+ const version = info?.version;
104
+ if (typeof version !== "string" || semver.valid(version) === null) {
105
+ return [];
106
+ }
107
+ return [{ name, version }];
108
+ });
98
109
  } catch {
99
110
  return [];
100
111
  }
@@ -109,10 +120,13 @@ function parsePnpmGlobalList(json) {
109
120
  if (!deps || typeof deps !== "object") {
110
121
  return [];
111
122
  }
112
- return Object.entries(deps).map(([name, info]) => ({
113
- name,
114
- version: info.version
115
- }));
123
+ return Object.entries(deps).flatMap(([name, info]) => {
124
+ const version = info?.version;
125
+ if (typeof version !== "string" || semver.valid(version) === null) {
126
+ return [];
127
+ }
128
+ return [{ name, version }];
129
+ });
116
130
  } catch {
117
131
  return [];
118
132
  }
@@ -1,19 +1,21 @@
1
1
  import * as p from '@clack/prompts';
2
2
  import c from 'ansis';
3
- import { a as arrow, b as colorizeVersionDiff, e as colorDiff } from '../shared/depfresh.CTatBHRg.mjs';
4
- import '../shared/depfresh.Dxgqypc6.mjs';
3
+ import { s as stripAnsi, a as arrow, b as colorizeVersionDiff, e as colorDiff } from '../shared/depfresh.BsXBwpPg.mjs';
4
+ import 'node:perf_hooks';
5
+ import '../shared/depfresh.DZ8SAjFx.mjs';
5
6
  import 'node:fs';
7
+ import 'pathe';
8
+ import 'yaml';
6
9
  import 'node:os';
7
10
  import 'find-up-simple';
8
11
  import 'ini';
9
- import 'pathe';
12
+ import '../shared/depfresh.CsTlbZg1.mjs';
10
13
  import './json-output.mjs';
11
14
  import 'node:child_process';
12
- import '../shared/depfresh.CHHNqHXD.mjs';
15
+ import 'node:path';
13
16
  import 'detect-indent';
14
17
  import 'semver';
15
18
  import 'pnpm-workspace-yaml';
16
- import 'yaml';
17
19
  import 'p-limit';
18
20
  import 'undici';
19
21
  import 'better-sqlite3';
@@ -26,26 +28,47 @@ const GROUP_COLORS = {
26
28
  minor: c.yellow,
27
29
  patch: c.green
28
30
  };
29
- function makeOption(dep) {
31
+ function getSelectionValue(depIndex) {
32
+ return String(depIndex);
33
+ }
34
+ function makeOption(dep, depIndex) {
30
35
  return {
31
- value: dep.name,
36
+ value: getSelectionValue(depIndex),
32
37
  label: `${dep.name} ${dep.currentVersion}${arrow()}${colorizeVersionDiff(dep.currentVersion, dep.targetVersion, dep.diff)} ${colorDiff(dep.diff)}`,
33
38
  hint: dep.deprecated ? c.red("deprecated") : void 0
34
39
  };
35
40
  }
41
+ function getSelectedUpdates(updates, selectedValues, groupIndicesByLabel = /* @__PURE__ */ new Map()) {
42
+ const selectedIndices = /* @__PURE__ */ new Set();
43
+ for (const value of selectedValues) {
44
+ const index = Number(value);
45
+ if (Number.isInteger(index) && index >= 0) {
46
+ selectedIndices.add(index);
47
+ continue;
48
+ }
49
+ const groupedIndices = groupIndicesByLabel.get(value) ?? groupIndicesByLabel.get(stripAnsi(value));
50
+ if (groupedIndices) {
51
+ for (const depIndex of groupedIndices) {
52
+ selectedIndices.add(depIndex);
53
+ }
54
+ }
55
+ }
56
+ return updates.filter((_dep, depIndex) => selectedIndices.has(depIndex));
57
+ }
36
58
  async function runClackFallback(updates) {
37
59
  const grouped = /* @__PURE__ */ new Map();
38
- for (const dep of updates) {
60
+ const groupIndicesByLabel = /* @__PURE__ */ new Map();
61
+ for (const [depIndex, dep] of updates.entries()) {
39
62
  const existing = grouped.get(dep.diff);
40
63
  if (existing) {
41
- existing.push(dep);
64
+ existing.push({ dep, depIndex });
42
65
  } else {
43
- grouped.set(dep.diff, [dep]);
66
+ grouped.set(dep.diff, [{ dep, depIndex }]);
44
67
  }
45
68
  }
46
69
  const hasStandardGroups = DIFF_GROUP_ORDER.some((d) => grouped.has(d));
47
70
  if (!hasStandardGroups) {
48
- const options = updates.map(makeOption);
71
+ const options = updates.map((dep, depIndex) => makeOption(dep, depIndex));
49
72
  const selected2 = await p.multiselect({
50
73
  message: "Select dependencies to update",
51
74
  options,
@@ -55,7 +78,7 @@ async function runClackFallback(updates) {
55
78
  p.cancel("Update cancelled");
56
79
  return [];
57
80
  }
58
- return updates.filter((u) => selected2.includes(u.name));
81
+ return getSelectedUpdates(updates, selected2);
59
82
  }
60
83
  const groupOptions = {};
61
84
  for (const diffType of DIFF_GROUP_ORDER) {
@@ -63,7 +86,10 @@ async function runClackFallback(updates) {
63
86
  if (!deps) continue;
64
87
  const colorFn = GROUP_COLORS[diffType];
65
88
  const label = colorFn ? colorFn(diffType) : diffType;
66
- groupOptions[label] = deps.map(makeOption);
89
+ groupOptions[label] = deps.map(({ dep, depIndex }) => makeOption(dep, depIndex));
90
+ const indices = deps.map(({ depIndex }) => depIndex);
91
+ groupIndicesByLabel.set(label, indices);
92
+ groupIndicesByLabel.set(stripAnsi(label), indices);
67
93
  }
68
94
  const selected = await p.groupMultiselect({
69
95
  message: "Select dependencies to update",
@@ -75,13 +101,17 @@ async function runClackFallback(updates) {
75
101
  p.cancel("Update cancelled");
76
102
  return [];
77
103
  }
78
- return updates.filter((u) => selected.includes(u.name));
104
+ return getSelectedUpdates(updates, selected, groupIndicesByLabel);
79
105
  }
80
106
  async function runInteractive(updates, options) {
81
107
  if (updates.length === 0) return [];
82
- if (process.stdin.isTTY && process.stdout.isTTY) {
83
- const { createInteractiveTUI } = await import('./index2.mjs');
84
- return createInteractiveTUI(updates, { explain: options?.explain ?? false });
108
+ if (process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function") {
109
+ try {
110
+ const { createInteractiveTUI } = await import('./index2.mjs');
111
+ return await createInteractiveTUI(updates, { explain: options?.explain ?? false });
112
+ } catch {
113
+ return runClackFallback(updates);
114
+ }
85
115
  }
86
116
  return runClackFallback(updates);
87
117
  }
@@ -30,16 +30,21 @@ function outputJsonEnvelope(packages, options, executionState, errors = []) {
30
30
  packagesWithUpdates: executionState.packagesWithUpdates,
31
31
  plannedUpdates: executionState.plannedUpdates,
32
32
  appliedUpdates: executionState.appliedUpdates,
33
- revertedUpdates: executionState.revertedUpdates
33
+ revertedUpdates: executionState.revertedUpdates,
34
+ failedResolutions: executionState.failedResolutions
34
35
  },
35
36
  meta: {
36
37
  schemaVersion: 1,
37
38
  cwd: options.cwd,
39
+ effectiveRoot: options.effectiveRoot ?? options.cwd,
38
40
  mode: options.mode,
39
41
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
40
42
  noPackagesFound: executionState.noPackagesFound,
43
+ hadResolutionErrors: executionState.failedResolutions > 0,
41
44
  didWrite: executionState.didWrite
42
- }
45
+ },
46
+ ...options.explainDiscovery && options.discoveryReport ? { discovery: options.discoveryReport } : {},
47
+ ...options.profile && options.profileReport ? { profile: options.profileReport } : {}
43
48
  };
44
49
  console.log(JSON.stringify(output, null, 2));
45
50
  }
@@ -1,4 +1,4 @@
1
- import { C as ConfigError } from '../shared/depfresh.CHHNqHXD.mjs';
1
+ import { p as parseIntegerOption, C as ConfigError } from '../shared/depfresh.CsTlbZg1.mjs';
2
2
  import { a as VALID_SORT_OPTIONS, V as VALID_LOG_LEVELS, b as VALID_OUTPUTS, c as VALID_MODES } from '../shared/depfresh.CFLB6fqi.mjs';
3
3
 
4
4
  function parseCommaSeparatedArg(value) {
@@ -40,6 +40,8 @@ async function normalizeArgs(args) {
40
40
  const exclude = parseCommaSeparatedArg(args.exclude);
41
41
  const ignorePaths = parseCommaSeparatedArg(args["ignore-paths"]);
42
42
  const refreshCache = Boolean(args["refresh-cache"] || args["no-cache"]);
43
+ const concurrency = parseIntegerOption(args.concurrency, "--concurrency", 1);
44
+ const cooldown = parseIntegerOption(args.cooldown, "--cooldown", 0);
43
45
  return resolveConfig({
44
46
  cwd: args.cwd || process.cwd(),
45
47
  recursive: args.recursive,
@@ -56,22 +58,27 @@ async function normalizeArgs(args) {
56
58
  peer: args.peer,
57
59
  includeLocked: args["include-locked"],
58
60
  output,
59
- concurrency: Number.parseInt(args.concurrency, 10),
61
+ concurrency,
60
62
  loglevel,
61
63
  depFields,
62
64
  all: args.all,
63
65
  group: args.group,
64
66
  sort,
65
67
  timediff: args.timediff,
66
- cooldown: Number.parseInt(args.cooldown, 10),
68
+ cooldown,
67
69
  nodecompat: args.nodecompat,
68
70
  long: args.long,
69
71
  explain: args.explain,
72
+ explainDiscovery: args["explain-discovery"],
73
+ profile: args.profile,
70
74
  install: args.install,
71
75
  update: args.update,
76
+ strictPostWrite: args["strict-post-write"],
72
77
  execute: args.execute,
73
78
  verifyCommand: args["verify-command"],
74
79
  failOnOutdated: args["fail-on-outdated"],
80
+ failOnResolutionErrors: args["fail-on-resolution-errors"],
81
+ failOnNoPackages: args["fail-on-no-packages"],
75
82
  ignoreOtherWorkspaces: args["ignore-other-workspaces"]
76
83
  });
77
84
  }