@emeryld/manager 0.8.1 → 0.8.2

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.
@@ -100,12 +100,12 @@ function formatInteractiveLines(state, selectedIndex, title, searchState) {
100
100
  lines.push(colors.yellow('No scripts match your search.'));
101
101
  }
102
102
  lines.push('');
103
- lines.push(colors.dim(`Use ↑/↓ (or j/k) to move, 1-${PAGE_SIZE} to run, ${PREVIOUS_KEY} prev page (when shown), ${NEXT_KEY} next page (when shown), ${BACK_KEY} back, Enter to run, Space to filter, Esc/Ctrl+C to exit.`));
103
+ lines.push(colors.dim(`Use ↑/↓ (or j/k) to move, 1-${PAGE_SIZE} to run, ${PREVIOUS_KEY} prev page (when shown), ${NEXT_KEY} next page (when shown), ${BACK_KEY} back, Enter to run, Type to filter (numbers run scripts), Esc/Ctrl+C to exit.`));
104
104
  if (searchState?.active) {
105
105
  lines.push(colors.dim('Search mode: type to filter, Backspace edits the query, Esc returns to navigation, Enter runs the highlighted entry.'));
106
106
  }
107
107
  else {
108
- lines.push(colors.dim('Press Space to start typing a filter.'));
108
+ lines.push(colors.dim('Type any non-number character to start filtering.'));
109
109
  }
110
110
  return lines;
111
111
  }
@@ -230,13 +230,13 @@ export async function promptForScript(entries, title) {
230
230
  selectedIndex = 0;
231
231
  render();
232
232
  };
