@wpmoo/toolkit 0.9.22 → 0.9.23

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/cli.js CHANGED
@@ -38,7 +38,7 @@ import { confirmPrompt, introPrompt, isPromptCancel, notePrompt, outroPrompt, se
38
38
  import { renderBanner } from './templates.js';
39
39
  import { checkForUpdate, isUpdateCheckSkipped, restartCli } from './update-check.js';
40
40
  import { packageName, packageVersion, renderVersion, renderVersionTag } from './version.js';
41
- import { environmentStatusJson, getEnvironmentStatus, renderEnvironmentStatusForTarget, renderEnvironmentStatusSummary, } from './status.js';
41
+ import { environmentStatusJson, getEnvironmentStatus, environmentBannerSummaryLine, renderEnvironmentStatusForTarget, } from './status.js';
42
42
  import { getGitHubAccounts, getGitHubRepositoryStatus, githubRepositoryUrl, realGitHub, createGitHubRepository, } from './github.js';
43
43
  import { environmentGitHubOwner } from './environment-context.js';
44
44
  import { handlePromptCancel, handleUnavailableMenuChoice, installPromptCancelKeyTracker, isMenuBackSignal, MenuBackSignal, menuIntroTitle, menuPromptMessage, } from './menu-navigation.js';
@@ -184,27 +184,16 @@ function validateRepoName(value) {
184
184
  function startupVersionLine(latestVersion) {
185
185
  return `v${packageVersion()}${latestVersion ? ` -> v${latestVersion} available` : ''}`;
186
186
  }
187
- function pluralize(count, singular, plural) {
188
- return `${count} ${count === 1 ? singular : plural}`;
189
- }
190
- function renderStartupEnvironmentLine(status) {
191
- if (status.kind !== 'environment') {
192
- return `Environment: ${renderEnvironmentStatusSummary(status)}`;
193
- }
194
- const issueCount = status.composeErrors.length + status.invalidSourceRepoPaths.length + status.missingCoreFiles.length;
195
- const issueSuffix = issueCount > 0 ? ` · ${pluralize(issueCount, 'issue', 'issues')}` : '';
196
- return [
197
- `Environment: Odoo ${status.odooVersion}`,
198
- pluralize(status.sourceRepoCount, 'repo', 'repos'),
199
- pluralize(status.moduleCandidateCount, 'module', 'modules'),
200
- ].join(' · ') + issueSuffix;
201
- }
202
187
  function renderStartupBanner(details, latestVersion) {
203
188
  const versionLine = startupVersionLine(latestVersion);
204
189
  return renderBanner(details?.(versionLine), details ? { version: versionLine } : undefined);
205
190
  }
206
191
  function renderCockpitStatusLines(status, serviceStatus, lastStatus) {
207
- return [renderStartupEnvironmentLine(status), renderServiceRuntimeStatusLine(serviceStatus), lastStatus];
192
+ return [
193
+ environmentBannerSummaryLine(status),
194
+ renderServiceRuntimeStatusLine(serviceStatus),
195
+ lastStatus,
196
+ ];
208
197
  }
209
198
  function renderLastCommandStatus(command) {
210
199
  return `Last: ${command.label} ✓ completed`;
@@ -29,41 +29,40 @@ function internalCommand(id, category, label, description, aliases = []) {
29
29
  };
30
30
  }
31
31
  export const cockpitCommands = [
32
- dailyCommand('start', 'services', 'Start services', 'Start the Odoo development services.', ['up', 'compose up']),
33
- dailyCommand('stop', 'services', 'Stop services', 'Stop the Odoo development services.', ['down', 'compose down']),
34
- dailyCommand('restart', 'services', 'Restart services', 'Restart the Odoo development services.', ['reload']),
35
- dailyCommand('logs', 'services', 'View logs', 'Stream logs for an Odoo environment service.', ['log', 'tail']),
36
- dailyCommand('shell', 'services', 'Open shell', 'Open a shell inside the Odoo service container.', ['bash', 'terminal']),
32
+ dailyCommand('start', 'services', 'Start services', 'Start Odoo services.', ['up', 'compose up']),
33
+ dailyCommand('stop', 'services', 'Stop services', 'Stop Odoo services.', ['down', 'compose down']),
34
+ dailyCommand('restart', 'services', 'Restart services', 'Restart Odoo services.', ['reload']),
35
+ dailyCommand('logs', 'services', 'View logs', 'Tail service logs.', ['log', 'tail']),
36
+ dailyCommand('shell', 'services', 'Open shell', 'Open a service shell.', ['bash', 'terminal']),
37
37
  internalCommand('list-modules', 'modules', 'List modules', 'Browse detected Odoo modules by source category.', [
38
38
  'modules list',
39
39
  'browse modules',
40
+ '/module',
41
+ 'module',
40
42
  ]),
41
- dailyCommand('install', 'modules', 'Install module', 'Install one or more Odoo modules into a database.', ['install module']),
42
- dailyCommand('update', 'modules', 'Update module', 'Update one or more Odoo modules in a database.', ['upgrade']),
43
- dailyCommand('test', 'modules', 'Run tests', 'Run Odoo tests for one or more modules.', ['tests', 'pytest']),
44
- dailyCommand('lint', 'modules', 'Run environment lint', 'Run the configured environment lint checks.', ['check', 'quality']),
45
- dailyCommand('pot', 'modules', 'Generate POT', 'Generate translation template files for a module.', ['translation', 'i18n']),
46
- dailyCommand('psql', 'database', 'Open psql', 'Open a PostgreSQL prompt for an environment database.', ['postgres', 'sql']),
47
- dailyCommand('snapshot', 'database', 'Create snapshot', 'Create a database snapshot.', ['backup', 'dump']),
48
- dailyCommand('restore-snapshot', 'database', 'Restore snapshot', 'Restore a database from a named snapshot.', ['restore', 'snapshot restore']),
49
- dailyCommand('resetdb', 'database', 'Reset database', 'Reset an environment database.', ['reset db', 'database reset']),
43
+ dailyCommand('install', 'modules', 'Install module', 'Install modules in the database.', ['install module', 'module']),
44
+ dailyCommand('update', 'modules', 'Update module', 'Update modules in the database.', ['upgrade', 'module']),
45
+ dailyCommand('test', 'modules', 'Run tests', 'Run tests for selected modules.', ['tests', 'pytest', 'module']),
46
+ dailyCommand('lint', 'modules', 'Run environment lint', 'Run environment lint checks.', ['check', 'quality']),
47
+ dailyCommand('pot', 'modules', 'Generate POT', 'Generate module translation templates.', ['translation', 'i18n']),
48
+ dailyCommand('psql', 'database', 'Open psql', 'Open PostgreSQL prompt.', ['postgres', 'sql', '/db']),
49
+ dailyCommand('snapshot', 'database', 'Create snapshot', 'Create a database snapshot.', ['backup', 'dump', '/snapshot']),
50
+ dailyCommand('restore-snapshot', 'database', 'Restore snapshot', 'Restore a named snapshot.', ['restore', 'snapshot restore']),
51
+ dailyCommand('resetdb', 'database', 'Reset database', 'Reset the environment database.', ['reset db', 'database reset']),
50
52
  internalCommand('status', 'diagnostics', 'Environment status', 'Show a summary of the current environment state.', [
51
53
  'state',
52
54
  'summary',
53
55
  ]),
54
- internalCommand('doctor', 'diagnostics', 'Run doctor', 'Run environment diagnostics and report actionable issues.', [
55
- 'diagnose',
56
- 'health',
57
- ]),
58
- internalCommand('add-repo', 'repositories', 'Add source repo', 'Add a source repository as an environment submodule.', [
56
+ internalCommand('doctor', 'diagnostics', 'Run doctor', 'Run environment diagnostics.', ['diagnose', 'health']),
57
+ internalCommand('add-repo', 'repositories', 'Add source repo', 'Add a source repository.', [
59
58
  'repository add',
60
59
  'source add',
61
60
  ]),
62
- internalCommand('remove-repo', 'repositories', 'Remove source repo', 'Remove a source repository from the environment.', ['repository remove', 'source remove']),
63
- internalCommand('add-module', 'modules', 'Add module', 'Add a module folder to a source repository.', ['module add']),
64
- internalCommand('remove-module', 'modules', 'Remove module', 'Remove a module folder from a source repository.', ['module remove']),
65
- internalCommand('safe-reset', 'maintenance', 'Safe reset environment', 'Refresh generated environment files while preserving source repositories.', ['reset', 'refresh']),
66
- internalCommand('exit', 'maintenance', 'Exit', 'Leave the command palette.', ['quit', 'back']),
61
+ internalCommand('remove-repo', 'repositories', 'Remove source repo', 'Remove a source repository.', ['repository remove', 'source remove']),
62
+ internalCommand('add-module', 'modules', 'Add module', 'Add a module to a source repository.', ['module add']),
63
+ internalCommand('remove-module', 'modules', 'Remove module', 'Remove a module from a source repository.', ['module remove']),
64
+ internalCommand('safe-reset', 'maintenance', 'Safe reset environment', 'Refresh generated files only.', ['reset', 'refresh', '/safe']),
65
+ internalCommand('exit', 'maintenance', 'Exit', 'Close the command palette.', ['quit', 'back']),
67
66
  ];
68
67
  const defaultCommandIds = new Set(['start', 'logs', 'test', 'status', 'doctor', 'exit']);
69
68
  export function normalizeCockpitSearchTerm(term) {
@@ -33,6 +33,21 @@ function categoryHeading(category) {
33
33
  function commandName(command) {
34
34
  return `${rgb(226, 184, 96, ` ${command.label.padEnd(topLevelCommandLabelWidth)}`)}${dim(` ${command.description}`)}`;
35
35
  }
36
+ const disabledReasonNextStep = {
37
+ 'No modules found.': 'Next: choose "Add module" first.',
38
+ 'Services stopped.': 'Next: choose "Start services" first.',
39
+ 'Already running.': 'Next: choose "Stop services" or "Restart services".',
40
+ 'Docker not running.': 'Next: start Docker, then choose "Start services".',
41
+ 'No source repos found.': 'Next: choose "Add source repo" first.',
42
+ };
43
+ function disabledError(reason) {
44
+ const base = 'This option is disabled and cannot be selected.';
45
+ if (!reason) {
46
+ return base;
47
+ }
48
+ const nextStep = disabledReasonNextStep[reason];
49
+ return nextStep ? `${base}\nReason: ${reason}\n${nextStep}` : `${base}\nReason: ${reason}`;
50
+ }
36
51
  function serviceDisabledReason(command, serviceStatus) {
37
52
  if (command.category !== 'services' || !serviceStatus)
38
53
  return undefined;
@@ -56,9 +71,6 @@ function disabledReason(command, serviceStatus, moduleCount, sourceRepoCount) {
56
71
  moduleDisabledReason(command, moduleCount) ??
57
72
  sourceRepoDisabledReason(command, sourceRepoCount));
58
73
  }
59
- function disabledError() {
60
- return 'This option is disabled and cannot be selected.';
61
- }
62
74
  function commandDisabledValue(reason) {
63
75
  if (!reason) {
64
76
  return undefined;
@@ -131,7 +143,7 @@ export async function selectCockpitTopLevelMenu(options = {}) {
131
143
  pageSize: topLevelPageSize(choices.length),
132
144
  loop: false,
133
145
  hideMessage: true,
134
- disabledError: disabledError(),
146
+ disabledError,
135
147
  navigationWarning: options.navigationWarning,
136
148
  escapeBehavior: 'ignore',
137
149
  });
@@ -171,10 +171,13 @@ function hiddenSelectTheme(disabledError, navigationHelp = 'exit', navigationWar
171
171
  };
172
172
  }
173
173
  function disabledErrorI18n(disabledError, activeReason) {
174
- const i18n = { disabledError };
174
+ const i18n = typeof disabledError === 'string' ? { disabledError } : { disabledError: disabledError(undefined) };
175
175
  Object.defineProperty(i18n, 'disabledError', {
176
176
  get: () => {
177
177
  const reason = activeReason();
178
+ if (typeof disabledError === 'function') {
179
+ return disabledError(reason);
180
+ }
178
181
  return reason ? `${disabledError}\nReason: ${reason}` : disabledError;
179
182
  },
180
183
  });
package/dist/status.js CHANGED
@@ -5,6 +5,10 @@ import { defaultOdooVersion, markerPath } from './environment.js';
5
5
  import { emptyModuleQualitySummary, mergeModuleQualitySummaries, scanModuleQuality, } from './module-quality.js';
6
6
  import { isValidPathSegment, validateRepoPath } from './path-validation.js';
7
7
  const validSourceTypes = ['private', 'oca', 'external'];
8
+ const summarySeparator = ' \u00B7 ';
9
+ function pluralize(count, singular, plural) {
10
+ return `${count} ${count === 1 ? singular : plural}`;
11
+ }
8
12
  function normalizeSourceType(sourceType) {
9
13
  if (typeof sourceType === 'string' && validSourceTypes.includes(sourceType)) {
10
14
  return sourceType;
@@ -85,6 +89,18 @@ async function missingCoreFiles(target, odooVersion) {
85
89
  missing.push(...composeLayout.missingFiles);
86
90
  return { missing, composeFiles: composeLayout.files, composeErrors: composeLayout.errors };
87
91
  }
92
+ export function environmentBannerSummaryLine(status) {
93
+ if (status.kind !== 'environment') {
94
+ return `Environment: ${summaryText(status)}`;
95
+ }
96
+ const issueCount = status.composeErrors.length + status.invalidSourceRepoPaths.length + status.missingCoreFiles.length;
97
+ const issueSuffix = issueCount > 0 ? `${summarySeparator}${pluralize(issueCount, 'issue', 'issues')}` : '';
98
+ return [
99
+ `Environment: Odoo ${status.odooVersion}`,
100
+ pluralize(status.sourceRepoCount, 'repo', 'repos'),
101
+ pluralize(status.moduleCandidateCount, 'module', 'modules'),
102
+ ].join(summarySeparator) + issueSuffix;
103
+ }
88
104
  function summaryText(status) {
89
105
  if (status.kind === 'no_environment')
90
106
  return 'No WPMoo environment detected.';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wpmoo/toolkit",
3
- "version": "0.9.22",
3
+ "version": "0.9.23",
4
4
  "description": "WPMoo Toolkit for development, staging, and production lifecycle workflows.",
5
5
  "type": "module",
6
6
  "repository": {