newo 1.7.0 → 1.7.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.7.1] - 2025-09-15
9
+
10
+ ### Enhanced
11
+ - **Multi-Customer Commands**: Improved user experience for multi-customer operations
12
+ - `newo status` now automatically checks all customers when no default is specified
13
+ - `newo push` provides interactive customer selection dialog when multiple customers exist
14
+ - No more error messages for commands that support multi-customer operations
15
+ - Better user guidance with clear options for customer selection
16
+
17
+ ### Fixed
18
+ - **Command Flow**: Moved customer selection logic into command-specific handlers
19
+ - Prevents early exit errors for multi-customer operations
20
+ - Each command now handles customer selection appropriately
21
+ - Maintains backward compatibility with single-customer setups
22
+
8
23
  ## [1.7.0] - 2025-09-15
9
24
 
10
25
  ### Added
package/dist/cli.js CHANGED
@@ -151,45 +151,9 @@ async function main() {
151
151
  }
152
152
  return;
153
153
  }
154
- if (args.customer) {
155
- const customer = getCustomer(customerConfig, args.customer);
156
- if (!customer) {
157
- console.error(`Unknown customer: ${args.customer}`);
158
- console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
159
- process.exit(1);
160
- }
161
- selectedCustomer = customer;
162
- }
163
- else {
164
- // For pull command, try to get default but fall back to all customers if multiple exist
165
- if (cmd === 'pull') {
166
- try {
167
- selectedCustomer = tryGetDefaultCustomer(customerConfig);
168
- if (!selectedCustomer) {
169
- // Multiple customers exist with no default, pull from all
170
- allCustomers = getAllCustomers(customerConfig);
171
- if (verbose)
172
- console.log(`šŸ“„ No default customer specified, pulling from all ${allCustomers.length} customers`);
173
- }
174
- }
175
- catch (error) {
176
- const message = error instanceof Error ? error.message : String(error);
177
- console.error(message);
178
- process.exit(1);
179
- }
180
- }
181
- else {
182
- // For other commands, require explicit customer selection
183
- try {
184
- selectedCustomer = getDefaultCustomer(customerConfig);
185
- }
186
- catch (error) {
187
- const message = error instanceof Error ? error.message : String(error);
188
- console.error(message);
189
- process.exit(1);
190
- }
191
- }
192
- }
154
+ // Customer selection logic moved inside command processing to avoid early failures
155
+ if (verbose)
156
+ console.log(`šŸ” Command parsed: "${cmd}"`);
193
157
  if (!cmd || ['help', '-h', '--help'].includes(cmd)) {
194
158
  console.log(`NEWO CLI - Multi-Customer Support
195
159
  Usage:
@@ -201,7 +165,7 @@ Usage:
201
165
  newo import-akb <file> <persona_id> [--customer <idn>] # import AKB articles from file
202
166
 
203
167
  Flags:
204
- --customer <idn> # specify customer (if not set, uses default or all for pull)
168
+ --customer <idn> # specify customer (if not set, uses default or interactive selection)
205
169
  --verbose, -v # enable detailed logging
206
170
 
207
171
  Environment Variables:
@@ -219,8 +183,9 @@ Multi-Customer Examples:
219
183
  # Commands:
220
184
  newo pull # Pull from all customers (if no default set)
221
185
  newo pull --customer acme # Pull projects for Acme only
222
- newo push --customer globex # Push changes for Globex
223
- newo status # Status for default customer
186
+ newo status # Status for all customers (if no default set)
187
+ newo push # Interactive selection for multiple customers
188
+ newo push --customer globex # Push changes for Globex only
224
189
 
225
190
  File Structure:
226
191
  newo_customers/
@@ -233,7 +198,28 @@ File Structure:
233
198
  `);
234
199
  return;
235
200
  }