233
- const enterSearchMode = () => {
233
+ const enterSearchMode = (initialQuery = '') => {
234
234
  if (searchActive)
235
235
  return;
236
236
  pageBeforeSearch = state.page;
237
237
  selectionBeforeSearch = selectedIndex;
238
238
  searchActive = true;
239
- searchQuery = '';
239
+ searchQuery = initialQuery;
240
240
  applySearch();
241
241
  };
242
242
  const exitSearchMode = () => {
@@ -256,8 +256,10 @@ export async function promptForScript(entries, title) {
256
256
  const isEnter = buffer.length === 1 && (buffer[0] === 0x0d || buffer[0] === 0x0a);
257
257
  const isEscape = buffer.length === 1 && buffer[0] === 0x1b;
258
258
  const isBackspace = buffer.length === 1 && (buffer[0] === 0x7f || buffer[0] === 0x08);
259
- const isPrintable = buffer.length === 1 && buffer[0] >= 0x20 && buffer[0] <= 0x7e;
260
- const isSpace = buffer.length === 1 && buffer[0] === 0x20;
259
+ const ascii = buffer.length === 1 ? buffer[0] : undefined;
260
+ const isPrintable = ascii !== undefined && ascii >= 0x20 && ascii <= 0x7e;
261
+ const isDigit = ascii !== undefined && ascii >= 0x30 && ascii <= 0x39;
262
+ const typedChar = isPrintable && ascii !== undefined ? String.fromCharCode(ascii) : '';
261
263
  if (isCtrlC) {
262
264
  cleanup();
263
265
  process.exit(1);
@@ -270,10 +272,6 @@ export async function promptForScript(entries, title) {
270
272
  cleanup();
271
273
  process.exit(1);
272
274
  }
273
- if (!searchActive && isSpace) {
274
- enterSearchMode();
275
- return;
276
- }
277
275
  if (isArrowUp ||
278
276
  (buffer.length === 1 && (buffer[0] === 0x6b || buffer[0] === 0x4b))) {
279
277
  selectedIndex =
@@ -307,14 +305,14 @@ export async function promptForScript(entries, title) {
307
305
  return;
308
306
  }
309
307
  if (isPrintable) {
310
- searchQuery += String.fromCharCode(buffer[0]);
308
+ searchQuery += typedChar;
311
309
  applySearch();
312
310
  return;
313
311
  }
314
312
  return;
315
313
  }
316
- if (buffer.length === 1 && buffer[0] >= 0x30 && buffer[0] <= 0x39) {
317
- const numericValue = buffer[0] - 0x30;
314
+ if (isDigit && ascii !== undefined) {
315
+ const numericValue = ascii - 0x30;
318
316
  const option = state.options.find((opt) => opt.hotkey === numericValue);
319
317
  if (option)
320
318
  activateOption(option);
@@ -322,20 +320,8 @@ export async function promptForScript(entries, title) {
322
320
  process.stdout.write('\x07');
323
321
  return;
324
322
  }
325
- if (buffer.length === 1 &&
326
- ((buffer[0] >= 0x41 && buffer[0] <= 0x5a) ||
327
- (buffer[0] >= 0x61 && buffer[0] <= 0x7a))) {
328
- const char = String.fromCharCode(buffer[0]).toLowerCase();
329
- const foundIndex = baseEntries.findIndex((entry) => entry.displayName.toLowerCase().startsWith(char));
330
- if (foundIndex !== -1) {
331
- const page = Math.floor(foundIndex / PAGE_SIZE);
332
- state = buildVisibleOptions(filteredEntries, page);
333
- selectedIndex = foundIndex % PAGE_SIZE;
334
- render();
335
- }
336
- else {
337
- process.stdout.write('\x07');
338
- }
323
+ if (isPrintable) {
324
+ enterSearchMode(typedChar);
339
325
  return;
340
326
  }
341
327
  process.stdout.write('\x07');
@@ -0,0 +1,7 @@
1
+ export function packageFilterArg(pkg) {
2
+ if (pkg.name)
3
+ return pkg.name;
4
+ if (!pkg.relativeDir || pkg.relativeDir === '.')
5
+ return '.';
6
+ return `./${pkg.relativeDir}`;
7
+ }
@@ -1,6 +1,7 @@
1
1
  import { askLine } from './prompts.js';
2
2
  import { run } from './utils/run.js';
3
3
  import { colors, logGlobal } from './utils/log.js';
4
+ import { packageFilterArg } from './utils/package-filter.js';
4
5
  const DEP_FIELDS = ['dependencies', 'devDependencies', 'peerDependencies'];
5
6
  const FIELD_LABELS = {
6
7
  dependencies: 'deps',
@@ -49,17 +50,35 @@ function getMostCommonVersion(versions) {
49
50
  }
50
51
  return best;
51
52
  }
52
- function hasWorkspaceScope(targets, allPackages, feature) {
53
+ function ensureVersionControlTargets(targets, allPackages, feature) {
53
54
  if (allPackages.length === 0) {
54
55
  logGlobal('No packages found to run version control actions.', colors.yellow);
55
56
  return false;
56
57
  }
57
- if (targets.length !== allPackages.length) {
58
- logGlobal(`${feature} works across the whole workspace; select "All packages" first.`, colors.yellow);
58
+ if (targets.length === 0) {
59
+ logGlobal(`Select at least one package before running ${feature}.`, colors.yellow);
59
60
  return false;
60
61
  }
61
62
  return true;
62
63
  }
64
+ function buildPackageFilterArgs(targets, allPackages) {
65
+ if (!targets.length || targets.length === allPackages.length) {
66
+ return [];
67
+ }
68
+ const seen = new Set();
69
+ const args = [];
70
+ for (const pkg of targets) {
71
+ const filterArg = packageFilterArg(pkg);
72
+ if (seen.has(filterArg))
73
+ continue;
74
+ seen.add(filterArg);
75
+ args.push('--filter', filterArg);
76
+ }
77
+ return args;
78
+ }
79
+ function buildRecursiveUpdateArgs(targets, allPackages, spec) {
80
+ return ['update', '-r', ...buildPackageFilterArgs(targets, allPackages), spec];
81
+ }
63
82
  function formatConflictDetails(versions) {
64
83
  return [...versions.entries()]
65
84
  .map(([range, entries]) => {
@@ -73,7 +92,7 @@ function formatConflictDetails(versions) {
73
92
  .join(' | ');
74
93
  }
75
94
  async function updateByPattern(targets, allPackages) {
76
- if (!hasWorkspaceScope(targets, allPackages, 'Regex dependency update'))
95
+ if (!ensureVersionControlTargets(targets, allPackages, 'Regex dependency update'))
77
96
  return;
78
97
  const scopeLabel = describeVersionControlScope(targets, allPackages);
79
98
  const defaultPattern = '@emeryld/*';
@@ -90,15 +109,15 @@ async function updateByPattern(targets, allPackages) {
90
109
  ? rawPattern
91
110
  : `${rawPattern}@${normalizedVersion}`;
92
111
  logGlobal(`Updating dependencies using ${colors.cyan(spec)} for ${scopeLabel}…`, colors.cyan);
93
- await run('pnpm', ['update', '-r', spec]);
112
+ await run('pnpm', buildRecursiveUpdateArgs(targets, allPackages, spec));
94
113
  logGlobal('Pattern update complete.', colors.green);
95
114
  }
96
115
  async function runVersionHealthScan(targets, allPackages) {
97
- if (!hasWorkspaceScope(targets, allPackages, 'Version health scan'))
116
+ if (!ensureVersionControlTargets(targets, allPackages, 'Version health scan'))
98
117
  return;
99
118
  const scopeLabel = describeVersionControlScope(targets, allPackages);
100
119
  logGlobal(`Scanning version misalignments for ${scopeLabel}…`, colors.cyan);
101
- const dependencyMap = collectDependencyMap(allPackages);
120
+ const dependencyMap = collectDependencyMap(targets);
102
121
  const conflicts = [...dependencyMap.entries()].filter(([, versions]) => versions.size > 1);
103
122
  if (conflicts.length === 0) {
104
123
  logGlobal('No version conflicts detected.', colors.green);
@@ -110,12 +129,12 @@ async function runVersionHealthScan(targets, allPackages) {
110
129
  });
111
130
  }
112
131
  async function alignDependencyVersions(targets, allPackages) {
113
- if (!hasWorkspaceScope(targets, allPackages, 'Dependency alignment'))
132
+ if (!ensureVersionControlTargets(targets, allPackages, 'Dependency alignment'))
114
133
  return;
115
134
  const scopeLabel = describeVersionControlScope(targets, allPackages);
116
- const dependencyMap = collectDependencyMap(allPackages);
135
+ const dependencyMap = collectDependencyMap(targets);
117
136
  if (!dependencyMap.size) {
118
- logGlobal('No dependencies found in the workspace.', colors.yellow);
137
+ logGlobal(`No dependencies found in ${scopeLabel}.`, colors.yellow);
119
138
  return;
120
139
  }
121
140
  const misaligned = [...dependencyMap.entries()]
@@ -131,7 +150,7 @@ async function alignDependencyVersions(targets, allPackages) {
131
150
  const dependency = dependencyInput || defaultDependency;
132
151
  const versions = dependencyMap.get(dependency);
133
152
  if (!versions) {
134
- logGlobal(`Dependency "${dependency}" not tracked across the workspace.`, colors.yellow);
153
+ logGlobal(`Dependency "${dependency}" not tracked across ${scopeLabel}.`, colors.yellow);
135
154
  return;
136
155
  }
137
156
  const defaultVersion = getMostCommonVersion(versions) ?? [...versions.keys()][0];
@@ -146,15 +165,15 @@ async function alignDependencyVersions(targets, allPackages) {
146
165
  }
147
166
  const spec = `${dependency}@${targetVersion}`;
148
167
  logGlobal(`Aligning ${colors.cyan(dependency)} to ${colors.cyan(targetVersion)} across ${scopeLabel}…`, colors.cyan);
149
- await run('pnpm', ['update', '-r', spec]);
168
+ await run('pnpm', buildRecursiveUpdateArgs(targets, allPackages, spec));
150
169
  logGlobal('Dependency alignment complete.', colors.green);
151
170
  }
152
171
  async function showDriftReport(targets, allPackages) {
153
- if (!hasWorkspaceScope(targets, allPackages, 'Drift report'))
172
+ if (!ensureVersionControlTargets(targets, allPackages, 'Drift report'))
154
173
  return;
155
174
  const scopeLabel = describeVersionControlScope(targets, allPackages);
156
175
  logGlobal(`Computing drift report for ${scopeLabel}…`, colors.cyan);
157
- const dependencyMap = collectDependencyMap(allPackages);
176
+ const dependencyMap = collectDependencyMap(targets);
158
177
  const driftLines = [];
159
178
  for (const [dep, versions] of dependencyMap.entries()) {
160
179
  if (versions.size <= 1)
@@ -176,7 +195,7 @@ async function showDriftReport(targets, allPackages) {
176
195
  driftLines.forEach((line) => console.log(` ${line}`));
177
196
  }
178
197
  async function checkLockfile(targets, allPackages) {
179
- if (!hasWorkspaceScope(targets, allPackages, 'Lockfile check'))
198
+ if (!ensureVersionControlTargets(targets, allPackages, 'Lockfile check'))
180
199
  return;
181
200
  const scopeLabel = describeVersionControlScope(targets, allPackages);
182
201
  logGlobal(`Verifying lockfile matches the ${scopeLabel} manifests…`, colors.cyan);
package/dist/workspace.js CHANGED
@@ -4,6 +4,7 @@ import { rm } from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
  import { run, rootDir } from './utils/run.js';
6
6
  import { logGlobal, logPkg, colors } from './utils/log.js';
7
+ import { packageFilterArg } from './utils/package-filter.js';
7
8
  import { collectGitStatus, gitAdd, gitCommit } from './git.js';
8
9
  import { askLine, promptSingleKey } from './prompts.js';
9
10
  const dependencyFiles = new Set([
@@ -186,13 +187,6 @@ async function promptCommitMessage(proposed) {
186
187
  }
187
188
  return custom.trim();
188
189
  }
189
- function packageFilterArg(pkg) {
190
- if (pkg.name)
191
- return pkg.name;
192
- if (!pkg.relativeDir || pkg.relativeDir === '.')
193
- return '.';
194
- return `./${pkg.relativeDir}`;
195
- }
196
190
  async function runLifecycleScenario(pkg, scenario) {
197
191
  if (pkg) {
198
192
  logPkg(pkg, scenario.singleMessage);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emeryld/manager",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Interactive manager for pnpm monorepos (update/test/build/publish).",
5
5
  "license": "MIT",
6
6
  "type": "module",