@mondaydotcomorg/z2h-cli 0.15.0 → 0.15.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.
Files changed (110) hide show
  1. package/dist/commands/build.d.ts +0 -1
  2. package/dist/commands/build.d.ts.map +1 -1
  3. package/dist/commands/build.js +0 -3
  4. package/dist/commands/clean.d.ts +1 -4
  5. package/dist/commands/clean.d.ts.map +1 -1
  6. package/dist/commands/clean.js +1 -4
  7. package/dist/commands/create-workspace.d.ts +1 -4
  8. package/dist/commands/create-workspace.d.ts.map +1 -1
  9. package/dist/commands/create-workspace.js +1 -4
  10. package/dist/commands/create.d.ts +0 -1
  11. package/dist/commands/create.d.ts.map +1 -1
  12. package/dist/commands/create.js +0 -3
  13. package/dist/commands/delete.d.ts +0 -1
  14. package/dist/commands/delete.d.ts.map +1 -1
  15. package/dist/commands/delete.js +1 -4
  16. package/dist/commands/deploy.d.ts +0 -1
  17. package/dist/commands/deploy.d.ts.map +1 -1
  18. package/dist/commands/deploy.js +4 -7
  19. package/dist/commands/dev.d.ts +0 -1
  20. package/dist/commands/dev.d.ts.map +1 -1
  21. package/dist/commands/dev.js +0 -3
  22. package/dist/commands/doctor.d.ts +1 -4
  23. package/dist/commands/doctor.d.ts.map +1 -1
  24. package/dist/commands/doctor.js +2 -5
  25. package/dist/commands/edit.d.ts +1 -4
  26. package/dist/commands/edit.d.ts.map +1 -1
  27. package/dist/commands/edit.js +1 -4
  28. package/dist/commands/generate-z2h-token.d.ts +0 -1
  29. package/dist/commands/generate-z2h-token.d.ts.map +1 -1
  30. package/dist/commands/generate-z2h-token.js +0 -3
  31. package/dist/commands/grant.d.ts +1 -5
  32. package/dist/commands/grant.d.ts.map +1 -1
  33. package/dist/commands/grant.js +1 -10
  34. package/dist/commands/migrate.d.ts +0 -1
  35. package/dist/commands/migrate.d.ts.map +1 -1
  36. package/dist/commands/migrate.js +106 -4
  37. package/dist/commands/tag.d.ts +0 -1
  38. package/dist/commands/tag.d.ts.map +1 -1
  39. package/dist/commands/tag.js +1 -4
  40. package/dist/esm/commands/build.d.ts +0 -1
  41. package/dist/esm/commands/build.d.ts.map +1 -1
  42. package/dist/esm/commands/build.mjs +1 -4
  43. package/dist/esm/commands/clean.d.ts +1 -4
  44. package/dist/esm/commands/clean.d.ts.map +1 -1
  45. package/dist/esm/commands/clean.mjs +2 -5
  46. package/dist/esm/commands/create-workspace.d.ts +1 -4
  47. package/dist/esm/commands/create-workspace.d.ts.map +1 -1
  48. package/dist/esm/commands/create-workspace.mjs +2 -5
  49. package/dist/esm/commands/create.d.ts +0 -1
  50. package/dist/esm/commands/create.d.ts.map +1 -1
  51. package/dist/esm/commands/create.mjs +1 -4
  52. package/dist/esm/commands/delete.d.ts +0 -1
  53. package/dist/esm/commands/delete.d.ts.map +1 -1
  54. package/dist/esm/commands/delete.mjs +2 -5
  55. package/dist/esm/commands/deploy.d.ts +0 -1
  56. package/dist/esm/commands/deploy.d.ts.map +1 -1
  57. package/dist/esm/commands/deploy.mjs +5 -8
  58. package/dist/esm/commands/dev.d.ts +0 -1
  59. package/dist/esm/commands/dev.d.ts.map +1 -1
  60. package/dist/esm/commands/dev.mjs +1 -4
  61. package/dist/esm/commands/doctor.d.ts +1 -4
  62. package/dist/esm/commands/doctor.d.ts.map +1 -1
  63. package/dist/esm/commands/doctor.mjs +3 -6
  64. package/dist/esm/commands/edit.d.ts +1 -4
  65. package/dist/esm/commands/edit.d.ts.map +1 -1
  66. package/dist/esm/commands/edit.mjs +2 -5
  67. package/dist/esm/commands/generate-z2h-token.d.ts +0 -1
  68. package/dist/esm/commands/generate-z2h-token.d.ts.map +1 -1
  69. package/dist/esm/commands/generate-z2h-token.mjs +1 -4
  70. package/dist/esm/commands/grant.d.ts +1 -5
  71. package/dist/esm/commands/grant.d.ts.map +1 -1
  72. package/dist/esm/commands/grant.mjs +2 -11
  73. package/dist/esm/commands/migrate.d.ts +0 -1
  74. package/dist/esm/commands/migrate.d.ts.map +1 -1
  75. package/dist/esm/commands/migrate.mjs +101 -5
  76. package/dist/esm/commands/tag.d.ts +0 -1
  77. package/dist/esm/commands/tag.d.ts.map +1 -1
  78. package/dist/esm/commands/tag.mjs +2 -5
  79. package/dist/esm/index.mjs +20 -29
  80. package/dist/esm/util/workspace.d.ts +3 -0
  81. package/dist/esm/util/workspace.d.ts.map +1 -1
  82. package/dist/esm/util/workspace.mjs +64 -2
  83. package/dist/index.js +19 -28
  84. package/dist/util/workspace.d.ts +3 -0
  85. package/dist/util/workspace.d.ts.map +1 -1
  86. package/dist/util/workspace.js +65 -0
  87. package/package.json +1 -1
  88. package/src/commands/__tests__/doctor.test.ts +12 -5
  89. package/src/commands/build.ts +1 -6
  90. package/src/commands/clean.ts +2 -9
  91. package/src/commands/create-workspace.ts +2 -10
  92. package/src/commands/create.ts +1 -6
  93. package/src/commands/delete.ts +2 -7
  94. package/src/commands/deploy.ts +5 -10
  95. package/src/commands/dev.ts +1 -6
  96. package/src/commands/doctor.ts +3 -11
  97. package/src/commands/edit.ts +2 -10
  98. package/src/commands/generate-z2h-token.ts +1 -6
  99. package/src/commands/grant.ts +2 -16
  100. package/src/commands/migrate.ts +139 -6
  101. package/src/commands/tag.ts +2 -7
  102. package/src/index.ts +29 -41
  103. package/src/util/workspace.ts +75 -1
  104. package/dist/esm/migrations/auto-migrate.d.ts +0 -2
  105. package/dist/esm/migrations/auto-migrate.d.ts.map +0 -1
  106. package/dist/esm/migrations/auto-migrate.mjs +0 -169
  107. package/dist/migrations/auto-migrate.d.ts +0 -2
  108. package/dist/migrations/auto-migrate.d.ts.map +0 -1
  109. package/dist/migrations/auto-migrate.js +0 -177
  110. package/src/migrations/auto-migrate.ts +0 -217