201
+ if (verbose)
202
+ console.log(`šŸ” Starting command processing for: ${cmd}`);
236
203
  if (cmd === 'pull') {
204
+ // Handle customer selection for pull command
205
+ if (args.customer) {
206
+ const customer = getCustomer(customerConfig, args.customer);
207
+ if (!customer) {
208
+ console.error(`Unknown customer: ${args.customer}`);
209
+ console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
210
+ process.exit(1);
211
+ }
212
+ selectedCustomer = customer;
213
+ }
214
+ else {
215
+ // Try to get default, fall back to all customers
216
+ selectedCustomer = tryGetDefaultCustomer(customerConfig);
217
+ if (!selectedCustomer) {
218
+ allCustomers = getAllCustomers(customerConfig);
219
+ if (verbose)
220
+ console.log(`šŸ“„ No default customer specified, pulling from all ${allCustomers.length} customers`);
221
+ }
222
+ }
237
223
  if (selectedCustomer) {
238
224
  // Single customer pull
239
225
  const accessToken = await getValidAccessToken(selectedCustomer);
@@ -255,7 +241,128 @@ File Structure:
255
241
  }
256
242
  return;
257
243
  }
244
+ if (cmd === 'status') {
245
+ // Handle customer selection for status command
246
+ if (args.customer) {
247
+ const customer = getCustomer(customerConfig, args.customer);
248
+ if (!customer) {
249
+ console.error(`Unknown customer: ${args.customer}`);
250
+ console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
251
+ process.exit(1);
252
+ }
253
+ selectedCustomer = customer;
254
+ }
255
+ else {
256
+ // Try to get default, fall back to all customers
257
+ selectedCustomer = tryGetDefaultCustomer(customerConfig);
258
+ if (!selectedCustomer) {
259
+ allCustomers = getAllCustomers(customerConfig);
260
+ console.log(`šŸ”„ Checking status for ${allCustomers.length} customers...`);
261
+ }
262
+ }
263
+ if (selectedCustomer) {
264
+ // Single customer status
265
+ await status(selectedCustomer, verbose);
266
+ }
267
+ else if (allCustomers.length > 0) {
268
+ // Multi-customer status
269
+ for (const customer of allCustomers) {
270
+ console.log(`\nšŸ“‹ Status for customer: ${customer.idn}`);
271
+ await status(customer, verbose);
272
+ }
273
+ console.log(`\nāœ… Status check completed for all ${allCustomers.length} customers`);
274
+ }
275
+ return;
276
+ }
277
+ if (cmd === 'push') {
278
+ // Handle customer selection for push command
279
+ if (args.customer) {
280
+ const customer = getCustomer(customerConfig, args.customer);
281
+ if (!customer) {
282
+ console.error(`Unknown customer: ${args.customer}`);
283
+ console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
284
+ process.exit(1);
285
+ }
286
+ selectedCustomer = customer;
287
+ }
288
+ else {
289
+ // Try to get default, provide interactive selection if multiple exist
290
+ selectedCustomer = tryGetDefaultCustomer(customerConfig);
291
+ if (!selectedCustomer) {
292
+ // Multiple customers exist with no default, ask user
293
+ allCustomers = getAllCustomers(customerConfig);
294
+ console.log(`\nšŸ“¤ Multiple customers available for push:`);
295
+ allCustomers.forEach((customer, index) => {
296
+ console.log(` ${index + 1}. ${customer.idn}`);
297
+ });
298
+ console.log(` ${allCustomers.length + 1}. All customers`);
299
+ const readline = await import('readline');
300
+ const rl = readline.createInterface({
301
+ input: process.stdin,
302
+ output: process.stdout
303
+ });
304
+ const choice = await new Promise((resolve) => {
305
+ rl.question(`\nSelect customer to push (1-${allCustomers.length + 1}): `, resolve);
306
+ });
307
+ rl.close();
308
+ const choiceNum = parseInt(choice.trim());
309
+ if (choiceNum === allCustomers.length + 1) {
310
+ // User selected "All customers"
311
+ console.log(`šŸ”„ Pushing to all ${allCustomers.length} customers...`);
312
+ }
313
+ else if (choiceNum >= 1 && choiceNum <= allCustomers.length) {
314
+ // User selected specific customer
315
+ selectedCustomer = allCustomers[choiceNum - 1] || null;
316
+ allCustomers = []; // Clear to indicate single customer mode
317
+ if (selectedCustomer) {
318
+ console.log(`šŸ”„ Pushing to customer: ${selectedCustomer.idn}`);
319
+ }
320
+ }
321
+ else {
322
+ console.error('Invalid choice. Exiting.');
323
+ process.exit(1);
324
+ }
325
+ }
326
+ }
327
+ if (selectedCustomer) {
328
+ // Single customer push
329
+ const accessToken = await getValidAccessToken(selectedCustomer);
330
+ const client = await makeClient(verbose, accessToken);
331
+ await pushChanged(client, selectedCustomer, verbose);
332
+ }
333
+ else if (allCustomers.length > 0) {
334
+ // Multi-customer push (user selected "All customers")
335
+ console.log(`šŸ”„ Pushing to ${allCustomers.length} customers...`);
336
+ for (const customer of allCustomers) {
337
+ console.log(`\nšŸ“¤ Pushing for customer: ${customer.idn}`);
338
+ const accessToken = await getValidAccessToken(customer);
339
+ const client = await makeClient(verbose, accessToken);
340
+ await pushChanged(client, customer, verbose);
341
+ }
342
+ console.log(`\nāœ… Push completed for all ${allCustomers.length} customers`);
343
+ }
344
+ return;
345
+ }
258
346
  // For all other commands, require a single selected customer
