@quonfig/cli 0.0.1 → 0.0.4

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 (40) hide show
  1. package/README.md +157 -21
  2. package/bin/dev.js +5 -9
  3. package/bin/dev.mjs +6 -0
  4. package/dist/codegen/zod-introspection.js +13 -4
  5. package/dist/commands/init.d.ts +13 -0
  6. package/dist/commands/init.js +83 -0
  7. package/dist/commands/login.js +27 -6
  8. package/dist/commands/mcp.js +1 -1
  9. package/dist/commands/pull.d.ts +15 -0
  10. package/dist/commands/pull.js +176 -0
  11. package/dist/commands/serve.js +3 -1
  12. package/dist/commands/sync.d.ts +18 -0
  13. package/dist/commands/sync.js +142 -0
  14. package/dist/commands/whoami.js +4 -1
  15. package/dist/commands/workspace/bootstrap.d.ts +13 -0
  16. package/dist/commands/workspace/bootstrap.js +155 -0
  17. package/dist/init/init-workspace.d.ts +25 -0
  18. package/dist/init/init-workspace.js +162 -0
  19. package/dist/init/samples.d.ts +11 -0
  20. package/dist/init/samples.js +118 -0
  21. package/dist/init/schema.d.ts +9 -0
  22. package/dist/init/schema.js +334 -0
  23. package/dist/init/templates.d.ts +12 -0
  24. package/dist/init/templates.js +161 -0
  25. package/dist/util/coerce.js +2 -1
  26. package/dist/util/get-client.js +6 -44
  27. package/dist/util/get-valid-token.d.ts +5 -0
  28. package/dist/util/get-valid-token.js +44 -0
  29. package/dist/util/git-ops.d.ts +32 -0
  30. package/dist/util/git-ops.js +164 -0
  31. package/dist/util/gitea-api.d.ts +11 -0
  32. package/dist/util/gitea-api.js +45 -0
  33. package/dist/util/gitea-token-storage.d.ts +8 -0
  34. package/dist/util/gitea-token-storage.js +47 -0
  35. package/dist/util/token-storage.d.ts +1 -0
  36. package/dist/util/token-storage.js +30 -11
  37. package/dist/verify/validate.d.ts +14 -0
  38. package/dist/verify/validate.js +224 -19
  39. package/oclif.manifest.json +298 -2
  40. package/package.json +2 -2
package/README.md CHANGED
@@ -14,7 +14,7 @@ $ npm install -g @quonfig/cli
14
14
  $ qfg COMMAND
15
15
  running command...
16
16
  $ qfg (--version)
17
- @quonfig/cli/0.0.1 darwin-arm64 node-v24.4.1
17
+ @quonfig/cli/0.0.4 linux-x64 node-v24.4.1
18
18
  $ qfg --help [COMMAND]
19
19
  USAGE
20
20
  $ qfg COMMAND