@@ -17,7 +17,7 @@ import { migrateCommand } from './commands/migrate.mjs';
17
17
  import { tagCommand } from './commands/tag.mjs';
18
18
  import { editCommand } from './commands/edit.mjs';
19
19
  import { doctorCommand } from './commands/doctor.mjs';
20
- import { fail, errorLog, getOutputMode } from './util/logger.mjs';
20
+ import { fail, errorLog, setOutputMode, getOutputMode } from './util/logger.mjs';
21
21
  import { trackDuration } from './util/tracker.mjs';
22
22
  import { getCwdAppName } from './util/app-name.mjs';
23
23
 
@@ -61,14 +61,19 @@ async function main() {
61
61
  ' 1 user-resolvable error (bad cwd, invalid package.json, lock held)',
62
62
  ' 2 system error (yarn install or trident build failed)',
63
63
  ].join('\n'))
64
- .version(version);
64
+ .version(version)
65
+ .option('--json', 'emit structured JSON on stdout')
66
+ .hook('preAction', (_thisCommand, actionCommand) => {
67
+ if (actionCommand.optsWithGlobals().json) {
68
+ setOutputMode('json');
69
+ }
70
+ });
65
71
  program
66
72
  .command('build')
67
73
  .description('Build the consumer into its .zth/ shadow and copy artifacts to ./build/')
