@git.zone/cli 2.13.13 → 2.14.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.
Files changed (39) hide show
  1. package/dist_ts/00_commitinfo_data.js +2 -2
  2. package/dist_ts/gitzone.cli.js +46 -35
  3. package/dist_ts/helpers.climode.d.ts +22 -0
  4. package/dist_ts/helpers.climode.js +158 -0
  5. package/dist_ts/helpers.smartconfig.d.ts +11 -0
  6. package/dist_ts/helpers.smartconfig.js +139 -0
  7. package/dist_ts/mod_commit/index.d.ts +2 -0
  8. package/dist_ts/mod_commit/index.js +204 -92
  9. package/dist_ts/mod_config/index.d.ts +7 -2
  10. package/dist_ts/mod_config/index.js +390 -176
  11. package/dist_ts/mod_format/classes.formatcontext.d.ts +11 -2
  12. package/dist_ts/mod_format/classes.formatcontext.js +14 -4
  13. package/dist_ts/mod_format/formatters/packagejson.formatter.js +1 -67
  14. package/dist_ts/mod_format/formatters/smartconfig.formatter.d.ts +2 -7
  15. package/dist_ts/mod_format/formatters/smartconfig.formatter.js +57 -93
  16. package/dist_ts/mod_format/index.d.ts +4 -1
  17. package/dist_ts/mod_format/index.js +235 -67
  18. package/dist_ts/mod_format/mod.plugins.d.ts +1 -2
  19. package/dist_ts/mod_format/mod.plugins.js +2 -3
  20. package/dist_ts/mod_services/index.d.ts +2 -0
  21. package/dist_ts/mod_services/index.js +366 -168
  22. package/dist_ts/mod_standard/index.d.ts +3 -1
  23. package/dist_ts/mod_standard/index.js +159 -59
  24. package/package.json +1 -1
  25. package/readme.hints.md +25 -32
  26. package/readme.md +87 -65
  27. package/ts/00_commitinfo_data.ts +1 -1
  28. package/ts/gitzone.cli.ts +50 -38
  29. package/ts/helpers.climode.ts +212 -0
  30. package/ts/helpers.smartconfig.ts +192 -0
  31. package/ts/mod_commit/index.ts +325 -98
  32. package/ts/mod_config/index.ts +490 -182
  33. package/ts/mod_format/classes.formatcontext.ts +20 -3
  34. package/ts/mod_format/formatters/packagejson.formatter.ts +0 -91
  35. package/ts/mod_format/formatters/smartconfig.formatter.ts +67 -94
  36. package/ts/mod_format/index.ts +294 -81
  37. package/ts/mod_format/mod.plugins.ts +0 -2
  38. package/ts/mod_services/index.ts +550 -183
  39. package/ts/mod_standard/index.ts +191 -58
@@ -1,73 +1,116 @@
1
1
  // gitzone config - manage release registry configuration
2
2
 
3
- import * as plugins from './mod.plugins.js';
4
- import { ReleaseConfig } from './classes.releaseconfig.js';
5
- import { CommitConfig } from './classes.commitconfig.js';
6
- import { runFormatter, type ICheckResult } from '../mod_format/index.js';
3
+ import * as plugins from "./mod.plugins.js";
4
+ import { ReleaseConfig } from "./classes.releaseconfig.js";
5
+ import { CommitConfig } from "./classes.commitconfig.js";
6
+ import { runFormatter, type ICheckResult } from "../mod_format/index.js";
7
+ import type { ICliMode } from "../helpers.climode.js";
8
+ import { getCliMode, printJson } from "../helpers.climode.js";
9
+ import {
10
+ getCliConfigValueFromData,
11
+ readSmartconfigFile,
12
+ setCliConfigValueInData,
13
+ unsetCliConfigValueInData,
14
+ writeSmartconfigFile,
15
+ } from "../helpers.smartconfig.js";
7
16
 
8
17
  export { ReleaseConfig, CommitConfig };
9
18
 
19
+ const defaultCliMode: ICliMode = {
20
+ output: "human",
21
+ interactive: true,
22
+ json: false,
23
+ plain: false,
24
+ quiet: false,
25
+ yes: false,
26
+ help: false,
27
+ agent: false,
28
+ checkUpdates: true,
29
+ isTty: true,
30
+ };
31
+
10
32
  /**
11
33
  * Format .smartconfig.json with diff preview
12
34
  * Shows diff first, asks for confirmation, then applies
13
35
  */