@@ -31,6 +31,7 @@ USAGE
31
31
  * [`qfg generate-new-hex-key`](#qfg-generate-new-hex-key)
32
32
  * [`qfg get [NAME]`](#qfg-get-name)
33
33
  * [`qfg info [NAME]`](#qfg-info-name)
34
+ * [`qfg init [DIRECTORY]`](#qfg-init-directory)
34
35
  * [`qfg interactive`](#qfg-interactive)
35
36
  * [`qfg list`](#qfg-list)
36
37
  * [`qfg login`](#qfg-login)
@@ -38,12 +39,15 @@ USAGE
38
39
  * [`qfg mcp`](#qfg-mcp)
39
40
  * [`qfg override [NAME]`](#qfg-override-name)
40
41
  * [`qfg profile`](#qfg-profile)
42
+ * [`qfg pull`](#qfg-pull)
41
43
  * [`qfg schema NAME`](#qfg-schema-name)
42
44
  * [`qfg serve DATA-FILE`](#qfg-serve-data-file)
43
45
  * [`qfg set-default [NAME]`](#qfg-set-default-name)
46
+ * [`qfg sync`](#qfg-sync)
44
47
  * [`qfg verify [PATH]`](#qfg-verify-path)
45
48
  * [`qfg whoami`](#qfg-whoami)
46
49
  * [`qfg workspace`](#qfg-workspace)
50
+ * [`qfg workspace bootstrap`](#qfg-workspace-bootstrap)
47
51
 
48
52
  ## `qfg create NAME`
49
53
 
@@ -92,7 +96,7 @@ EXAMPLES
92
96
  $ qfg create my.new.string --type json --value="{\"key\": \"value\"}"
93
97
  ```
94
98
 
95
- _See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/create.ts)_
99
+ _See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/create.ts)_
96
100
 
97
101
  ## `qfg download`
98
102
 
@@ -125,7 +129,7 @@ EXAMPLES
125
129
  $ qfg download --environment=test --sdk-key=YOUR_SDK_KEY
126
130
  ```
127
131
 
128
- _See code: [src/commands/download.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/download.ts)_
132
+ _See code: [src/commands/download.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/download.ts)_
129
133
 
130
134
  ## `qfg generate`
131
135
 
@@ -195,7 +199,7 @@ EXAMPLES
195
199
  $ qfg generate --targets node-ts -o ./dist # combine with targets
196
200
  ```
197
201
 
198
- _See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/generate.ts)_
202
+ _See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/generate.ts)_
199
203
 
200
204
  ## `qfg generate-new-hex-key`
201
205
 
@@ -218,7 +222,7 @@ EXAMPLES
218
222
  $ qfg generate-new-hex-key
219
223
  ```
220
224
 
221
- _See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/generate-new-hex-key.ts)_
225
+ _See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/generate-new-hex-key.ts)_
222
226
 
223
227
  ## `qfg get [NAME]`
224
228
 
@@ -250,7 +254,7 @@ EXAMPLES
250
254
  $ qfg get my.config.name --environment=production
251
255
  ```
252
256
 
253
- _See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/get.ts)_
257
+ _See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/get.ts)_
254
258
 
255
259
  ## `qfg info [NAME]`
256
260
 
@@ -280,7 +284,45 @@ EXAMPLES
280
284
  $ qfg info my.config.name
281
285
  ```
282
286
 
283
- _See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/info.ts)_
287
+ _See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/info.ts)_
288
+
289
+ ## `qfg init [DIRECTORY]`
290
+
291
+ Initialize or update a Quonfig workspace
292
+
293
+ ```
294
+ USAGE
295
+ $ qfg init [DIRECTORY] [--json] [--interactive] [--no-color] [--verbose] [--dry-run] [--samples]
296
+
297
+ ARGUMENTS
298
+ DIRECTORY [default: .] Target directory (default: current directory)
299
+
300
+ FLAGS
301
+ --dry-run Show what would be done without writing anything
302
+ --[no-]samples Include sample configs (default: yes on first init, no on update)
303
+
304
+ GLOBAL FLAGS
305
+ --[no-]interactive Force interactive mode
306
+ --json Format output as json.
307
+ --no-color Do not colorize output
308
+ --verbose Verbose output
309
+
310
+ DESCRIPTION
311
+ Initialize or update a Quonfig workspace
312
+
313
+ EXAMPLES
314
+ $ qfg init
315
+
316
+ $ qfg init ./my-workspace
317
+
318
+ $ qfg init --no-samples
319
+
320
+ $ qfg init --samples
321
+
322
+ $ qfg init --dry-run
323
+ ```
324
+
325
+ _See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/init.ts)_
284
326
 
285
327
  ## `qfg interactive`
286
328
 
@@ -298,7 +340,7 @@ EXAMPLES
298
340
  $ qfg
299
341
  ```
300
342
 
301
- _See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/interactive.ts)_
343
+ _See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/interactive.ts)_
302
344
 
303
345
  ## `qfg list`
304
346
 
@@ -335,7 +377,7 @@ EXAMPLES
335
377
  $ qfg list --feature-flags
336
378
  ```
337
379
 
338
- _See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/list.ts)_
380
+ _See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/list.ts)_
339
381
 
340
382
  ## `qfg login`
341
383
 
@@ -363,7 +405,7 @@ EXAMPLES
363
405
  $ qfg login --profile myprofile
364
406
  ```
365
407
 
366
- _See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/login.ts)_
408
+ _See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/login.ts)_
367
409
 
368
410
  ## `qfg logout`
369
411
 
@@ -386,7 +428,7 @@ EXAMPLES
386
428
  $ qfg logout
387
429
  ```
388
430
 
389
- _See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/logout.ts)_
431
+ _See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/logout.ts)_
390
432
 
391
433
  ## `qfg mcp`
392
434
 
@@ -416,10 +458,10 @@ EXAMPLES
416
458
 
417
459
  $ qfg mcp --editor cursor
418
460
 
419
- $ qfg mcp --url http://local-app.goatsofquonfig.com:3003/api/v1/mcp
461
+ $ qfg mcp --url http://local-app.quonfig-staging.com:3003/api/v1/mcp
420
462
  ```
421
463
 
422
- _See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/mcp.ts)_
464
+ _See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/mcp.ts)_
423
465
 
424
466
  ## `qfg override [NAME]`
425
467
 
@@ -458,7 +500,7 @@ EXAMPLES
458
500
  $ qfg override my.double.config --value=3.14159
459
501
  ```
460
502
 
461
- _See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/override.ts)_
503
+ _See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/override.ts)_
462
504
 
463
505
  ## `qfg profile`
464
506
 
@@ -481,7 +523,36 @@ EXAMPLES
481
523
  $ qfg profile
482
524
  ```
483
525
 
484
- _See code: [src/commands/profile.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/profile.ts)_
526
+ _See code: [src/commands/profile.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/profile.ts)_
527
+
528
+ ## `qfg pull`
529
+
530
+ Clone or update a local directory from the workspace Gitea repo
531
+
532
+ ```
533
+ USAGE
534
+ $ qfg pull [--json] [--interactive] [--no-color] [--verbose] [--dir <value>] [--workspace <value>]
535
+
536
+ FLAGS
537
+ --dir=<value> Local directory to clone/update (defaults to QUONFIG_DIR env var)
538
+ --workspace=<value> Workspace ID (defaults to active profile)
539
+
540
+ GLOBAL FLAGS
541
+ --[no-]interactive Force interactive mode
542
+ --json Format output as json.
543
+ --no-color Do not colorize output
544
+ --verbose Verbose output
545
+
546
+ DESCRIPTION
547
+ Clone or update a local directory from the workspace Gitea repo
548
+
549
+ EXAMPLES
550
+ $ qfg pull --dir ./our-config
551
+
552
+ $ qfg pull # uses QUONFIG_DIR env var
553
+ ```
554
+
555
+ _See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/pull.ts)_
485
556
 
486
557
  ## `qfg schema NAME`
487
558
 
@@ -519,7 +590,7 @@ EXAMPLES
519
590
  $ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
520
591
  ```
521
592
 
522
- _See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/schema.ts)_
593
+ _See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/schema.ts)_
523
594
 
524
595
  ## `qfg serve DATA-FILE`
525
596
 
@@ -555,7 +626,7 @@ EXAMPLES
555
626
  $ qfg serve ./quonfig.test.588.config.json --port=3099
556
627
  ```
557
628
 
558
- _See code: [src/commands/serve.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/serve.ts)_
629
+ _See code: [src/commands/serve.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/serve.ts)_
559
630
 
560
631
  ## `qfg set-default [NAME]`
561
632
 
@@ -599,7 +670,39 @@ EXAMPLES
599
670
  $ qfg set-default my.config.name --env-var=MY_ENV_VAR_NAME --environment=production
600
671
  ```
601
672
 
602
- _See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/set-default.ts)_
673
+ _See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/set-default.ts)_
674
+
675
+ ## `qfg sync`
676
+
677
+ Continuously poll for workspace config updates and apply them locally
678
+
679
+ ```
680
+ USAGE
681
+ $ qfg sync --watch [--json] [--interactive] [--no-color] [--verbose] [--dir <value>] [--interval <value>]
682
+
683
+ FLAGS
684
+ --dir=<value> Local directory to sync (defaults to QUONFIG_DIR env var)
685
+ --interval=<value> [default: 60] Poll interval in seconds
686
+ --watch (required) Run as a continuous polling daemon (required)
687
+
688
+ GLOBAL FLAGS
689
+ --[no-]interactive Force interactive mode
690
+ --json Format output as json.
691
+ --no-color Do not colorize output
692
+ --verbose Verbose output
693
+
694
+ DESCRIPTION
695
+ Continuously poll for workspace config updates and apply them locally
696
+
697
+ EXAMPLES
698
+ $ qfg sync --watch
699
+
700
+ $ qfg sync --watch --interval 10
701
+
702
+ $ qfg sync --watch --dir ./our-config --interval 30
703
+ ```
704
+
705
+ _See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/sync.ts)_
603
706
 
604
707
  ## `qfg verify [PATH]`
605
708
 
@@ -632,7 +735,7 @@ EXAMPLES
632
735
  $ qfg verify --json
633
736
  ```
634
737
 
635
- _See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/verify.ts)_
738
+ _See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/verify.ts)_
636
739
 
637
740
  ## `qfg whoami`
638
741
 
@@ -655,7 +758,7 @@ EXAMPLES
655
758
  $ qfg whoami
656
759
  ```
657
760
 
658
- _See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/whoami.ts)_
761
+ _See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/whoami.ts)_
659
762
 
660
763
  ## `qfg workspace`
661
764
 
@@ -678,7 +781,40 @@ EXAMPLES
678
781
  $ qfg workspace
679
782
  ```
680
783
 
681
- _See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.1/src/commands/workspace.ts)_
784
+ _See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/workspace.ts)_
785
+
786
+ ## `qfg workspace bootstrap`
787
+
788
+ Push a local git repo to Gitea as this workspace's config repository
789
+
790
+ ```
791
+ USAGE
792
+ $ qfg workspace bootstrap [--json] [--interactive] [--no-color] [--verbose] [--dir <value>] [--force]
793
+ [--skip-validate]
794
+
795
+ FLAGS
796
+ --dir=<value> Local directory to push (defaults to current directory)
797
+ --force Force push even if remote already has commits
798
+ --skip-validate Skip config validation before pushing
799
+
800
+ GLOBAL FLAGS
801
+ --[no-]interactive Force interactive mode
802
+ --json Format output as json.
803
+ --no-color Do not colorize output
804
+ --verbose Verbose output
805
+
806
+ DESCRIPTION
807
+ Push a local git repo to Gitea as this workspace's config repository
808
+
809
+ EXAMPLES
810
+ $ qfg workspace bootstrap --dir ./our-config
811
+
812
+ $ qfg workspace bootstrap --dir ./launch-migrator/output
813
+
814
+ $ qfg workspace bootstrap --dir ./our-config --skip-validate
815
+ ```
816
+
817
+ _See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.4/src/commands/workspace/bootstrap.ts)_
682
818
  <!-- commandsstop -->
683
819
 
684
820
  ## Local Development
package/bin/dev.js CHANGED
@@ -1,9 +1,5 @@
1
- #!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning
2
-
3
- // eslint-disable-next-line node/shebang
4
- async function main() {
5
- const {execute} = await import('@oclif/core')
6
- await execute({development: true, dir: import.meta.url})
7
- }
8
-
9
- await main()
1
+ #!/usr/bin/env bash
2
+ # Run dev CLI from any directory — ensures ts-node resolves from cli's node_modules
3
+ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
4
+ export TS_NODE_PROJECT="$DIR/tsconfig.json"
5
+ exec node --loader "$DIR/node_modules/ts-node/esm.mjs" --no-warnings=ExperimentalWarning "$DIR/bin/dev.mjs" "$@"
package/bin/dev.mjs ADDED
@@ -0,0 +1,6 @@
1
+ async function main() {
2
+ const {execute} = await import('@oclif/core')
3
+ await execute({development: true, dir: import.meta.url})
4
+ }
5
+
6
+ await main()
@@ -183,18 +183,27 @@ export function getMetaDescription(schema) {
183
183
  if (!meta || Object.keys(meta).length === 0) {
184
184
  return undefined;
185
185
  }
186
- // If meta only has a description key, return just the description
187
186
  const keys = Object.keys(meta);
188
- if (keys.length === 1 && keys[0] === 'description' && typeof meta.description === 'string') {
189
- return meta.description;
187
+ const standardKeys = new Set(['title', 'description']);
188
+ const hasExtraKeys = keys.some((k) => !standardKeys.has(k));
189
+ // If meta has non-standard keys (e.g. id, required, deprecated),
190
+ // return JSON representation of the entire meta object so all metadata is preserved
191
+ if (hasExtraKeys) {
192
+ try {
193
+ return JSON.stringify(meta, null, 2);
194
+ }
195
+ catch {
196
+ // If JSON serialization fails, fall through
197
+ }
190
198
  }
199
+ // For standard keys only (title, description), prefer description then title
191
200
  if (typeof meta.description === 'string') {
192
201
  return meta.description;
193
202
  }
194
203
  if (typeof meta.title === 'string') {
195
204
  return meta.title;
196
205
  }
197
- // Otherwise, return JSON representation of the entire meta object
206
+ // Fallback: return JSON representation of the entire meta object
198
207
  try {
199
208
  return JSON.stringify(meta, null, 2);
200
209
  }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from '../index.js';
2
+ export default class Init extends BaseCommand {
3
+ static args: {
4
+ directory: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ 'dry-run': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
+ samples: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<object>;
13
+ }
@@ -0,0 +1,83 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../index.js';
3
+ import { executeInit, planInit } from '../init/init-workspace.js';
4
+ export default class Init extends BaseCommand {
5
+ static args = {
6
+ directory: Args.string({
7
+ default: '.',
8
+ description: 'Target directory (default: current directory)',
9
+ }),
10
+ };
11
+ static description = 'Initialize or update a Quonfig workspace';
12
+ static examples = [
13
+ '<%= config.bin %> init',
14
+ '<%= config.bin %> init ./my-workspace',
15
+ '<%= config.bin %> init --no-samples',
16
+ '<%= config.bin %> init --samples',
17
+ '<%= config.bin %> init --dry-run',
18
+ ];
19
+ static flags = {
20
+ 'dry-run': Flags.boolean({
21
+ default: false,
22
+ description: 'Show what would be done without writing anything',
23
+ }),
24
+ samples: Flags.boolean({
25
+ allowNo: true,
26
+ description: 'Include sample configs (default: yes on first init, no on update)',
27
+ }),
28
+ };
29
+ async run() {
30
+ const { args, flags } = await this.parse(Init);
31
+ const dir = args.directory;
32
+ const plan = planInit({
33
+ dir,
34
+ dryRun: flags['dry-run'],
35
+ samples: flags.samples,
36
+ });
37
+ const mode = plan.isFirstTime ? 'Initializing' : 'Updating';
38
+ const writeActions = plan.actions.filter((a) => a.kind !== 'skip-file' && a.kind !== 'skip-hook');
39
+ const skipActions = plan.actions.filter((a) => a.kind === 'skip-file' || a.kind === 'skip-hook');
40
+ if (flags['dry-run']) {
41
+ this.log(`${mode} workspace (dry run)\n`);
42
+ if (writeActions.length > 0) {
43
+ this.log('Would perform:');
44
+ for (const action of writeActions) {
45
+ this.log(` + ${action.description}`);
46
+ }
47
+ }
48
+ if (skipActions.length > 0) {
49
+ this.log('\nSkipped:');
50
+ for (const action of skipActions) {
51
+ this.log(` - ${action.description}`);
52
+ }
53
+ }
54
+ if (writeActions.length === 0) {
55
+ this.log(' Nothing to do.');
56
+ }
57
+ if (this.jsonEnabled()) {
58
+ return { actions: plan.actions, dryRun: true, isFirstTime: plan.isFirstTime, samplesIncluded: plan.samplesIncluded };
59
+ }
60
+ return plan;
61
+ }
62
+ // Execute for real
63
+ executeInit(plan, dir);
64
+ this.log(`${mode} workspace: ${dir === '.' ? process.cwd() : dir}\n`);
65
+ for (const action of writeActions) {
66
+ this.log(` + ${action.description}`);
67
+ }
68
+ for (const action of skipActions) {
69
+ this.log(` - ${action.description}`);
70
+ }
71
+ if (!plan.samplesIncluded && plan.isFirstTime) {
72
+ this.log('\nSample data skipped. Run `qfg init --samples` to add examples.');
73
+ }
74
+ else if (!plan.isFirstTime && !plan.samplesIncluded) {
75
+ this.log('\nDocs updated. Run `qfg init --samples` to add example configs.');
76
+ }
77
+ this.log('\nRun `qfg verify` to validate the workspace.');
78
+ if (this.jsonEnabled()) {
79
+ return { actions: plan.actions, dryRun: false, isFirstTime: plan.isFirstTime, samplesIncluded: plan.samplesIncluded };
80
+ }
81
+ return plan;
82
+ }
83
+ }
@@ -1,4 +1,5 @@
1
1
  import { Flags } from '@oclif/core';
2
+ import { select } from '@inquirer/prompts';
2
3
  import { BaseCommand } from '../index.js';
3
4
  import { decodeJWT, pollForToken, requestDeviceCode } from '../util/oauth-client.js';
4
5
  import { getApiUrl } from '../util/domain-urls.js';
@@ -53,6 +54,8 @@ export default class Login extends BaseCommand {
53
54
  // Resolve org_id → workspace UUID via the API
54
55
  let workspaceId;
55
56
  let workspaceName;
57
+ let workspaceSlug;
58
+ let organizationName;
56
59
  try {
57
60
  const apiUrl = getApiUrl();
58
61
  this.verboseLog('Resolving workspace...', { apiUrl, orgId });
@@ -66,14 +69,28 @@ export default class Login extends BaseCommand {
66
69
  });
67
70
  if (res.ok) {
68
71
  const body = await res.json();
69
- const workspaces = body.json ?? body;
70
- const match = Array.isArray(workspaces)
71
- ? workspaces.find((w) => w.workosOrgId === orgId) ?? workspaces[0]
72
- : undefined;
72
+ const allWorkspaces = (body.json ?? body);
73
+ const orgWorkspaces = Array.isArray(allWorkspaces)
74
+ ? allWorkspaces.filter((w) => w.workosOrgId === orgId)
75
+ : [];
76
+ const candidates = orgWorkspaces.length > 0 ? orgWorkspaces : (Array.isArray(allWorkspaces) ? allWorkspaces : []);
77
+ let match;
78
+ if (candidates.length === 1) {
79
+ match = candidates[0];
80
+ }
81
+ else if (candidates.length > 1) {
82
+ const chosen = await select({
83
+ choices: candidates.map((w) => ({ name: w.workspaceSlug, value: w.workspaceId })),
84
+ message: 'Select workspace:',
85
+ });
86
+ match = candidates.find((w) => w.workspaceId === chosen);
87
+ }
73
88
  if (match) {
74
89
  workspaceId = match.workspaceId;
75
- workspaceName = match.organizationName;
76
- this.verboseLog('Resolved workspace', { workspaceId, workspaceName });
90
+ workspaceName = match.workspaceSlug;
91
+ workspaceSlug = match.workspaceSlug;
92
+ organizationName = match.organizationName;
93
+ this.verboseLog('Resolved workspace', { workspaceId, workspaceName, workspaceSlug, organizationName });
77
94
  }
78
95
  }
79
96
  else {
@@ -95,6 +112,7 @@ export default class Login extends BaseCommand {
95
112
  [profileName]: {
96
113
  workspace: workspaceId || orgId || 'unknown',
97
114
  workspaceName: workspaceName || (orgId ? `org:${orgId}` : undefined),
115
+ workspaceSlug,
98
116
  },
99
117
  },
100
118
  });
@@ -108,6 +126,9 @@ export default class Login extends BaseCommand {
108
126
  }
109
127
  if (workspaceName) {
110
128
  this.log(`Workspace: ${workspaceName} (${workspaceId})`);
129
+ if (organizationName) {
130
+ this.log(`Organization: ${organizationName}`);
131
+ }
111
132
  }
112
133
  else if (orgId) {
113
134
  this.log(`Organization: ${orgId}`);
@@ -12,7 +12,7 @@ export default class Mcp extends BaseCommand {
12
12
  static examples = [
13
13
  '<%= config.bin %> <%= command.id %>',
14
14
  '<%= config.bin %> <%= command.id %> --editor cursor',
15
- '<%= config.bin %> <%= command.id %> --url http://local-app.goatsofquonfig.com:3003/api/v1/mcp',
15
+ '<%= config.bin %> <%= command.id %> --url http://local-app.quonfig-staging.com:3003/api/v1/mcp',
16
16
  ];
17
17
  static flags = {
18
18
  ...BaseCommand.baseFlags,
@@ -0,0 +1,15 @@
1
+ import type { JsonObj } from '../result.js';
2
+ import { BaseCommand } from '../index.js';
3
+ export default class Pull extends BaseCommand {
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ dir: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ };
10
+ run(): Promise<JsonObj | void>;
11
+ private resolveRepoUrl;
12
+ private refreshAndGetUrl;
13
+ private looksLike401;
14
+ private maybeWriteQuonfigDir;
15
+ }