68
- .option('--json', 'emit structured JSON on stdout')
69
- .action(async (opts) => {
74
+ .action(async () => {
70
75
  try {
71
- await trackDuration('z2h_app_build_success', () => buildCommand(opts), { app_name: getCwdAppName() });
76
+ await trackDuration('z2h_app_build_success', () => buildCommand(), { app_name: getCwdAppName() });
72
77
  }
73
78
  catch (err) {
74
79
  await fail(`build failed: ${err.message}`, { command: 'build' }, 2);
@@ -78,7 +83,6 @@ async function main() {
78
83
  .command('dev')
79
84
  .description('Start the Trident dev server against .zth/ with native Vite HMR')
80
85
  .option('--open', 'open the browser automatically when the dev server is ready')
81
- .option('--json', 'emit structured JSON on stdout')
82
86
  .action(async (opts) => {
83
87
  try {
84
88
  await devCommand(opts);
@@ -90,10 +94,9 @@ async function main() {
90
94
  program
91
95
  .command('clean')
92
96
  .description('Delete <consumer>/.zth/ and <consumer>/build/')
93
- .option('--json', 'emit structured JSON on stdout')
94
- .action(async (opts) => {
97
+ .action(async () => {
95
98
  try {
96
- await cleanCommand(opts);
99
+ await cleanCommand();
97
100
  }
98
101
  catch (err) {
99
102
  await fail(`clean failed: ${err.message}`, { command: 'clean' }, 2);
@@ -103,7 +106,6 @@ async function main() {
103
106
  .command('deploy')
104
107
  .description('Build, upload to S3, and register the app with the Z2H backend')
105
108
  .option('--dry-run', 'print what would happen without uploading anything')
106
- .option('--json', 'emit structured JSON on stdout')
107
109
  .option('--remote <type>', 'First deploy only: source control remote to initialise — s3 (shared storage) or github (your repo). Error if the app already has a git remote.')
108
110
  .option('--repo-url <url>', 'First deploy only: GitHub repo URL — required when --remote=github. Error if the app already has a git remote.')
109
111
  .action(async (opts) => {
@@ -120,7 +122,6 @@ async function main() {
120
122
  .command('tag [app-name]')
121
123
  .description("Set an app's category tags — app type & team (interactive unless --tag or --json is given)")
122
124
  .option('--tag <tag>', 'set a tag (repeatable); full-replaces the tag set non-interactively', collectTag, [])
123
- .option('--json', "read-only: print the popular catalog + the app's current tags as JSON (no prompt)")
124
125
  .action(async (appName, opts) => {
125
126
  try {
126
127
  await trackDuration('z2h_app_tag_success', () => tagCommand(appName, opts), {
@@ -134,10 +135,9 @@ async function main() {
134
135
  program
135
136
  .command('create-workspace')
136
137
  .description(`Bootstrap the ${getWorkspaceHome()}/ yarn workspace (one-time setup)`)
137
- .option('--json', 'emit structured JSON on stdout')
138
- .action(async (opts) => {
138
+ .action(async () => {
139
139
  try {
140
- await createWorkspaceCommand(opts);
140
+ await createWorkspaceCommand();
141
141
  }
142
142
  catch (err) {
143
143
  await fail(`create-workspace failed: ${err.message}`, { command: 'create-workspace' }, 1);
@@ -148,7 +148,6 @@ async function main() {
148
148
  .description('Open the bigbrain.me token page, capture the token via a loopback callback, and persist it to ~/.zth/auth.json')
149
149
  .option('--no-browser', "don't auto-open the browser; print the URL and prompt for manual paste")
150
150
  .option('--url <url>', 'Override the token page URL (e.g. for staging)')
151
- .option('--json', 'emit structured JSON on stdout')
152
151
  .action(async (opts) => {
153
152
  try {
154
153
  await generateZ2HTokenCommand(opts);
@@ -164,7 +163,6 @@ async function main() {
164
163
  .option('--html-file <path>', 'Path to an HTML file to embed (implies --template html-embed)')
165
164
  .option('--remote <type>', 'Source control remote: s3 (shared storage) or github (your repo)')
166
165
  .option('--repo-url <url>', 'GitHub repo URL — required when --remote=github')
167
- .option('--json', 'emit structured JSON on stdout')
168
166
  .action(async (appName, opts) => {
169
167
  try {
170
168
  await trackDuration('z2h_app_created', () => createCommand(appName, opts), {
@@ -180,10 +178,9 @@ async function main() {
180
178
  program
181
179
  .command('edit <app-name>')
182
180
  .description(`Pull the latest version of an app into ${getWorkspaceHome()}/<app-name>, cloning it first if needed`)
183
- .option('--json', 'emit structured JSON on stdout')
184
- .action(async (appName, opts) => {
181
+ .action(async (appName) => {
185
182
  try {
186
- await trackDuration('z2h_app_edit_pulled', () => editCommand(appName, opts), {
183
+ await trackDuration('z2h_app_edit_pulled', () => editCommand(appName), {
187
184
  app_name: appName,
188
185
  });
189
186
  }
@@ -196,7 +193,6 @@ async function main() {
196
193
  .description('Grant access to the current app (email, --public, or both)')
197
194
  .option('--public', 'make the app visible to all monday.com employees')
198
195
  .option('--level <level>', 'access level for user grants: owner, editor, or viewer (default: viewer)')
199
- .option('--json', 'emit structured JSON on stdout')
200
196
  .action(async (email, opts) => {
201
197
  try {
202
198
  await grantCommand(email, opts);
@@ -209,7 +205,6 @@ async function main() {
209
205
  .command('revoke [email]')
210
206
  .description('Revoke access to the current app (email or --public; owners/admins only)')
211
207
  .option('--public', 'remove the public grant, making the app visible to owners/admins only')
212
- .option('--json', 'emit structured JSON on stdout')
213
208
  .action(async (email, opts) => {
214
209
  try {
215
210
  await revokeCommand(email, opts);
@@ -221,10 +216,9 @@ async function main() {
221
216
  program
222
217
  .command('transfer-owner [email]')
223
218
  .description('Transfer ownership of the current app to another monday.com user')
224
- .option('--json', 'emit structured JSON on stdout')
225
- .action(async (email, opts) => {
219
+ .action(async (email) => {
226
220
  try {
227
- await transferOwnerCommand(email, opts);
221
+ await transferOwnerCommand(email);
228
222
  }
229
223
  catch (err) {
230
224
  await fail(`transfer-owner failed: ${err.message}`, { command: 'transfer-owner' }, 1);
@@ -234,7 +228,6 @@ async function main() {
234
228
  .command('delete')
235
229
  .description('Permanently delete the current app and take it offline (owners/admins only)')
236
230
  .option('--yes', 'skip the confirmation prompt (required for --json / non-interactive)')
237
- .option('--json', 'emit structured JSON on stdout')
238
231
  .action(async (opts) => {
239
232
  try {
240
233
  await trackDuration('z2h_app_delete_success', () => deleteCommand(opts), {
@@ -249,7 +242,6 @@ async function main() {
249
242
  .command('migrate')
250
243
  .description('Run pending workspace migrations')
251
244
  .option('--dry-run', 'show pending migrations without applying any changes')
252
- .option('--json', 'emit structured JSON on stdout')
253
245
  .action(async (opts) => {
254
246
  try {
255
247
  await migrateCommand(version, opts);
@@ -261,10 +253,9 @@ async function main() {
261
253
  program
262
254
  .command('doctor')
263
255
  .description('Check that all Z2H tools, auth, workspace, and plugins are correctly set up')
264
- .option('--json', 'emit structured JSON on stdout')
265
- .action(async (opts) => {
256
+ .action(async () => {
266
257
  try {
267
- await doctorCommand(opts);
258
+ await doctorCommand();
268
259
  }
269
260
  catch (err) {
270
261
  await fail(`doctor failed: ${err.message}`, { command: 'doctor' }, 1);
@@ -17,4 +17,7 @@ export declare function isZ2hWorkspace(dir: string): Promise<boolean>;
17
17
  * throws an Error pointing the user at `z2h-cli create-workspace`.
18
18
  */
19
19
  export declare function assertWorkspace(): Promise<string>;
20
+ export declare function runYarnInstall(workspaceHome: string, context: string): Promise<boolean>;
21
+ export declare function isNxAvailable(workspaceHome: string): boolean;
22
+ export declare function ensureWorkspaceNx(workspaceHome: string): Promise<boolean>;
20
23
  //# sourceMappingURL=workspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../src/util/workspace.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,sBAAsB,iBAAiB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAMD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWlE;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CASvD"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../src/util/workspace.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,sBAAsB,iBAAiB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAMD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWlE;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CASvD;AAED,wBAAsB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU7F;AAWD,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAQ5D;AAID,wBAAsB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAoC/E"}
@@ -1,6 +1,9 @@
1
1
  import path from 'node:path';
2
2
  import os from 'node:os';
3
- import { pathExists, readJson } from 'fs-extra';
3
+ import { createRequire } from 'node:module';
4
+ import { pathExists, readJson, writeFile, writeJson } from 'fs-extra';
5
+ import { execa } from 'execa';
6
+ import { info, warn } from './logger.mjs';
4
7
 
5
8
  const WORKSPACE_DIR_NAME = 'zth-projects';
6
9
  const WORKSPACE_MARKER_FIELD = 'z2hWorkspace';
@@ -42,5 +45,64 @@ async function assertWorkspace() {
42
45
  throw new Error(`No Z2H workspace at ${home}. Run "z2h-cli create-workspace" first ` +
43
46
  `(or set Z2H_WORKSPACE_HOME if you keep the workspace elsewhere).`);
44
47
  }
48
+ async function runYarnInstall(workspaceHome, context) {
49
+ info(`${context} — running yarn install...`);
50
+ try {
51
+ await execa('yarn', ['install'], { cwd: workspaceHome, stdio: 'pipe' });
52
+ return true;
53
+ }
54
+ catch (err) {
55
+ const stderr = err.stderr || err.message;
56
+ warn(`yarn install failed: ${stderr}`);
57
+ return false;
58
+ }
59
+ }
60
+ async function getLatestTrientMonorepoVersion() {
61
+ try {
62
+ const { stdout } = await execa('npm', ['view', '@mondaydotcomorg/trident-monorepo', 'version']);
63
+ return `^${stdout.trim()}`;
64
+ }
65
+ catch {
66
+ return '*';
67
+ }
68
+ }
69
+ function isNxAvailable(workspaceHome) {
70
+ try {
71
+ const wsRequire = createRequire(path.join(workspaceHome, 'package.json'));
72
+ wsRequire.resolve('nx/src/generators/tree');
73
+ return true;
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }
79
+ const NX_JSON_CONTENT = '{\n "extends": "@mondaydotcomorg/trident-monorepo/presets/nx.json"\n}\n';
80
+ async function ensureWorkspaceNx(workspaceHome) {
81
+ const nxJsonPath = path.join(workspaceHome, 'nx.json');
82
+ const pkgPath = path.join(workspaceHome, 'package.json');
83
+ if (!(await pathExists(nxJsonPath))) {
84
+ await writeFile(nxJsonPath, NX_JSON_CONTENT);
85
+ info('added nx.json to workspace');
86
+ }
87
+ const pkg = (await readJson(pkgPath));
88
+ if (!pkg.devDependencies?.['@mondaydotcomorg/trident-monorepo']) {
89
+ pkg.devDependencies = pkg.devDependencies ?? {};
90
+ pkg.devDependencies['@mondaydotcomorg/trident-monorepo'] = await getLatestTrientMonorepoVersion();
91
+ }
92
+ if (!pkg.scripts?.nx) {
93
+ pkg.scripts = pkg.scripts ?? {};
94
+ pkg.scripts.nx = 'nx';
95
+ }
96
+ await writeJson(pkgPath, pkg, { spaces: 2 });
97
+ // Only install if NX isn't already importable from the workspace
98
+ if (!isNxAvailable(workspaceHome)) {
99
+ const ok = await runYarnInstall(workspaceHome, 'NX not found, installing');
100
+ if (!ok) {
101
+ warn('workspace migrations require NX. Fix the install error and run "z2h-cli migrate" to retry.');
102
+ return false;
103
+ }
104
+ }
105
+ return true;
106
+ }
45
107
 
46
- export { WORKSPACE_DIR_NAME, WORKSPACE_MARKER_FIELD, assertWorkspace, getWorkspaceHome, isZ2hWorkspace };
108
+ export { WORKSPACE_DIR_NAME, WORKSPACE_MARKER_FIELD, assertWorkspace, ensureWorkspaceNx, getWorkspaceHome, isNxAvailable, isZ2hWorkspace, runYarnInstall };
package/dist/index.js CHANGED
@@ -67,14 +67,19 @@ async function main() {
67
67
  ' 1 user-resolvable error (bad cwd, invalid package.json, lock held)',
68
68
  ' 2 system error (yarn install or trident build failed)',
69
69
  ].join('\n'))
70
- .version(version);
70
+ .version(version)
71
+ .option('--json', 'emit structured JSON on stdout')
72
+ .hook('preAction', (_thisCommand, actionCommand) => {
73
+ if (actionCommand.optsWithGlobals().json) {
74
+ util_logger.setOutputMode('json');
75
+ }
76
+ });
71
77
  program
72
78
  .command('build')
73
79
  .description('Build the consumer into its .zth/ shadow and copy artifacts to ./build/')
74
- .option('--json', 'emit structured JSON on stdout')
75
- .action(async (opts) => {
80
+ .action(async () => {
76
81
  try {
77
- await util_tracker.trackDuration('z2h_app_build_success', () => commands_build.buildCommand(opts), { app_name: util_appName.getCwdAppName() });
82
+ await util_tracker.trackDuration('z2h_app_build_success', () => commands_build.buildCommand(), { app_name: util_appName.getCwdAppName() });
78
83
  }
79
84
  catch (err) {
80
85
  await util_logger.fail(`build failed: ${err.message}`, { command: 'build' }, 2);
@@ -84,7 +89,6 @@ async function main() {
84
89
  .command('dev')
85
90
  .description('Start the Trident dev server against .zth/ with native Vite HMR')
86
91
  .option('--open', 'open the browser automatically when the dev server is ready')
87
- .option('--json', 'emit structured JSON on stdout')
88
92
  .action(async (opts) => {
89
93
  try {
90
94
  await commands_dev.devCommand(opts);
@@ -96,10 +100,9 @@ async function main() {
96
100
  program
97
101
  .command('clean')
98
102
  .description('Delete <consumer>/.zth/ and <consumer>/build/')
99
- .option('--json', 'emit structured JSON on stdout')
100
- .action(async (opts) => {
103
+ .action(async () => {
101
104
  try {
102
- await commands_clean.cleanCommand(opts);
105
+ await commands_clean.cleanCommand();
103
106
  }
104
107
  catch (err) {
105
108
  await util_logger.fail(`clean failed: ${err.message}`, { command: 'clean' }, 2);
@@ -109,7 +112,6 @@ async function main() {
109
112
  .command('deploy')
110
113
  .description('Build, upload to S3, and register the app with the Z2H backend')
111
114
  .option('--dry-run', 'print what would happen without uploading anything')
112
- .option('--json', 'emit structured JSON on stdout')
113
115
  .option('--remote <type>', 'First deploy only: source control remote to initialise — s3 (shared storage) or github (your repo). Error if the app already has a git remote.')
114
116
  .option('--repo-url <url>', 'First deploy only: GitHub repo URL — required when --remote=github. Error if the app already has a git remote.')
115
117
  .action(async (opts) => {
@@ -126,7 +128,6 @@ async function main() {
126
128
  .command('tag [app-name]')
127
129
  .description("Set an app's category tags — app type & team (interactive unless --tag or --json is given)")
128
130
  .option('--tag <tag>', 'set a tag (repeatable); full-replaces the tag set non-interactively', collectTag, [])
129
- .option('--json', "read-only: print the popular catalog + the app's current tags as JSON (no prompt)")
130
131
  .action(async (appName, opts) => {
131
132
  try {
132
133
  await util_tracker.trackDuration('z2h_app_tag_success', () => commands_tag.tagCommand(appName, opts), {
@@ -140,10 +141,9 @@ async function main() {
140
141
  program
141
142
  .command('create-workspace')
142
143
  .description(`Bootstrap the ${util_workspace.getWorkspaceHome()}/ yarn workspace (one-time setup)`)
143
- .option('--json', 'emit structured JSON on stdout')
144
- .action(async (opts) => {
144
+ .action(async () => {
145
145
  try {
146
- await commands_createWorkspace.createWorkspaceCommand(opts);
146
+ await commands_createWorkspace.createWorkspaceCommand();
147
147
  }
148
148
  catch (err) {
149
149
  await util_logger.fail(`create-workspace failed: ${err.message}`, { command: 'create-workspace' }, 1);
@@ -154,7 +154,6 @@ async function main() {
154
154
  .description('Open the bigbrain.me token page, capture the token via a loopback callback, and persist it to ~/.zth/auth.json')
155
155
  .option('--no-browser', "don't auto-open the browser; print the URL and prompt for manual paste")
156
156
  .option('--url <url>', 'Override the token page URL (e.g. for staging)')
157
- .option('--json', 'emit structured JSON on stdout')
158
157
  .action(async (opts) => {
159
158
  try {
160
159
  await commands_generateZ2hToken.generateZ2HTokenCommand(opts);
@@ -170,7 +169,6 @@ async function main() {
170
169
  .option('--html-file <path>', 'Path to an HTML file to embed (implies --template html-embed)')
171
170
  .option('--remote <type>', 'Source control remote: s3 (shared storage) or github (your repo)')
172
171
  .option('--repo-url <url>', 'GitHub repo URL — required when --remote=github')
173
- .option('--json', 'emit structured JSON on stdout')
174
172
  .action(async (appName, opts) => {
175
173
  try {
176
174
  await util_tracker.trackDuration('z2h_app_created', () => commands_create.createCommand(appName, opts), {
@@ -186,10 +184,9 @@ async function main() {
186
184
  program
187
185
  .command('edit <app-name>')
188
186
  .description(`Pull the latest version of an app into ${util_workspace.getWorkspaceHome()}/<app-name>, cloning it first if needed`)
189
- .option('--json', 'emit structured JSON on stdout')
190
- .action(async (appName, opts) => {
187
+ .action(async (appName) => {
191
188
  try {
192
- await util_tracker.trackDuration('z2h_app_edit_pulled', () => commands_edit.editCommand(appName, opts), {
189
+ await util_tracker.trackDuration('z2h_app_edit_pulled', () => commands_edit.editCommand(appName), {
193
190
  app_name: appName,
194
191
  });
195
192
  }
@@ -202,7 +199,6 @@ async function main() {
202
199
  .description('Grant access to the current app (email, --public, or both)')
203
200
  .option('--public', 'make the app visible to all monday.com employees')
204
201
  .option('--level <level>', 'access level for user grants: owner, editor, or viewer (default: viewer)')
205
- .option('--json', 'emit structured JSON on stdout')
206
202
  .action(async (email, opts) => {
207
203
  try {
208
204
  await commands_grant.grantCommand(email, opts);
@@ -215,7 +211,6 @@ async function main() {
215
211
  .command('revoke [email]')
216
212
  .description('Revoke access to the current app (email or --public; owners/admins only)')
217
213
  .option('--public', 'remove the public grant, making the app visible to owners/admins only')
218
- .option('--json', 'emit structured JSON on stdout')
219
214
  .action(async (email, opts) => {
220
215
  try {
221
216
  await commands_grant.revokeCommand(email, opts);
@@ -227,10 +222,9 @@ async function main() {
227
222
  program
228
223
  .command('transfer-owner [email]')
229
224
  .description('Transfer ownership of the current app to another monday.com user')
230
- .option('--json', 'emit structured JSON on stdout')
231
- .action(async (email, opts) => {
225
+ .action(async (email) => {
232
226
  try {
233
- await commands_grant.transferOwnerCommand(email, opts);
227
+ await commands_grant.transferOwnerCommand(email);
234
228
  }
235
229
  catch (err) {
236
230
  await util_logger.fail(`transfer-owner failed: ${err.message}`, { command: 'transfer-owner' }, 1);
@@ -240,7 +234,6 @@ async function main() {
240
234
  .command('delete')
241
235
  .description('Permanently delete the current app and take it offline (owners/admins only)')
242
236
  .option('--yes', 'skip the confirmation prompt (required for --json / non-interactive)')
243
- .option('--json', 'emit structured JSON on stdout')
244
237
  .action(async (opts) => {
245
238
  try {
246
239
  await util_tracker.trackDuration('z2h_app_delete_success', () => commands_delete.deleteCommand(opts), {
@@ -255,7 +248,6 @@ async function main() {
255
248
  .command('migrate')
256
249
  .description('Run pending workspace migrations')
257
250
  .option('--dry-run', 'show pending migrations without applying any changes')
258
- .option('--json', 'emit structured JSON on stdout')
259
251
  .action(async (opts) => {
260
252
  try {
261
253
  await commands_migrate.migrateCommand(version, opts);
@@ -267,10 +259,9 @@ async function main() {
267
259
  program
268
260
  .command('doctor')
269
261
  .description('Check that all Z2H tools, auth, workspace, and plugins are correctly set up')
270
- .option('--json', 'emit structured JSON on stdout')
271
- .action(async (opts) => {
262
+ .action(async () => {
272
263
  try {
273
- await commands_doctor.doctorCommand(opts);
264
+ await commands_doctor.doctorCommand();
274
265
  }
275
266
  catch (err) {
276
267
  await util_logger.fail(`doctor failed: ${err.message}`, { command: 'doctor' }, 1);
@@ -17,4 +17,7 @@ export declare function isZ2hWorkspace(dir: string): Promise<boolean>;
17
17
  * throws an Error pointing the user at `z2h-cli create-workspace`.
18
18
  */
19
19
  export declare function assertWorkspace(): Promise<string>;
20
+ export declare function runYarnInstall(workspaceHome: string, context: string): Promise<boolean>;
21
+ export declare function isNxAvailable(workspaceHome: string): boolean;
22
+ export declare function ensureWorkspaceNx(workspaceHome: string): Promise<boolean>;
20
23
  //# sourceMappingURL=workspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/util/workspace.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,sBAAsB,iBAAiB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAMD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWlE;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CASvD"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/util/workspace.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,sBAAsB,iBAAiB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAMD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWlE;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CASvD;AAED,wBAAsB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU7F;AAWD,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAQ5D;AAID,wBAAsB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAoC/E"}
@@ -2,7 +2,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const path = require('node:path');
4
4
  const os = require('node:os');
5
+ const node_module = require('node:module');
5
6
  const fs = require('fs-extra');
7
+ const execa = require('execa');
8
+ const util_logger = require('./logger.js');
6
9
 
7
10
  const _interopDefault = e => e && e.__esModule ? e : { default: e };
8
11
 
@@ -49,9 +52,71 @@ async function assertWorkspace() {
49
52
  throw new Error(`No Z2H workspace at ${home}. Run "z2h-cli create-workspace" first ` +
50
53
  `(or set Z2H_WORKSPACE_HOME if you keep the workspace elsewhere).`);
51
54
  }
55
+ async function runYarnInstall(workspaceHome, context) {
56
+ util_logger.info(`${context} — running yarn install...`);
57
+ try {
58
+ await execa.execa('yarn', ['install'], { cwd: workspaceHome, stdio: 'pipe' });
59
+ return true;
60
+ }
61
+ catch (err) {
62
+ const stderr = err.stderr || err.message;
63
+ util_logger.warn(`yarn install failed: ${stderr}`);
64
+ return false;
65
+ }
66
+ }
67
+ async function getLatestTrientMonorepoVersion() {
68
+ try {
69
+ const { stdout } = await execa.execa('npm', ['view', '@mondaydotcomorg/trident-monorepo', 'version']);
70
+ return `^${stdout.trim()}`;
71
+ }
72
+ catch {
73
+ return '*';
74
+ }
75
+ }
76
+ function isNxAvailable(workspaceHome) {
77
+ try {
78
+ const wsRequire = node_module.createRequire(path__default.default.join(workspaceHome, 'package.json'));
79
+ wsRequire.resolve('nx/src/generators/tree');
80
+ return true;
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ const NX_JSON_CONTENT = '{\n "extends": "@mondaydotcomorg/trident-monorepo/presets/nx.json"\n}\n';
87
+ async function ensureWorkspaceNx(workspaceHome) {
88
+ const nxJsonPath = path__default.default.join(workspaceHome, 'nx.json');
89
+ const pkgPath = path__default.default.join(workspaceHome, 'package.json');
90
+ if (!(await fs.pathExists(nxJsonPath))) {
91
+ await fs.writeFile(nxJsonPath, NX_JSON_CONTENT);
92
+ util_logger.info('added nx.json to workspace');
93
+ }
94
+ const pkg = (await fs.readJson(pkgPath));
95
+ if (!pkg.devDependencies?.['@mondaydotcomorg/trident-monorepo']) {
96
+ pkg.devDependencies = pkg.devDependencies ?? {};
97
+ pkg.devDependencies['@mondaydotcomorg/trident-monorepo'] = await getLatestTrientMonorepoVersion();
98
+ }
99
+ if (!pkg.scripts?.nx) {
100
+ pkg.scripts = pkg.scripts ?? {};
101
+ pkg.scripts.nx = 'nx';
102
+ }
103
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
104
+ // Only install if NX isn't already importable from the workspace
105
+ if (!isNxAvailable(workspaceHome)) {
106
+ const ok = await runYarnInstall(workspaceHome, 'NX not found, installing');
107
+ if (!ok) {
108
+ util_logger.warn('workspace migrations require NX. Fix the install error and run "z2h-cli migrate" to retry.');
109
+ return false;
110
+ }
111
+ }
112
+ return true;
113
+ }
52
114
 
53
115
  exports.WORKSPACE_DIR_NAME = WORKSPACE_DIR_NAME;
54
116
  exports.WORKSPACE_MARKER_FIELD = WORKSPACE_MARKER_FIELD;
55
117
  exports.assertWorkspace = assertWorkspace;
118
+ exports.ensureWorkspaceNx = ensureWorkspaceNx;
56
119
  exports.getWorkspaceHome = getWorkspaceHome;
120
+ exports.isNxAvailable = isNxAvailable;
57
121
  exports.isZ2hWorkspace = isZ2hWorkspace;
122
+ exports.runYarnInstall = runYarnInstall;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mondaydotcomorg/z2h-cli",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "bin": "./bin/z2h-cli.js",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -4,6 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
4
  import { execa } from 'execa';
5
5
  import { pathExists, readJson } from 'fs-extra';
6
6
  import { tryReadAuthTokenSync } from '../../util/auth/auth-store';
7
+ import { getOutputMode, setOutputMode } from '../../util/logger';
7
8
  import { doctorCommand } from '../doctor';
8
9
 
9
10
  vi.mock('execa', () => ({ execa: vi.fn() }));
@@ -402,10 +403,16 @@ describe('doctorCommand', () => {
402
403
  // ── JSON output ───────────────────────────────────────────────────────────
403
404
 
404
405
  it('emits structured JSON when --json flag is set', async () => {
405
- const jsonLine = await captureStdout(() => doctorCommand({ json: true }));
406
- const parsed = JSON.parse(jsonLine.trim()) as { ok: boolean; checks: unknown[] };
407
- expect(parsed.ok).toBe(true);
408
- expect(Array.isArray(parsed.checks)).toBe(true);
409
- expect(parsed.checks.length).toBeGreaterThan(0);
406
+ const currentOutputMode = getOutputMode();
407
+ try {
408
+ setOutputMode('json');
409
+ const jsonLine = await captureStdout(() => doctorCommand());
410
+ const parsed = JSON.parse(jsonLine.trim()) as { ok: boolean; checks: unknown[] };
411
+ expect(parsed.ok).toBe(true);
412
+ expect(Array.isArray(parsed.checks)).toBe(true);
413
+ expect(parsed.checks.length).toBeGreaterThan(0);
414
+ } finally {
415
+ setOutputMode(currentOutputMode);
416
+ }
410
417
  });
411
418
  });
@@ -1,19 +1,14 @@
1
1
  import { pathExists } from 'fs-extra';
2
2
  import { execa } from 'execa';
3
- import { info, output, setOutputMode } from '../util/logger';
3
+ import { info, output } from '../util/logger';
4
4
  import { prepareShadow } from '../shadow/prepare';
5
5
 
6
6
  export interface BuildOptions {
7
- json?: boolean;
8
7
  publicUrl?: string;
9
8
  }
10
9
 
11
10
  // Should be run from the consumer app directory — uses process.cwd() to locate it.
12
11
  export async function buildCommand(opts: BuildOptions = {}): Promise<void> {
13
- if (opts.json) {
14
- setOutputMode('json');
15
- }
16
-
17
12
  const ctx = await prepareShadow();
18
13
 
19
14
  // Typecheck against the shadow's tsconfig (the consumer dir has none).
@@ -1,16 +1,9 @@
1
1
  import { pathExists, remove } from 'fs-extra';
2
- import { info, output, setOutputMode } from '../util/logger';
2
+ import { info, output } from '../util/logger';
3
3
  import { resolveConsumerPaths } from '../shadow/paths';
4
4
 
5
- export interface CleanOptions {
6
- json?: boolean;
7
- }
8
-
9
5
  // Should be run from the consumer app directory — uses process.cwd() to locate it.
10
- export async function cleanCommand(opts: CleanOptions = {}): Promise<void> {
11
- if (opts.json) {
12
- setOutputMode('json');
13
- }
6
+ export async function cleanCommand(): Promise<void> {
14
7
  const paths = resolveConsumerPaths(process.cwd());
15
8
 
16
9
  const existed = await pathExists(paths.shadowDir);
@@ -1,21 +1,13 @@
1
1
  import path from 'node:path';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { ensureDir, writeFile, pathExists, readdir, copy } from 'fs-extra';
4
- import { info, output, setOutputMode } from '../util/logger';
4
+ import { info, output } from '../util/logger';
5
5
  import { getWorkspaceHome } from '../util/workspace';
6
6
  import { getTemplatesDir } from '../util/templates';
7
7
  import { runYarnInstall } from '../util/npm/install';
8
8
  import { workspaceNpmrcContents, workspaceYarnrcContents } from '../util/npm/broker-config';
9
9
 
10
- export interface CreateWorkspaceOptions {
11
- json?: boolean;
12
- }
13
-
14
- export async function createWorkspaceCommand(opts: CreateWorkspaceOptions = {}): Promise<void> {
15
- if (opts.json) {
16
- setOutputMode('json');
17
- }
18
-
10
+ export async function createWorkspaceCommand(): Promise<void> {
19
11
  const home = getWorkspaceHome();
20
12
  const templatesDir = getTemplatesDir();
21
13
  const wsTemplatesDir = path.join(templatesDir, 'workspace');
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { ensureDir, pathExists, readdir, writeFile, copy } from 'fs-extra';
4
- import { info, warn, output, setOutputMode } from '../util/logger';
4
+ import { info, warn, output } from '../util/logger';
5
5
  import { assertWorkspace } from '../util/workspace';
6
6
  import { getTemplatesDir, renderTemplate, APP_TEMPLATES, type AppTemplate } from '../util/templates';
7
7
  import { APP_NAME_PATTERN } from '../constants';
@@ -19,7 +19,6 @@ export interface CreateOptions {
19
19
  htmlFile?: string;
20
20
  remote?: RemoteType;
21
21
  repoUrl?: string;
22
- json?: boolean;
23
22
  }
24
23
 
25
24
  // Names reserved by the host MF (mf-bigbrain-zth) for platform pages under
@@ -50,10 +49,6 @@ async function isDirEmpty(dir: string): Promise<boolean> {
50
49
  }
51
50
 
52
51
  export async function createCommand(appName: string, opts: CreateOptions = {}): Promise<void> {
53
- if (opts.json) {
54
- setOutputMode('json');
55
- }
56
-
57
52
  // Validate workspace.
58
53
  const workspace = await assertWorkspace();
59
54