14
- async function formatSmartconfigWithDiff(): Promise<void> {
36
+ async function formatSmartconfigWithDiff(mode: ICliMode): Promise<void> {
37
+ if (!mode.interactive) {
38
+ return;
39
+ }
40
+
15
41
  // Check for diffs first
16
- const checkResult = await runFormatter('smartconfig', {
42
+ const checkResult = (await runFormatter("smartconfig", {
17
43
  checkOnly: true,
18
44
  showDiff: true,
19
- }) as ICheckResult | void;
45
+ })) as ICheckResult | void;
20
46
 
21
47
  if (checkResult && checkResult.hasDiff) {
22
- const shouldApply = await plugins.smartinteract.SmartInteract.getCliConfirmation(
23
- 'Apply formatting changes to .smartconfig.json?',
24
- true
25
- );
48
+ const shouldApply =
49
+ await plugins.smartinteract.SmartInteract.getCliConfirmation(
50
+ "Apply formatting changes to .smartconfig.json?",
51
+ true,
52
+ );
26
53
  if (shouldApply) {
27
- await runFormatter('smartconfig', { silent: true });
54
+ await runFormatter("smartconfig", { silent: true });
28
55
  }
29
56
  }
30
57
  }
31
58
 
32
59
  export const run = async (argvArg: any) => {
60
+ const mode = await getCliMode(argvArg);
33
61
  const command = argvArg._?.[1];
34
62
  const value = argvArg._?.[2];
35
63
 
64
+ if (mode.help || command === "help") {
65
+ showHelp(mode);
66
+ return;
67
+ }
68
+
36
69
  // If no command provided, show interactive menu
37
70
  if (!command) {
71
+ if (!mode.interactive) {
72
+ showHelp(mode);
73
+ return;
74
+ }
38
75
  await handleInteractiveMenu();
39
76
  return;
40
77
  }
41
78
 
42
79
  switch (command) {
43
- case 'show':
44
- await handleShow();
80
+ case "show":
81
+ await handleShow(mode);
45
82
  break;
46
- case 'add':
47
- await handleAdd(value);
83
+ case "add":
84
+ await handleAdd(value, mode);
48
85
  break;
49
- case 'remove':
50
- await handleRemove(value);
86
+ case "remove":
87
+ await handleRemove(value, mode);
51
88
  break;
52
- case 'clear':
53
- await handleClear();
89
+ case "clear":
90
+ await handleClear(mode);
54
91
  break;
55
- case 'access':
56
- case 'accessLevel':
57
- await handleAccessLevel(value);
92
+ case "access":
93
+ case "accessLevel":
94
+ await handleAccessLevel(value, mode);
58
95
  break;
59
- case 'commit':
60
- await handleCommit(argvArg._?.[2], argvArg._?.[3]);
96
+ case "commit":
97
+ await handleCommit(argvArg._?.[2], argvArg._?.[3], mode);
61
98
  break;
62
- case 'services':
63
- await handleServices();
99
+ case "services":
100
+ await handleServices(mode);
64
101
  break;
65
- case 'help':
66
- showHelp();
102
+ case "get":
103
+ await handleGet(value, mode);
104
+ break;
105
+ case "set":
106
+ await handleSet(value, argvArg._?.[3], mode);
107
+ break;
108
+ case "unset":
109
+ await handleUnset(value, mode);
67
110
  break;
68
111
  default:
69
- plugins.logger.log('error', `Unknown command: ${command}`);
70
- showHelp();
112
+ plugins.logger.log("error", `Unknown command: ${command}`);
113
+ showHelp(mode);
71
114
  }
72
115
  };
73
116
 
@@ -75,55 +118,61 @@ export const run = async (argvArg: any) => {
75
118
  * Interactive menu for config command
76
119
  */
77
120
  async function handleInteractiveMenu(): Promise<void> {
78
- console.log('');
79
- console.log('╭─────────────────────────────────────────────────────────────╮');
80
- console.log('│ gitzone config - Project Configuration │');
81
- console.log('╰─────────────────────────────────────────────────────────────╯');
82
- console.log('');
121
+ console.log("");
122
+ console.log(
123
+ "╭─────────────────────────────────────────────────────────────╮",
124
+ );
125
+ console.log(
126
+ "│ gitzone config - Project Configuration │",
127
+ );
128
+ console.log(
129
+ "╰─────────────────────────────────────────────────────────────╯",
130
+ );
131
+ console.log("");
83
132
 
84
133
  const interactInstance = new plugins.smartinteract.SmartInteract();
85
134
  const response = await interactInstance.askQuestion({
86
- type: 'list',
87
- name: 'action',
88
- message: 'What would you like to do?',
89
- default: 'show',
135
+ type: "list",
136
+ name: "action",
137
+ message: "What would you like to do?",
138
+ default: "show",
90
139
  choices: [
91
- { name: 'Show current configuration', value: 'show' },
92
- { name: 'Add a registry', value: 'add' },
93
- { name: 'Remove a registry', value: 'remove' },
94
- { name: 'Clear all registries', value: 'clear' },
95
- { name: 'Set access level (public/private)', value: 'access' },
96
- { name: 'Configure commit options', value: 'commit' },
97
- { name: 'Configure services', value: 'services' },
98
- { name: 'Show help', value: 'help' },
140
+ { name: "Show current configuration", value: "show" },
141
+ { name: "Add a registry", value: "add" },
142
+ { name: "Remove a registry", value: "remove" },
143
+ { name: "Clear all registries", value: "clear" },
144
+ { name: "Set access level (public/private)", value: "access" },
145
+ { name: "Configure commit options", value: "commit" },
146
+ { name: "Configure services", value: "services" },
147
+ { name: "Show help", value: "help" },
99
148
  ],
100
149
  });
101
150
 
102
151
  const action = (response as any).value;
103
152
 
104
153
  switch (action) {
105
- case 'show':
106
- await handleShow();
154
+ case "show":
155
+ await handleShow(defaultCliMode);
107
156
  break;
108
- case 'add':
109
- await handleAdd();
157
+ case "add":
158
+ await handleAdd(undefined, defaultCliMode);
110
159
  break;
111
- case 'remove':
112
- await handleRemove();
160
+ case "remove":
161
+ await handleRemove(undefined, defaultCliMode);
113
162
  break;
114
- case 'clear':
115
- await handleClear();
163
+ case "clear":
164
+ await handleClear(defaultCliMode);
116
165
  break;
117
- case 'access':
118
- await handleAccessLevel();
166
+ case "access":
167
+ await handleAccessLevel(undefined, defaultCliMode);
119
168
  break;
120
- case 'commit':
121
- await handleCommit();
169
+ case "commit":
170
+ await handleCommit(undefined, undefined, defaultCliMode);
122
171
  break;
123
- case 'services':
124
- await handleServices();
172
+ case "services":
173
+ await handleServices(defaultCliMode);
125
174
  break;
126
- case 'help':
175
+ case "help":
127
176
  showHelp();
128
177
  break;
129
178
  }
@@ -132,50 +181,69 @@ async function handleInteractiveMenu(): Promise<void> {
132
181
  /**
133
182
  * Show current registry configuration
134
183
  */
135
- async function handleShow(): Promise<void> {
184
+ async function handleShow(mode: ICliMode): Promise<void> {
185
+ if (mode.json) {
186
+ const smartconfigData = await readSmartconfigFile();
187
+ printJson(getCliConfigValueFromData(smartconfigData, ""));
188
+ return;
189
+ }
190
+
136
191
  const config = await ReleaseConfig.fromCwd();
137
192
  const registries = config.getRegistries();
138
193
  const accessLevel = config.getAccessLevel();
139
194
 
140
- console.log('');
141
- console.log('╭─────────────────────────────────────────────────────────────╮');
142
- console.log('│ Release Configuration │');
143
- console.log('╰─────────────────────────────────────────────────────────────╯');
144
- console.log('');
195
+ console.log("");
196
+ console.log(
197
+ "╭─────────────────────────────────────────────────────────────╮",
198
+ );
199
+ console.log(
200
+ "│ Release Configuration │",
201
+ );
202
+ console.log(
203
+ "╰─────────────────────────────────────────────────────────────╯",
204
+ );
205
+ console.log("");
145
206
 
146
207
  // Show access level
147
- plugins.logger.log('info', `Access Level: ${accessLevel}`);
148
- console.log('');
208
+ plugins.logger.log("info", `Access Level: ${accessLevel}`);
209
+ console.log("");
149
210
 
150
211
  if (registries.length === 0) {
151
- plugins.logger.log('info', 'No release registries configured.');
152
- console.log('');
153
- console.log(' Run `gitzone config add <registry-url>` to add one.');
154
- console.log('');
212
+ plugins.logger.log("info", "No release registries configured.");
213
+ console.log("");
214
+ console.log(" Run `gitzone config add <registry-url>` to add one.");
215
+ console.log("");
155
216
  } else {
156
- plugins.logger.log('info', `Configured registries (${registries.length}):`);
157
- console.log('');
217
+ plugins.logger.log("info", `Configured registries (${registries.length}):`);
218
+ console.log("");
158
219
  registries.forEach((url, index) => {
159
220
  console.log(` ${index + 1}. ${url}`);
160
221
  });
161
- console.log('');
222
+ console.log("");
162
223
  }
163
224
  }
164
225
 
165
226
  /**
166
227
  * Add a registry URL
167
228
  */
168
- async function handleAdd(url?: string): Promise<void> {
229
+ async function handleAdd(
230
+ url: string | undefined,
231
+ mode: ICliMode,
232
+ ): Promise<void> {
169
233
  if (!url) {
234
+ if (!mode.interactive) {
235
+ throw new Error("Registry URL is required in non-interactive mode");
236
+ }
237
+
170
238
  // Interactive mode
171
239
  const interactInstance = new plugins.smartinteract.SmartInteract();
172
240
  const response = await interactInstance.askQuestion({
173
- type: 'input',
174
- name: 'registryUrl',
175
- message: 'Enter registry URL:',
176
- default: 'https://registry.npmjs.org',
241
+ type: "input",
242
+ name: "registryUrl",
243
+ message: "Enter registry URL:",
244
+ default: "https://registry.npmjs.org",
177
245
  validate: (input: string) => {
178
- return !!(input && input.trim() !== '');
246
+ return !!(input && input.trim() !== "");
179
247
  },
180
248
  });
181
249
  url = (response as any).value;
@@ -186,32 +254,48 @@ async function handleAdd(url?: string): Promise<void> {
186
254
 
187
255
  if (added) {
188
256
  await config.save();
189
- plugins.logger.log('success', `Added registry: ${url}`);
190
- await formatSmartconfigWithDiff();
257
+ if (mode.json) {
258
+ printJson({
259
+ ok: true,
260
+ action: "add",
261
+ registry: url,
262
+ registries: config.getRegistries(),
263
+ });
264
+ return;
265
+ }
266
+ plugins.logger.log("success", `Added registry: ${url}`);
267
+ await formatSmartconfigWithDiff(mode);
191
268
  } else {
192
- plugins.logger.log('warn', `Registry already exists: ${url}`);
269
+ plugins.logger.log("warn", `Registry already exists: ${url}`);
193
270
  }
194
271
  }
195
272
 
196
273
  /**
197
274
  * Remove a registry URL
198
275
  */
199
- async function handleRemove(url?: string): Promise<void> {
276
+ async function handleRemove(
277
+ url: string | undefined,
278
+ mode: ICliMode,
279
+ ): Promise<void> {
200
280
  const config = await ReleaseConfig.fromCwd();
201
281
  const registries = config.getRegistries();
202
282
 
203
283
  if (registries.length === 0) {
204
- plugins.logger.log('warn', 'No registries configured to remove.');
284
+ plugins.logger.log("warn", "No registries configured to remove.");
205
285
  return;
206
286
  }
207
287
 
208
288
  if (!url) {
289
+ if (!mode.interactive) {
290
+ throw new Error("Registry URL is required in non-interactive mode");
291
+ }
292
+
209
293
  // Interactive mode - show list to select from
210
294
  const interactInstance = new plugins.smartinteract.SmartInteract();
211
295
  const response = await interactInstance.askQuestion({
212
- type: 'list',
213
- name: 'registryUrl',
214
- message: 'Select registry to remove:',
296
+ type: "list",
297
+ name: "registryUrl",
298
+ message: "Select registry to remove:",
215
299
  choices: registries,
216
300
  default: registries[0],
217
301
  });
@@ -222,99 +306,135 @@ async function handleRemove(url?: string): Promise<void> {
222
306
 
223
307
  if (removed) {
224
308
  await config.save();
225
- plugins.logger.log('success', `Removed registry: ${url}`);
226
- await formatSmartconfigWithDiff();
309
+ if (mode.json) {
310
+ printJson({
311
+ ok: true,
312
+ action: "remove",
313
+ registry: url,
314
+ registries: config.getRegistries(),
315
+ });
316
+ return;
317
+ }
318
+ plugins.logger.log("success", `Removed registry: ${url}`);
319
+ await formatSmartconfigWithDiff(mode);
227
320
  } else {
228
- plugins.logger.log('warn', `Registry not found: ${url}`);
321
+ plugins.logger.log("warn", `Registry not found: ${url}`);
229
322
  }
230
323
  }
231
324
 
232
325
  /**
233
326
  * Clear all registries
234
327
  */
235
- async function handleClear(): Promise<void> {
328
+ async function handleClear(mode: ICliMode): Promise<void> {
236
329
  const config = await ReleaseConfig.fromCwd();
237
330
 
238
331
  if (!config.hasRegistries()) {
239
- plugins.logger.log('info', 'No registries to clear.');
332
+ plugins.logger.log("info", "No registries to clear.");
240
333
  return;
241
334
  }
242
335
 
243
336
  // Confirm before clearing
244
- const confirmed = await plugins.smartinteract.SmartInteract.getCliConfirmation(
245
- 'Clear all configured registries?',
246
- false
247
- );
337
+ const confirmed = mode.interactive
338
+ ? await plugins.smartinteract.SmartInteract.getCliConfirmation(
339
+ "Clear all configured registries?",
340
+ false,
341
+ )
342
+ : true;
248
343
 
249
344
  if (confirmed) {
250
345
  config.clearRegistries();
251
346
  await config.save();
252
- plugins.logger.log('success', 'All registries cleared.');
253
- await formatSmartconfigWithDiff();
347
+ if (mode.json) {
348
+ printJson({ ok: true, action: "clear", registries: [] });
349
+ return;
350
+ }
351
+ plugins.logger.log("success", "All registries cleared.");
352
+ await formatSmartconfigWithDiff(mode);
254
353
  } else {
255
- plugins.logger.log('info', 'Operation cancelled.');
354
+ plugins.logger.log("info", "Operation cancelled.");
256
355
  }
257
356
  }
258
357
 
259
358
  /**
260
359
  * Set or toggle access level
261
360
  */
262
- async function handleAccessLevel(level?: string): Promise<void> {
361
+ async function handleAccessLevel(
362
+ level: string | undefined,
363
+ mode: ICliMode,
364
+ ): Promise<void> {
263
365
  const config = await ReleaseConfig.fromCwd();
264
366
  const currentLevel = config.getAccessLevel();
265
367
 
266
368
  if (!level) {
369
+ if (!mode.interactive) {
370
+ throw new Error("Access level is required in non-interactive mode");
371
+ }
372
+
267
373
  // Interactive mode - toggle or ask
268
374
  const interactInstance = new plugins.smartinteract.SmartInteract();
269
375
  const response = await interactInstance.askQuestion({
270
- type: 'list',
271
- name: 'accessLevel',
272
- message: 'Select npm access level for publishing:',
273
- choices: ['public', 'private'],
376
+ type: "list",
377
+ name: "accessLevel",
378
+ message: "Select npm access level for publishing:",
379
+ choices: ["public", "private"],
274
380
  default: currentLevel,
275
381
  });
276
382
  level = (response as any).value;
277
383
  }
278
384
 
279
385
  // Validate the level
280
- if (level !== 'public' && level !== 'private') {
281
- plugins.logger.log('error', `Invalid access level: ${level}. Must be 'public' or 'private'.`);
386
+ if (level !== "public" && level !== "private") {
387
+ plugins.logger.log(
388
+ "error",
389
+ `Invalid access level: ${level}. Must be 'public' or 'private'.`,
390
+ );
282
391
  return;
283
392
  }
284
393
 
285
394
  if (level === currentLevel) {
286
- plugins.logger.log('info', `Access level is already set to: ${level}`);
395
+ plugins.logger.log("info", `Access level is already set to: ${level}`);
287
396
  return;
288
397
  }
289
398
 
290
- config.setAccessLevel(level as 'public' | 'private');
399
+ config.setAccessLevel(level as "public" | "private");
291
400
  await config.save();
292
- plugins.logger.log('success', `Access level set to: ${level}`);
293
- await formatSmartconfigWithDiff();
401
+ if (mode.json) {
402
+ printJson({ ok: true, action: "access", accessLevel: level });
403
+ return;
404
+ }
405
+ plugins.logger.log("success", `Access level set to: ${level}`);
406
+ await formatSmartconfigWithDiff(mode);
294
407
  }
295
408
 
296
409
  /**
297
410
  * Handle commit configuration
298
411
  */
299
- async function handleCommit(setting?: string, value?: string): Promise<void> {
412
+ async function handleCommit(
413
+ setting: string | undefined,
414
+ value: string | undefined,
415
+ mode: ICliMode,
416
+ ): Promise<void> {
300
417
  const config = await CommitConfig.fromCwd();
301
418
 
302
419
  // No setting = interactive mode
303
420
  if (!setting) {
421
+ if (!mode.interactive) {
422
+ throw new Error("Commit setting is required in non-interactive mode");
423
+ }
304
424
  await handleCommitInteractive(config);
305
425
  return;
306
426
  }
307
427
 
308
428
  // Direct setting
309
429
  switch (setting) {
310
- case 'alwaysTest':
311
- await handleCommitSetting(config, 'alwaysTest', value);
430
+ case "alwaysTest":
431
+ await handleCommitSetting(config, "alwaysTest", value, mode);
312
432
  break;
313
- case 'alwaysBuild':
314
- await handleCommitSetting(config, 'alwaysBuild', value);
433
+ case "alwaysBuild":
434
+ await handleCommitSetting(config, "alwaysBuild", value, mode);
315
435
  break;
316
436
  default:
317
- plugins.logger.log('error', `Unknown commit setting: ${setting}`);
437
+ plugins.logger.log("error", `Unknown commit setting: ${setting}`);
318
438
  showCommitHelp();
319
439
  }
320
440
  }
@@ -323,109 +443,297 @@ async function handleCommit(setting?: string, value?: string): Promise<void> {
323
443
  * Interactive commit configuration
324
444
  */
325
445
  async function handleCommitInteractive(config: CommitConfig): Promise<void> {
326
- console.log('');
327
- console.log('╭─────────────────────────────────────────────────────────────╮');
328
- console.log('│ Commit Configuration │');
329
- console.log('╰─────────────────────────────────────────────────────────────╯');
330
- console.log('');
446
+ console.log("");
447
+ console.log(
448
+ "╭─────────────────────────────────────────────────────────────╮",
449
+ );
450
+ console.log(
451
+ "│ Commit Configuration │",
452
+ );
453
+ console.log(
454
+ "╰─────────────────────────────────────────────────────────────╯",
455
+ );
456
+ console.log("");
331
457
 
332
458
  const interactInstance = new plugins.smartinteract.SmartInteract();
333
459
  const response = await interactInstance.askQuestion({
334
- type: 'checkbox',
335
- name: 'commitOptions',
336
- message: 'Select commit options to enable:',
460
+ type: "checkbox",
461
+ name: "commitOptions",
462
+ message: "Select commit options to enable:",
337
463
  choices: [
338
- { name: 'Always run tests before commit (-t)', value: 'alwaysTest' },
339
- { name: 'Always build after commit (-b)', value: 'alwaysBuild' },
464
+ { name: "Always run tests before commit (-t)", value: "alwaysTest" },
465
+ { name: "Always build after commit (-b)", value: "alwaysBuild" },
340
466
  ],
341
467
  default: [
342
- ...(config.getAlwaysTest() ? ['alwaysTest'] : []),
343
- ...(config.getAlwaysBuild() ? ['alwaysBuild'] : []),
468
+ ...(config.getAlwaysTest() ? ["alwaysTest"] : []),
469
+ ...(config.getAlwaysBuild() ? ["alwaysBuild"] : []),
344
470
  ],
345
471
  });
346
472
 
347
473
  const selected = (response as any).value || [];
348
- config.setAlwaysTest(selected.includes('alwaysTest'));
349
- config.setAlwaysBuild(selected.includes('alwaysBuild'));
474
+ config.setAlwaysTest(selected.includes("alwaysTest"));
475
+ config.setAlwaysBuild(selected.includes("alwaysBuild"));
350
476
  await config.save();
351
477
 
352
- plugins.logger.log('success', 'Commit configuration updated');
353
- await formatSmartconfigWithDiff();
478
+ plugins.logger.log("success", "Commit configuration updated");
479
+ await formatSmartconfigWithDiff(defaultCliMode);
354
480
  }
355
481
 
356
482
  /**
357
483
  * Set a specific commit setting
358
484
  */
359
- async function handleCommitSetting(config: CommitConfig, setting: string, value?: string): Promise<void> {
485
+ async function handleCommitSetting(
486
+ config: CommitConfig,
487
+ setting: string,
488
+ value: string | undefined,
489
+ mode: ICliMode,
490
+ ): Promise<void> {
360
491
  // Parse boolean value
361
- const boolValue = value === 'true' || value === '1' || value === 'on';
492
+ const boolValue = value === "true" || value === "1" || value === "on";
362
493
 
363
- if (setting === 'alwaysTest') {
494
+ if (setting === "alwaysTest") {
364
495
  config.setAlwaysTest(boolValue);
365
- } else if (setting === 'alwaysBuild') {
496
+ } else if (setting === "alwaysBuild") {
366
497
  config.setAlwaysBuild(boolValue);
367
498
  }
368
499
 
369
500
  await config.save();
370
- plugins.logger.log('success', `Set ${setting} to ${boolValue}`);
371
- await formatSmartconfigWithDiff();
501
+ if (mode.json) {
502
+ printJson({ ok: true, action: "commit", setting, value: boolValue });
503
+ return;
504
+ }
505
+ plugins.logger.log("success", `Set ${setting} to ${boolValue}`);
506
+ await formatSmartconfigWithDiff(mode);
372
507
  }
373
508
 
374
509
  /**
375
510
  * Show help for commit subcommand
376
511
  */
377
512
  function showCommitHelp(): void {
378
- console.log('');
379
- console.log('Usage: gitzone config commit [setting] [value]');
380
- console.log('');
381
- console.log('Settings:');
382
- console.log(' alwaysTest [true|false] Always run tests before commit');
383
- console.log(' alwaysBuild [true|false] Always build after commit');
384
- console.log('');
385
- console.log('Examples:');
386
- console.log(' gitzone config commit # Interactive mode');
387
- console.log(' gitzone config commit alwaysTest true');
388
- console.log(' gitzone config commit alwaysBuild false');
389
- console.log('');
513
+ console.log("");
514
+ console.log("Usage: gitzone config commit [setting] [value]");
515
+ console.log("");
516
+ console.log("Settings:");
517
+ console.log(" alwaysTest [true|false] Always run tests before commit");
518
+ console.log(" alwaysBuild [true|false] Always build after commit");
519
+ console.log("");
520
+ console.log("Examples:");
521
+ console.log(" gitzone config commit # Interactive mode");
522
+ console.log(" gitzone config commit alwaysTest true");
523
+ console.log(" gitzone config commit alwaysBuild false");
524
+ console.log("");
390
525
  }
391
526
 
392
527
  /**
393
528
  * Handle services configuration
394
529
  */
395
- async function handleServices(): Promise<void> {
530
+ async function handleServices(mode: ICliMode): Promise<void> {
531
+ if (!mode.interactive) {
532
+ throw new Error(
533
+ "Use `gitzone services config --json` or `gitzone services set ...` in non-interactive mode",
534
+ );
535
+ }
536
+
396
537
  // Import and use ServiceManager's configureServices
397
- const { ServiceManager } = await import('../mod_services/classes.servicemanager.js');
538
+ const { ServiceManager } =
539
+ await import("../mod_services/classes.servicemanager.js");
398
540
  const serviceManager = new ServiceManager();
399
541
  await serviceManager.init();
400
542
  await serviceManager.configureServices();
401
543
  }
402
544
 
545
+ async function handleGet(
546
+ configPath: string | undefined,
547
+ mode: ICliMode,
548
+ ): Promise<void> {
549
+ if (!configPath) {
550
+ throw new Error("Configuration path is required");
551
+ }
552
+
553
+ const smartconfigData = await readSmartconfigFile();
554
+ const value = getCliConfigValueFromData(smartconfigData, configPath);
555
+
556
+ if (mode.json) {
557
+ printJson({ path: configPath, value, exists: value !== undefined });
558
+ return;
559
+ }
560
+
561
+ if (value === undefined) {
562
+ plugins.logger.log("warn", `No value set for ${configPath}`);
563
+ return;
564
+ }
565
+
566
+ if (typeof value === "string") {
567
+ console.log(value);
568
+ return;
569
+ }
570
+
571
+ printJson(value);
572
+ }
573
+
574
+ async function handleSet(
575
+ configPath: string | undefined,
576
+ rawValue: string | undefined,
577
+ mode: ICliMode,
578
+ ): Promise<void> {
579
+ if (!configPath) {
580
+ throw new Error("Configuration path is required");
581
+ }
582
+ if (rawValue === undefined) {
583
+ throw new Error("Configuration value is required");
584
+ }
585
+
586
+ const smartconfigData = await readSmartconfigFile();
587
+ const parsedValue = parseConfigValue(rawValue);
588
+ setCliConfigValueInData(smartconfigData, configPath, parsedValue);
589
+ await writeSmartconfigFile(smartconfigData);
590
+
591
+ if (mode.json) {
592
+ printJson({
593
+ ok: true,
594
+ action: "set",
595
+ path: configPath,
596
+ value: parsedValue,
597
+ });
598
+ return;
599
+ }
600
+
601
+ plugins.logger.log("success", `Set ${configPath}`);
602
+ }
603
+
604
+ async function handleUnset(
605
+ configPath: string | undefined,
606
+ mode: ICliMode,
607
+ ): Promise<void> {
608
+ if (!configPath) {
609
+ throw new Error("Configuration path is required");
610
+ }
611
+
612
+ const smartconfigData = await readSmartconfigFile();
613
+ const removed = unsetCliConfigValueInData(smartconfigData, configPath);
614
+ if (!removed) {
615
+ if (mode.json) {
616
+ printJson({
617
+ ok: false,
618
+ action: "unset",
619
+ path: configPath,
620
+ removed: false,
621
+ });
622
+ return;
623
+ }
624
+
625
+ plugins.logger.log("warn", `No value set for ${configPath}`);
626
+ return;
627
+ }
628
+
629
+ await writeSmartconfigFile(smartconfigData);
630
+
631
+ if (mode.json) {
632
+ printJson({ ok: true, action: "unset", path: configPath, removed: true });
633
+ return;
634
+ }
635
+
636
+ plugins.logger.log("success", `Unset ${configPath}`);
637
+ }
638
+
639
+ function parseConfigValue(rawValue: string): any {
640
+ const trimmedValue = rawValue.trim();
641
+ if (trimmedValue === "true") {
642
+ return true;
643
+ }
644
+ if (trimmedValue === "false") {
645
+ return false;
646
+ }
647
+ if (trimmedValue === "null") {
648
+ return null;
649
+ }
650
+ if (/^-?\d+(\.\d+)?$/.test(trimmedValue)) {
651
+ return Number(trimmedValue);
652
+ }
653
+ if (
654
+ (trimmedValue.startsWith("{") && trimmedValue.endsWith("}")) ||
655
+ (trimmedValue.startsWith("[") && trimmedValue.endsWith("]")) ||
656
+ (trimmedValue.startsWith('"') && trimmedValue.endsWith('"'))
657
+ ) {
658
+ return JSON.parse(trimmedValue);
659
+ }
660
+ return rawValue;
661
+ }
662
+
403
663
  /**
404
664
  * Show help for config command
405
665
  */
406
- function showHelp(): void {
407
- console.log('');
408
- console.log('Usage: gitzone config <command> [options]');
409
- console.log('');
410
- console.log('Commands:');
411
- console.log(' show Display current release configuration');
412
- console.log(' add [url] Add a registry URL');
413
- console.log(' remove [url] Remove a registry URL');
414
- console.log(' clear Clear all registries');
415
- console.log(' access [public|private] Set npm access level for publishing');
416
- console.log(' commit [setting] [value] Configure commit options');
417
- console.log(' services Configure which services are enabled');
418
- console.log('');
419
- console.log('Examples:');
420
- console.log(' gitzone config show');
421
- console.log(' gitzone config add https://registry.npmjs.org');
422
- console.log(' gitzone config add https://verdaccio.example.com');
423
- console.log(' gitzone config remove https://registry.npmjs.org');
424
- console.log(' gitzone config clear');
425
- console.log(' gitzone config access public');
426
- console.log(' gitzone config access private');
427
- console.log(' gitzone config commit # Interactive');
428
- console.log(' gitzone config commit alwaysTest true');
429
- console.log(' gitzone config services # Interactive');
430
- console.log('');
666
+ export function showHelp(mode?: ICliMode): void {
667
+ if (mode?.json) {
668
+ printJson({
669
+ command: "config",
670
+ usage: "gitzone config <command> [options]",
671
+ commands: [
672
+ {
673
+ name: "show",
674
+ description: "Display current @git.zone/cli configuration",
675
+ },
676
+ { name: "get <path>", description: "Read a single config value" },
677
+ { name: "set <path> <value>", description: "Write a config value" },
678
+ { name: "unset <path>", description: "Delete a config value" },
679
+ { name: "add [url]", description: "Add a release registry" },
680
+ { name: "remove [url]", description: "Remove a release registry" },
681
+ { name: "clear", description: "Clear all release registries" },
682
+ {
683
+ name: "access [public|private]",
684
+ description: "Set npm publish access level",
685
+ },
686
+ {
687
+ name: "commit <setting> <value>",
688
+ description: "Set commit defaults",
689
+ },
690
+ ],
691
+ examples: [
692
+ "gitzone config show --json",
693
+ "gitzone config get release.accessLevel",
694
+ "gitzone config set cli.interactive false",
695
+ "gitzone config set cli.output json",
696
+ ],
697
+ });
698
+ return;
699
+ }
700
+
701
+ console.log("");
702
+ console.log("Usage: gitzone config <command> [options]");
703
+ console.log("");
704
+ console.log("Commands:");
705
+ console.log(
706
+ " show Display current @git.zone/cli configuration",
707
+ );
708
+ console.log(" get <path> Read a single config value");
709
+ console.log(" set <path> <value> Write a config value");
710
+ console.log(" unset <path> Delete a config value");
711
+ console.log(" add [url] Add a registry URL");
712
+ console.log(" remove [url] Remove a registry URL");
713
+ console.log(" clear Clear all registries");
714
+ console.log(
715
+ " access [public|private] Set npm access level for publishing",
716
+ );
717
+ console.log(" commit [setting] [value] Configure commit options");
718
+ console.log(
719
+ " services Configure which services are enabled",
720
+ );
721
+ console.log("");
722
+ console.log("Examples:");
723
+ console.log(" gitzone config show");
724
+ console.log(" gitzone config show --json");
725
+ console.log(" gitzone config get release.accessLevel");
726
+ console.log(" gitzone config set cli.interactive false");
727
+ console.log(" gitzone config set cli.output json");
728
+ console.log(" gitzone config unset cli.output");
729
+ console.log(" gitzone config add https://registry.npmjs.org");
730
+ console.log(" gitzone config add https://verdaccio.example.com");
731
+ console.log(" gitzone config remove https://registry.npmjs.org");
732
+ console.log(" gitzone config clear");
733
+ console.log(" gitzone config access public");
734
+ console.log(" gitzone config access private");
735
+ console.log(" gitzone config commit # Interactive");
736
+ console.log(" gitzone config commit alwaysTest true");
737
+ console.log(" gitzone config services # Interactive");
738
+ console.log("");
431
739
  }