347
+ if (args.customer) {
348
+ const customer = getCustomer(customerConfig, args.customer);
349
+ if (!customer) {
350
+ console.error(`Unknown customer: ${args.customer}`);
351
+ console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
352
+ process.exit(1);
353
+ }
354
+ selectedCustomer = customer;
355
+ }
356
+ else {
357
+ try {
358
+ selectedCustomer = getDefaultCustomer(customerConfig);
359
+ }
360
+ catch (error) {
361
+ const message = error instanceof Error ? error.message : String(error);
362
+ console.error(message);
363
+ process.exit(1);
364
+ }
365
+ }
259
366
  if (!selectedCustomer) {
260
367
  console.error('Customer selection required for this command');
261
368
  process.exit(1);
@@ -263,13 +370,7 @@ File Structure:
263
370
  // Get access token for the selected customer
264
371
  const accessToken = await getValidAccessToken(selectedCustomer);
265
372
  const client = await makeClient(verbose, accessToken);
266
- if (cmd === 'push') {
267
- await pushChanged(client, selectedCustomer, verbose);
268
- }
269
- else if (cmd === 'status') {
270
- await status(selectedCustomer, verbose);
271
- }
272
- else if (cmd === 'meta') {
373
+ if (cmd === 'meta') {
273
374
  if (!selectedCustomer.projectId) {
274
375
  console.error(`No project ID configured for customer ${selectedCustomer.idn}`);
275
376
  console.error(`Set NEWO_CUSTOMER_${selectedCustomer.idn.toUpperCase()}_PROJECT_ID in your .env file`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newo",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "NEWO CLI: sync AI Agent skills and customer attributes between NEWO platform and local files. Multi-customer workspaces, Git-first workflows, comprehensive project management.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -158,40 +158,9 @@ async function main(): Promise<void> {
158
158
  return;
159
159
  }
160
160
 
161
- if (args.customer) {
162
- const customer = getCustomer(customerConfig, args.customer as string);
163
- if (!customer) {
164
- console.error(`Unknown customer: ${args.customer}`);
165
- console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
166
- process.exit(1);
167
- }
168
- selectedCustomer = customer;
169
- } else {
170
- // For pull command, try to get default but fall back to all customers if multiple exist
171
- if (cmd === 'pull') {
172
- try {
173
- selectedCustomer = tryGetDefaultCustomer(customerConfig);
174
- if (!selectedCustomer) {
175
- // Multiple customers exist with no default, pull from all
176
- allCustomers = getAllCustomers(customerConfig);
177
- if (verbose) console.log(`šŸ“„ No default customer specified, pulling from all ${allCustomers.length} customers`);
178
- }
179
- } catch (error: unknown) {
180
- const message = error instanceof Error ? error.message : String(error);
181
- console.error(message);
182
- process.exit(1);
183
- }
184
- } else {
185
- // For other commands, require explicit customer selection
186
- try {
187
- selectedCustomer = getDefaultCustomer(customerConfig);
188
- } catch (error: unknown) {
189
- const message = error instanceof Error ? error.message : String(error);
190
- console.error(message);
191
- process.exit(1);
192
- }
193
- }
194
- }
161
+ // Customer selection logic moved inside command processing to avoid early failures
162
+
163
+ if (verbose) console.log(`šŸ” Command parsed: "${cmd}"`);
195
164
 
196
165
  if (!cmd || ['help', '-h', '--help'].includes(cmd)) {
197
166
  console.log(`NEWO CLI - Multi-Customer Support
@@ -204,7 +173,7 @@ Usage:
204
173
  newo import-akb <file> <persona_id> [--customer <idn>] # import AKB articles from file
205
174
 
206
175
  Flags:
207
- --customer <idn> # specify customer (if not set, uses default or all for pull)
176
+ --customer <idn> # specify customer (if not set, uses default or interactive selection)
208
177
  --verbose, -v # enable detailed logging
209
178
 
210
179
  Environment Variables:
@@ -222,8 +191,9 @@ Multi-Customer Examples:
222
191
  # Commands:
223
192
  newo pull # Pull from all customers (if no default set)
224
193
  newo pull --customer acme # Pull projects for Acme only
225
- newo push --customer globex # Push changes for Globex
226
- newo status # Status for default customer
194
+ newo status # Status for all customers (if no default set)
195
+ newo push # Interactive selection for multiple customers
196
+ newo push --customer globex # Push changes for Globex only
227
197
 
228
198
  File Structure:
229
199
  newo_customers/
@@ -237,7 +207,27 @@ File Structure:
237
207
  return;
238
208
  }
239
209
 
210
+ if (verbose) console.log(`šŸ” Starting command processing for: ${cmd}`);
211
+
240
212
  if (cmd === 'pull') {
213
+ // Handle customer selection for pull command
214
+ if (args.customer) {
215
+ const customer = getCustomer(customerConfig, args.customer as string);
216
+ if (!customer) {
217
+ console.error(`Unknown customer: ${args.customer}`);
218
+ console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
219
+ process.exit(1);
220
+ }
221
+ selectedCustomer = customer;
222
+ } else {
223
+ // Try to get default, fall back to all customers
224
+ selectedCustomer = tryGetDefaultCustomer(customerConfig);
225
+ if (!selectedCustomer) {
226
+ allCustomers = getAllCustomers(customerConfig);
227
+ if (verbose) console.log(`šŸ“„ No default customer specified, pulling from all ${allCustomers.length} customers`);
228
+ }
229
+ }
230
+
241
231
  if (selectedCustomer) {
242
232
  // Single customer pull
243
233
  const accessToken = await getValidAccessToken(selectedCustomer);
@@ -259,7 +249,128 @@ File Structure:
259
249
  return;
260
250
  }
261
251
 
252
+ if (cmd === 'status') {
253
+ // Handle customer selection for status command
254
+ if (args.customer) {
255
+ const customer = getCustomer(customerConfig, args.customer as string);
256
+ if (!customer) {
257
+ console.error(`Unknown customer: ${args.customer}`);
258
+ console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
259
+ process.exit(1);
260
+ }
261
+ selectedCustomer = customer;
262
+ } else {
263
+ // Try to get default, fall back to all customers
264
+ selectedCustomer = tryGetDefaultCustomer(customerConfig);
265
+ if (!selectedCustomer) {
266
+ allCustomers = getAllCustomers(customerConfig);
267
+ console.log(`šŸ”„ Checking status for ${allCustomers.length} customers...`);
268
+ }
269
+ }
270
+
271
+ if (selectedCustomer) {
272
+ // Single customer status
273
+ await status(selectedCustomer, verbose);
274
+ } else if (allCustomers.length > 0) {
275
+ // Multi-customer status
276
+ for (const customer of allCustomers) {
277
+ console.log(`\nšŸ“‹ Status for customer: ${customer.idn}`);
278
+ await status(customer, verbose);
279
+ }
280
+ console.log(`\nāœ… Status check completed for all ${allCustomers.length} customers`);
281
+ }
282
+ return;
283
+ }
284
+
285
+ if (cmd === 'push') {
286
+ // Handle customer selection for push command
287
+ if (args.customer) {
288
+ const customer = getCustomer(customerConfig, args.customer as string);
289
+ if (!customer) {
290
+ console.error(`Unknown customer: ${args.customer}`);
291
+ console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
292
+ process.exit(1);
293
+ }
294
+ selectedCustomer = customer;
295
+ } else {
296
+ // Try to get default, provide interactive selection if multiple exist
297
+ selectedCustomer = tryGetDefaultCustomer(customerConfig);
298
+ if (!selectedCustomer) {
299
+ // Multiple customers exist with no default, ask user
300
+ allCustomers = getAllCustomers(customerConfig);
301
+ console.log(`\nšŸ“¤ Multiple customers available for push:`);
302
+ allCustomers.forEach((customer, index) => {
303
+ console.log(` ${index + 1}. ${customer.idn}`);
304
+ });
305
+ console.log(` ${allCustomers.length + 1}. All customers`);
306
+
307
+ const readline = await import('readline');
308
+ const rl = readline.createInterface({
309
+ input: process.stdin,
310
+ output: process.stdout
311
+ });
312
+
313
+ const choice = await new Promise<string>((resolve) => {
314
+ rl.question(`\nSelect customer to push (1-${allCustomers.length + 1}): `, resolve);
315
+ });
316
+ rl.close();
317
+
318
+ const choiceNum = parseInt(choice.trim());
319
+ if (choiceNum === allCustomers.length + 1) {
320
+ // User selected "All customers"
321
+ console.log(`šŸ”„ Pushing to all ${allCustomers.length} customers...`);
322
+ } else if (choiceNum >= 1 && choiceNum <= allCustomers.length) {
323
+ // User selected specific customer
324
+ selectedCustomer = allCustomers[choiceNum - 1] || null;
325
+ allCustomers = []; // Clear to indicate single customer mode
326
+ if (selectedCustomer) {
327
+ console.log(`šŸ”„ Pushing to customer: ${selectedCustomer.idn}`);
328
+ }
329
+ } else {
330
+ console.error('Invalid choice. Exiting.');
331
+ process.exit(1);
332
+ }
333
+ }
334
+ }
335
+
336
+ if (selectedCustomer) {
337
+ // Single customer push
338
+ const accessToken = await getValidAccessToken(selectedCustomer);
339
+ const client = await makeClient(verbose, accessToken);
340
+ await pushChanged(client, selectedCustomer, verbose);
341
+ } else if (allCustomers.length > 0) {
342
+ // Multi-customer push (user selected "All customers")
343
+ console.log(`šŸ”„ Pushing to ${allCustomers.length} customers...`);
344
+ for (const customer of allCustomers) {
345
+ console.log(`\nšŸ“¤ Pushing for customer: ${customer.idn}`);
346
+ const accessToken = await getValidAccessToken(customer);
347
+ const client = await makeClient(verbose, accessToken);
348
+ await pushChanged(client, customer, verbose);
349
+ }
350
+ console.log(`\nāœ… Push completed for all ${allCustomers.length} customers`);
351
+ }
352
+ return;
353
+ }
354
+
262
355
  // For all other commands, require a single selected customer
356
+ if (args.customer) {
357
+ const customer = getCustomer(customerConfig, args.customer as string);
358
+ if (!customer) {
359
+ console.error(`Unknown customer: ${args.customer}`);
360
+ console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
361
+ process.exit(1);
362
+ }
363
+ selectedCustomer = customer;
364
+ } else {
365
+ try {
366
+ selectedCustomer = getDefaultCustomer(customerConfig);
367
+ } catch (error: unknown) {
368
+ const message = error instanceof Error ? error.message : String(error);
369
+ console.error(message);
370
+ process.exit(1);
371
+ }
372
+ }
373
+
263
374
  if (!selectedCustomer) {
264
375
  console.error('Customer selection required for this command');
265
376
  process.exit(1);
@@ -269,11 +380,7 @@ File Structure:
269
380
  const accessToken = await getValidAccessToken(selectedCustomer);
270
381
  const client = await makeClient(verbose, accessToken);
271
382
 
272
- if (cmd === 'push') {
273
- await pushChanged(client, selectedCustomer, verbose);
274
- } else if (cmd === 'status') {
275
- await status(selectedCustomer, verbose);
276
- } else if (cmd === 'meta') {
383
+ if (cmd === 'meta') {
277
384
  if (!selectedCustomer.projectId) {
278
385
  console.error(`No project ID configured for customer ${selectedCustomer.idn}`);
279
386
  console.error(`Set NEWO_CUSTOMER_${selectedCustomer.idn.toUpperCase()}_PROJECT_ID in your .env file`);