@uns-kit/cli 0.0.62 → 0.0.63

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/README.md CHANGED
@@ -46,7 +46,8 @@ When `git` is available on your PATH the scaffold also initializes a fresh repos
46
46
  ## Commands
47
47
 
48
48
  - `uns-kit create <name>` – create a new UNS project in the specified directory.
49
- - `uns-kit configure [path] [features...]` – run multiple configure templates in sequence (`--all` applies every template).
49
+ - `uns-kit configure [path] [features...]` – run multiple configure templates in sequence (`--all`, `--overwrite`).
50
+ - `uns-kit configure-templates [path] [templates...]` – copy any template directory (`--all`, `--overwrite`).
50
51
  - `uns-kit configure-devops [path]` – add Azure DevOps tooling (dependencies, script, config) to an existing project.
51
52
  - `uns-kit configure-vscode [path]` – copy VS Code launch/workspace files into an existing project.
52
53
  - `uns-kit configure-codegen [path]` – scaffold GraphQL code generation and UNS refresh scripts.
@@ -65,7 +66,16 @@ uns-kit configure --all
65
66
  uns-kit configure ./apps/gateway devops vscode codegen
66
67
  ```
67
68
 
68
- Mix and match feature names after an optional target directory. Use `--all` to apply every available template in one shot.
69
+ Mix and match feature names after an optional target directory. Use `--all` to apply every available template in one shot. Add `--overwrite` to refresh files from newer template versions.
70
+
71
+ ### Copy arbitrary templates
72
+
73
+ Need a template that is not wired into a configure feature (or added in a newer release)? Use:
74
+
75
+ ```bash
76
+ uns-kit configure-templates --all
77
+ uns-kit configure-templates ./apps/gateway uns-dictionary uns-measurements --overwrite
78
+ ```
69
79
 
70
80
  ### Configure Azure DevOps
71
81
 
@@ -85,7 +95,7 @@ The command prompts for your Azure DevOps organization/project, ensures the remo
85
95
  uns-kit configure-vscode
86
96
  ```
87
97
 
88
- Copies `.vscode/launch.json` plus a baseline workspace file into the project. Existing files are never overwritten, making it safe to re-run after hand edits.
98
+ Copies `.vscode/launch.json` plus a baseline workspace file into the project. Existing files are skipped unless you pass `--overwrite`.
89
99
 
90
100
  ### Configure GraphQL code generation
91
101
 
@@ -106,6 +116,7 @@ pnpm install
106
116
  ```
107
117
 
108
118
  Copies API-oriented examples (under `src/examples/`) and adds `@uns-kit/api` to your project dependencies.
119
+ Use `--overwrite` to refresh the examples after updating `uns-kit`.
109
120
 
110
121
  ### Add cron-based scaffolding
111
122
 
@@ -115,6 +126,7 @@ pnpm install
115
126
  ```
116
127
 
117
128
  Adds cron-oriented example stubs and installs `@uns-kit/cron`.
129
+ Use `--overwrite` to refresh the examples after updating `uns-kit`.
118
130
 
119
131
  ### Add Temporal scaffolding
120
132
 
@@ -124,6 +136,7 @@ pnpm install
124
136
  ```
125
137
 
126
138
  Copies Temporal example placeholders and installs `@uns-kit/temporal`.
139
+ Use `--overwrite` to refresh the examples after updating `uns-kit`.
127
140
 
128
141
  ### Add Python gateway scaffolding
129
142
 
@@ -132,6 +145,7 @@ uns-kit configure-python
132
145
  ```
133
146
 
134
147
  Copies the Python gateway client template (examples, scripts, requirements, proto) into your project so you can iterate on the gRPC gateway from Python alongside your TypeScript project.
148
+ Use `--overwrite` to refresh the examples after updating `uns-kit`.
135
149
 
136
150
  ### Extend the Config Schema
137
151
 
package/dist/index.js CHANGED
@@ -24,9 +24,9 @@ async function main() {
24
24
  return;
25
25
  }
26
26
  if (command === "configure-config") {
27
- const targetPath = args[1];
27
+ const { targetPath, overwrite } = parseTemplateCommandArgs(args.slice(1));
28
28
  try {
29
- await configureConfigFiles(targetPath);
29
+ await configureConfigFiles(targetPath, { overwrite });
30
30
  }
31
31
  catch (error) {
32
32
  console.error(error.message);
@@ -45,10 +45,21 @@ async function main() {
45
45
  }
46
46
  return;
47
47
  }
48
+ if (command === "configure-templates") {
49
+ const configureArgs = args.slice(1);
50
+ try {
51
+ await runConfigureTemplatesCommand(configureArgs);
52
+ }
53
+ catch (error) {
54
+ console.error(error.message);
55
+ process.exitCode = 1;
56
+ }
57
+ return;
58
+ }
48
59
  if (command === "configure-devops") {
49
- const targetPath = args[1];
60
+ const { targetPath, overwrite } = parseTemplateCommandArgs(args.slice(1));
50
61
  try {
51
- await configureDevops(targetPath);
62
+ await configureDevops(targetPath, { overwrite });
52
63
  }
53
64
  catch (error) {
54
65
  console.error(error.message);
@@ -57,9 +68,9 @@ async function main() {
57
68
  return;
58
69
  }
59
70
  if (command === "configure-vscode") {
60
- const targetPath = args[1];
71
+ const { targetPath, overwrite } = parseTemplateCommandArgs(args.slice(1));
61
72
  try {
62
- await configureVscode(targetPath);
73
+ await configureVscode(targetPath, { overwrite });
63
74
  }
64
75
  catch (error) {
65
76
  console.error(error.message);
@@ -68,9 +79,9 @@ async function main() {
68
79
  return;
69
80
  }
70
81
  if (command === "configure-codegen") {
71
- const targetPath = args[1];
82
+ const { targetPath, overwrite } = parseTemplateCommandArgs(args.slice(1));
72
83
  try {
73
- await configureCodegen(targetPath);
84
+ await configureCodegen(targetPath, { overwrite });
74
85
  }
75
86
  catch (error) {
76
87
  console.error(error.message);
@@ -79,9 +90,9 @@ async function main() {
79
90
  return;
80
91
  }
81
92
  if (command === "configure-api") {
82
- const targetPath = args[1];
93
+ const { targetPath, overwrite } = parseTemplateCommandArgs(args.slice(1));
83
94
  try {
84
- await configureApi(targetPath);
95
+ await configureApi(targetPath, { overwrite });
85
96
  }
86
97
  catch (error) {
87
98
  console.error(error.message);
@@ -90,9 +101,9 @@ async function main() {
90
101
  return;
91
102
  }
92
103
  if (command === "configure-cron") {
93
- const targetPath = args[1];
104
+ const { targetPath, overwrite } = parseTemplateCommandArgs(args.slice(1));
94
105
  try {
95
- await configureCron(targetPath);
106
+ await configureCron(targetPath, { overwrite });
96
107
  }
97
108
  catch (error) {
98
109
  console.error(error.message);
@@ -101,9 +112,9 @@ async function main() {
101
112
  return;
102
113
  }
103
114
  if (command === "configure-temporal") {
104
- const targetPath = args[1];
115
+ const { targetPath, overwrite } = parseTemplateCommandArgs(args.slice(1));
105
116
  try {
106
- await configureTemporal(targetPath);
117
+ await configureTemporal(targetPath, { overwrite });
107
118
  }
108
119
  catch (error) {
109
120
  console.error(error.message);
@@ -112,9 +123,9 @@ async function main() {
112
123
  return;
113
124
  }
114
125
  if (command === "configure-python") {
115
- const targetPath = args[1];
126
+ const { targetPath, overwrite } = parseTemplateCommandArgs(args.slice(1));
116
127
  try {
117
- await configurePython(targetPath);
128
+ await configurePython(targetPath, { overwrite });
118
129
  }
119
130
  catch (error) {
120
131
  console.error(error.message);
@@ -123,9 +134,9 @@ async function main() {
123
134
  return;
124
135
  }
125
136
  if (command === "configure-uns-reference") {
126
- const targetPath = args[1];
137
+ const { targetPath, overwrite } = parseTemplateCommandArgs(args.slice(1));
127
138
  try {
128
- await configureUnsReference(targetPath);
139
+ await configureUnsReference(targetPath, { overwrite });
129
140
  }
130
141
  catch (error) {
131
142
  console.error(error.message);
@@ -158,7 +169,8 @@ function printHelp() {
158
169
  "\nUsage: uns-kit <command> [options]\n" +
159
170
  "\nCommands:\n" +
160
171
  " create <name> Scaffold a new UNS application\n" +
161
- " configure [dir] [features...] Configure multiple templates (--all for everything)\n" +
172
+ " configure [dir] [features...] Configure multiple templates (--all, --overwrite)\n" +
173
+ " configure-templates [dir] [templates...] Copy any template directory (--all, --overwrite)\n" +
162
174
  " configure-config [dir] Copy configuration example files\n" +
163
175
  " configure-devops [dir] Configure Azure DevOps tooling in an existing project\n" +
164
176
  " configure-vscode [dir] Add VS Code workspace configuration files\n" +
@@ -235,7 +247,7 @@ async function initGitRepository(targetDir) {
235
247
  return false;
236
248
  }
237
249
  }
238
- async function configureDevops(targetPath) {
250
+ async function configureDevops(targetPath, options) {
239
251
  const targetDir = path.resolve(process.cwd(), targetPath ?? ".");
240
252
  const packagePath = path.join(targetDir, "package.json");
241
253
  const configPath = path.join(targetDir, "config.json");
@@ -330,9 +342,16 @@ async function configureDevops(targetPath) {
330
342
  throw new Error("Azure Pipelines template is missing. Please ensure templates/azure-pipelines.yml exists.");
331
343
  }
332
344
  const pipelineTargetPath = path.join(targetDir, "azure-pipelines.yml");
345
+ const overwrite = !!options?.overwrite;
333
346
  let pipelineMessage = "";
334
347
  if (await fileExists(pipelineTargetPath)) {
335
- pipelineMessage = " azure-pipelines.yml already exists (skipped).";
348
+ if (overwrite) {
349
+ await copyFile(azurePipelineTemplatePath, pipelineTargetPath);
350
+ pipelineMessage = " Overwrote azure-pipelines.yml pipeline definition.";
351
+ }
352
+ else {
353
+ pipelineMessage = " azure-pipelines.yml already exists (skipped).";
354
+ }
336
355
  }
337
356
  else {
338
357
  await copyFile(azurePipelineTemplatePath, pipelineTargetPath);
@@ -362,7 +381,7 @@ async function configureDevops(targetPath) {
362
381
  console.log(" Existing package.json already contained required entries.");
363
382
  }
364
383
  }
365
- async function configureVscode(targetPath) {
384
+ async function configureVscode(targetPath, options = {}) {
366
385
  const targetDir = path.resolve(process.cwd(), targetPath ?? ".");
367
386
  const templateDir = path.resolve(__dirname, "../templates/vscode");
368
387
  try {
@@ -371,7 +390,7 @@ async function configureVscode(targetPath) {
371
390
  catch (error) {
372
391
  throw new Error("VS Code template directory is missing. Please ensure templates/vscode is available.");
373
392
  }
374
- const { copied, skipped } = await copyTemplateDirectory(templateDir, targetDir, targetDir);
393
+ const { copied, skipped, overwritten } = await copyTemplateDirectory(templateDir, targetDir, targetDir, options);
375
394
  console.log("\nVS Code configuration files processed.");
376
395
  if (copied.length) {
377
396
  console.log(" Added:");
@@ -379,6 +398,12 @@ async function configureVscode(targetPath) {
379
398
  console.log(` ${file}`);
380
399
  }
381
400
  }
401
+ if (overwritten.length) {
402
+ console.log(" Overwritten:");
403
+ for (const file of overwritten) {
404
+ console.log(` ${file}`);
405
+ }
406
+ }
382
407
  if (skipped.length) {
383
408
  console.log(" Skipped (already exists):");
384
409
  for (const file of skipped) {
@@ -389,7 +414,7 @@ async function configureVscode(targetPath) {
389
414
  console.log(" No files were found in the VS Code template directory.");
390
415
  }
391
416
  }
392
- async function configureConfigFiles(targetPath) {
417
+ async function configureConfigFiles(targetPath, options = {}) {
393
418
  const targetDir = path.resolve(process.cwd(), targetPath ?? ".");
394
419
  const templateDir = path.resolve(__dirname, "../templates/config-files");
395
420
  try {
@@ -398,8 +423,8 @@ async function configureConfigFiles(targetPath) {
398
423
  catch (error) {
399
424
  throw new Error("Configuration template directory is missing. Please ensure templates/config-files is available.");
400
425
  }
401
- const { copied, skipped } = await copyTemplateDirectory(templateDir, targetDir, targetDir);
402
- const configFilesToAdjust = copied.filter((file) => {
426
+ const { copied, skipped, overwritten } = await copyTemplateDirectory(templateDir, targetDir, targetDir, options);
427
+ const configFilesToAdjust = [...copied, ...overwritten].filter((file) => {
403
428
  const filename = path.basename(file);
404
429
  return filename.toLowerCase().startsWith("config") && filename.toLowerCase().endsWith(".json");
405
430
  });
@@ -413,6 +438,12 @@ async function configureConfigFiles(targetPath) {
413
438
  console.log(` ${file}`);
414
439
  }
415
440
  }
441
+ if (overwritten.length) {
442
+ console.log(" Overwritten:");
443
+ for (const file of overwritten) {
444
+ console.log(` ${file}`);
445
+ }
446
+ }
416
447
  if (skipped.length) {
417
448
  console.log(" Skipped (already exists):");
418
449
  for (const file of skipped) {
@@ -436,7 +467,7 @@ async function applyConfigTemplatePlaceholders(targetDir, relativePaths) {
436
467
  await replaceConfigTemplatePlaceholders(absolutePath, replacements, packageName);
437
468
  }
438
469
  }
439
- async function configureCodegen(targetPath) {
470
+ async function configureCodegen(targetPath, options = {}) {
440
471
  const targetDir = path.resolve(process.cwd(), targetPath ?? ".");
441
472
  const templateDir = path.resolve(__dirname, "../templates/codegen");
442
473
  const packagePath = path.join(targetDir, "package.json");
@@ -457,7 +488,7 @@ async function configureCodegen(targetPath) {
457
488
  throw error;
458
489
  }
459
490
  const pkg = JSON.parse(pkgRaw);
460
- const { copied, skipped } = await copyTemplateDirectory(templateDir, targetDir, targetDir);
491
+ const { copied, skipped, overwritten } = await copyTemplateDirectory(templateDir, targetDir, targetDir, options);
461
492
  const devDeps = (pkg.devDependencies ??= {});
462
493
  const deps = pkg.dependencies ?? {};
463
494
  const requiredDevDeps = {
@@ -499,6 +530,12 @@ async function configureCodegen(targetPath) {
499
530
  console.log(` ${file}`);
500
531
  }
501
532
  }
533
+ if (overwritten.length) {
534
+ console.log(" Overwritten files:");
535
+ for (const file of overwritten) {
536
+ console.log(` ${file}`);
537
+ }
538
+ }
502
539
  if (skipped.length) {
503
540
  console.log(" Skipped existing files:");
504
541
  for (const file of skipped) {
@@ -515,50 +552,56 @@ async function configureCodegen(targetPath) {
515
552
  console.log(" Existing package.json already contained required scripts and dependencies.");
516
553
  }
517
554
  }
518
- async function configureApi(targetPath) {
555
+ async function configureApi(targetPath, options = {}) {
519
556
  await configurePlugin({
520
557
  targetPath,
521
558
  templateName: "api",
522
559
  dependencyName: "@uns-kit/api",
523
560
  dependencySpecifier: resolveUnsPackageSpecifier("@uns-kit/api", "../../uns-api/package.json"),
524
561
  label: "UNS API",
562
+ overwrite: options.overwrite,
525
563
  });
526
564
  }
527
- async function configureCron(targetPath) {
565
+ async function configureCron(targetPath, options = {}) {
528
566
  await configurePlugin({
529
567
  targetPath,
530
568
  templateName: "cron",
531
569
  dependencyName: "@uns-kit/cron",
532
570
  dependencySpecifier: resolveUnsPackageSpecifier("@uns-kit/cron", "../../uns-cron/package.json"),
533
571
  label: "UNS cron",
572
+ overwrite: options.overwrite,
534
573
  });
535
574
  }
536
- async function configureTemporal(targetPath) {
575
+ async function configureTemporal(targetPath, options = {}) {
537
576
  await configurePlugin({
538
577
  targetPath,
539
578
  templateName: "temporal",
540
579
  dependencyName: "@uns-kit/temporal",
541
580
  dependencySpecifier: resolveUnsPackageSpecifier("@uns-kit/temporal", "../../uns-temporal/package.json"),
542
581
  label: "UNS Temporal",
582
+ overwrite: options.overwrite,
543
583
  });
544
584
  }
545
- async function configurePython(targetPath) {
585
+ async function configurePython(targetPath, options = {}) {
546
586
  await configurePlugin({
547
587
  targetPath,
548
588
  templateName: "python",
549
589
  label: "UNS Python client",
590
+ overwrite: options.overwrite,
550
591
  });
551
592
  }
552
- async function configureUnsReference(targetPath) {
593
+ async function configureUnsReference(targetPath, options = {}) {
553
594
  await configurePlugin({
554
595
  targetPath,
555
596
  templateName: "uns-dictionary",
556
597
  label: "UNS dictionary (object/attribute metadata)",
598
+ overwrite: options.overwrite,
557
599
  });
558
600
  await configurePlugin({
559
601
  targetPath,
560
602
  templateName: "uns-measurements",
561
603
  label: "UNS measurements (units)",
604
+ overwrite: options.overwrite,
562
605
  });
563
606
  }
564
607
  const configureFeatureHandlers = {
@@ -585,7 +628,7 @@ const configureFeatureLabels = {
585
628
  "uns-reference": "UNS dictionaries (objects/attributes/measurements)",
586
629
  };
587
630
  async function runConfigureCommand(args) {
588
- const { targetPath, features } = parseConfigureArgs(args);
631
+ const { targetPath, features, overwrite } = parseConfigureArgs(args);
589
632
  if (!features.length) {
590
633
  throw new Error("No features specified. Provide feature names or pass --all.");
591
634
  }
@@ -594,18 +637,128 @@ async function runConfigureCommand(args) {
594
637
  console.log(`Configuring ${featureSummary} in ${location}`);
595
638
  for (const feature of features) {
596
639
  const handler = configureFeatureHandlers[feature];
597
- await handler(targetPath);
640
+ await handler(targetPath, { overwrite });
641
+ }
642
+ }
643
+ const DEFAULT_TEMPLATE_NAME = "default";
644
+ async function listTemplateDirectories(templateRoot, options) {
645
+ const entries = await readdir(templateRoot, { withFileTypes: true });
646
+ return entries
647
+ .filter((entry) => entry.isDirectory())
648
+ .map((entry) => entry.name)
649
+ .filter((name) => options?.includeDefault ? true : name !== DEFAULT_TEMPLATE_NAME)
650
+ .sort();
651
+ }
652
+ function parseConfigureTemplatesArgs(args, availableTemplates) {
653
+ const templateMap = new Map(availableTemplates.map((name) => [name.toLowerCase(), name]));
654
+ const templateSet = new Set(templateMap.keys());
655
+ const templateNames = [];
656
+ const unknownTemplates = [];
657
+ let targetPath;
658
+ let overwrite = false;
659
+ let includeAll = false;
660
+ let expectDir = false;
661
+ for (let i = 0; i < args.length; i += 1) {
662
+ const arg = args[i];
663
+ if (expectDir) {
664
+ targetPath = arg;
665
+ expectDir = false;
666
+ continue;
667
+ }
668
+ if (arg === "--dir") {
669
+ expectDir = true;
670
+ continue;
671
+ }
672
+ if (arg === "--all") {
673
+ includeAll = true;
674
+ continue;
675
+ }
676
+ if (arg === "--overwrite" || arg === "--force") {
677
+ overwrite = true;
678
+ continue;
679
+ }
680
+ if (arg.startsWith("--")) {
681
+ throw new Error(`Unknown option ${arg}.`);
682
+ }
683
+ const normalized = arg.trim().toLowerCase();
684
+ const resolved = templateMap.get(normalized);
685
+ if (resolved) {
686
+ templateNames.push(resolved);
687
+ continue;
688
+ }
689
+ if (!targetPath && (includeAll || i < args.length - 1) && !templateSet.has(normalized)) {
690
+ targetPath = arg;
691
+ continue;
692
+ }
693
+ unknownTemplates.push(arg);
694
+ }
695
+ if (expectDir) {
696
+ throw new Error("Missing value for --dir.");
697
+ }
698
+ if (unknownTemplates.length) {
699
+ const available = availableTemplates.join(", ");
700
+ throw new Error(`Unknown template(s): ${unknownTemplates.join(", ")}. Available templates: ${available || "none"}.`);
701
+ }
702
+ return { targetPath, overwrite, includeAll, templateNames };
703
+ }
704
+ async function runConfigureTemplatesCommand(args) {
705
+ const templateRoot = path.resolve(__dirname, "../templates");
706
+ const availableTemplates = await listTemplateDirectories(templateRoot, { includeDefault: true });
707
+ const allTemplates = await listTemplateDirectories(templateRoot, { includeDefault: false });
708
+ const { targetPath, overwrite, includeAll, templateNames } = parseConfigureTemplatesArgs(args, availableTemplates);
709
+ const selectedTemplates = new Set();
710
+ if (includeAll) {
711
+ allTemplates.forEach((name) => selectedTemplates.add(name));
712
+ }
713
+ for (const name of templateNames) {
714
+ selectedTemplates.add(name);
715
+ }
716
+ if (!selectedTemplates.size) {
717
+ throw new Error("No templates specified. Provide template names or pass --all.");
718
+ }
719
+ const targetDir = path.resolve(process.cwd(), targetPath ?? ".");
720
+ const sortedTemplates = Array.from(selectedTemplates).sort();
721
+ for (const templateName of sortedTemplates) {
722
+ const templateDir = path.resolve(templateRoot, templateName);
723
+ const { copied, skipped, overwritten } = await copyTemplateDirectory(templateDir, targetDir, targetDir, { overwrite });
724
+ console.log(`\nTemplate "${templateName}" processed.`);
725
+ if (copied.length) {
726
+ console.log(" Added:");
727
+ for (const file of copied) {
728
+ console.log(` ${file}`);
729
+ }
730
+ }
731
+ if (overwritten.length) {
732
+ console.log(" Overwritten:");
733
+ for (const file of overwritten) {
734
+ console.log(` ${file}`);
735
+ }
736
+ }
737
+ if (skipped.length) {
738
+ console.log(" Skipped (already exists):");
739
+ for (const file of skipped) {
740
+ console.log(` ${file}`);
741
+ }
742
+ }
743
+ if (!copied.length && !overwritten.length && !skipped.length) {
744
+ console.log(" No template files were copied.");
745
+ }
598
746
  }
599
747
  }
600
748
  function parseConfigureArgs(args) {
601
749
  let targetPath;
602
750
  let includeAll = false;
751
+ let overwrite = false;
603
752
  const featureInputs = [];
604
753
  for (const arg of args) {
605
754
  if (arg === "--all") {
606
755
  includeAll = true;
607
756
  continue;
608
757
  }
758
+ if (arg === "--overwrite" || arg === "--force") {
759
+ overwrite = true;
760
+ continue;
761
+ }
609
762
  if (arg.startsWith("--")) {
610
763
  throw new Error(`Unknown option ${arg}.`);
611
764
  }
@@ -636,7 +789,26 @@ function parseConfigureArgs(args) {
636
789
  for (const input of featureInputs) {
637
790
  addFeature(resolveConfigureFeatureName(input));
638
791
  }
639
- return { targetPath, features: featureOrder };
792
+ return { targetPath, features: featureOrder, overwrite };
793
+ }
794
+ function parseTemplateCommandArgs(args) {
795
+ let targetPath;
796
+ let overwrite = false;
797
+ for (const arg of args) {
798
+ if (arg === "--overwrite" || arg === "--force") {
799
+ overwrite = true;
800
+ continue;
801
+ }
802
+ if (arg.startsWith("--")) {
803
+ throw new Error(`Unknown option ${arg}.`);
804
+ }
805
+ if (!targetPath) {
806
+ targetPath = arg;
807
+ continue;
808
+ }
809
+ throw new Error(`Unexpected argument ${arg}.`);
810
+ }
811
+ return { targetPath, overwrite };
640
812
  }
641
813
  const configureFeatureAliases = {
642
814
  devops: "devops",
@@ -728,10 +900,11 @@ async function addGitRemote(dir, remoteName, remoteUrl) {
728
900
  throw new Error(`Failed to add git remote origin: ${error.message}`);
729
901
  }
730
902
  }
731
- async function copyTemplateDirectory(sourceDir, targetDir, targetRoot) {
903
+ async function copyTemplateDirectory(sourceDir, targetDir, targetRoot, options = {}) {
732
904
  const entries = await readdir(sourceDir, { withFileTypes: true });
733
905
  const copied = [];
734
906
  const skipped = [];
907
+ const overwritten = [];
735
908
  for (const entry of entries) {
736
909
  const sourcePath = path.join(sourceDir, entry.name);
737
910
  const destinationName = entry.isFile() ? normalizeTemplateFilename(entry.name) : entry.name;
@@ -739,22 +912,29 @@ async function copyTemplateDirectory(sourceDir, targetDir, targetRoot) {
739
912
  const relativePath = path.relative(targetRoot, destinationPath) || destinationName;
740
913
  if (entry.isDirectory()) {
741
914
  await mkdir(destinationPath, { recursive: true });
742
- const result = await copyTemplateDirectory(sourcePath, destinationPath, targetRoot);
915
+ const result = await copyTemplateDirectory(sourcePath, destinationPath, targetRoot, options);
743
916
  copied.push(...result.copied);
744
917
  skipped.push(...result.skipped);
918
+ overwritten.push(...result.overwritten);
745
919
  continue;
746
920
  }
747
921
  if (entry.isFile()) {
748
922
  await mkdir(path.dirname(destinationPath), { recursive: true });
749
923
  if (await fileExists(destinationPath)) {
750
- skipped.push(relativePath);
924
+ if (options.overwrite) {
925
+ await copyFile(sourcePath, destinationPath);
926
+ overwritten.push(relativePath);
927
+ }
928
+ else {
929
+ skipped.push(relativePath);
930
+ }
751
931
  continue;
752
932
  }
753
933
  await copyFile(sourcePath, destinationPath);
754
934
  copied.push(relativePath);
755
935
  }
756
936
  }
757
- return { copied, skipped };
937
+ return { copied, skipped, overwritten };
758
938
  }
759
939
  function normalizeTemplateFilename(filename) {
760
940
  if (filename === "gitignore" || filename === ".npmignore") {
@@ -763,7 +943,7 @@ function normalizeTemplateFilename(filename) {
763
943
  return filename;
764
944
  }
765
945
  async function configurePlugin(options) {
766
- const { targetPath, templateName, dependencyName, dependencySpecifier, label } = options;
946
+ const { targetPath, templateName, dependencyName, dependencySpecifier, label, overwrite } = options;
767
947
  const targetDir = path.resolve(process.cwd(), targetPath ?? ".");
768
948
  const templateDir = path.resolve(__dirname, `../templates/${templateName}`);
769
949
  const packagePath = path.join(targetDir, "package.json");
@@ -784,7 +964,7 @@ async function configurePlugin(options) {
784
964
  throw error;
785
965
  }
786
966
  const pkg = JSON.parse(pkgRaw);
787
- const { copied, skipped } = await copyTemplateDirectory(templateDir, targetDir, targetDir);
967
+ const { copied, skipped, overwritten } = await copyTemplateDirectory(templateDir, targetDir, targetDir, { overwrite });
788
968
  let pkgChanged = false;
789
969
  if (dependencyName && dependencySpecifier) {
790
970
  const deps = (pkg.dependencies ??= {});
@@ -803,13 +983,19 @@ async function configurePlugin(options) {
803
983
  console.log(` ${file}`);
804
984
  }
805
985
  }
986
+ if (overwritten.length) {
987
+ console.log(" Overwritten files:");
988
+ for (const file of overwritten) {
989
+ console.log(` ${file}`);
990
+ }
991
+ }
806
992
  if (skipped.length) {
807
993
  console.log(" Skipped existing files:");
808
994
  for (const file of skipped) {
809
995
  console.log(` ${file}`);
810
996
  }
811
997
  }
812
- if (!copied.length && !skipped.length) {
998
+ if (!copied.length && !overwritten.length && !skipped.length) {
813
999
  console.log(" No template files were copied.");
814
1000
  }
815
1001
  if (dependencyName && dependencySpecifier) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uns-kit/cli",
3
- "version": "0.0.62",
3
+ "version": "0.0.63",
4
4
  "description": "Command line scaffolding tool for UNS applications",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,13 +26,13 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "azure-devops-node-api": "^15.1.1",
29
- "@uns-kit/core": "1.0.29"
29
+ "@uns-kit/core": "1.0.31"
30
30
  },
31
31
  "unsKitPackages": {
32
- "@uns-kit/core": "1.0.29",
33
- "@uns-kit/api": "0.0.19",
34
- "@uns-kit/cron": "0.0.18",
35
- "@uns-kit/temporal": "0.0.18"
32
+ "@uns-kit/core": "1.0.31",
33
+ "@uns-kit/api": "0.0.41",
34
+ "@uns-kit/cron": "0.0.40",
35
+ "@uns-kit/temporal": "0.0.40"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsc -p tsconfig.build.json",
@@ -32,6 +32,6 @@ Update `config.json` with your broker, UNS URLs, and credentials. The generated
32
32
  - Run `uns-kit configure-codegen` to scaffold GraphQL code generation and UNS refresh scripts.
33
33
  - Edit `uns-dictionary.json` (object types/attributes + descriptions) and run `pnpm run generate-uns-dictionary` to emit `src/uns/uns-dictionary.generated.ts` for IDE hints/metadata; publish calls will automatically fall back to these descriptions when you omit them.
34
34
  - Edit `uns-measurements.json` (units + descriptions) and run `pnpm run generate-uns-measurements` to emit `src/uns/uns-measurements.generated.ts` and feed measurement unit IntelliSense.
35
- - Run `uns-kit configure-api` / `configure-cron` / `configure-temporal` to pull in example stubs and install the matching UNS plugins.
35
+ - Run `uns-kit configure-api` / `configure-cron` / `configure-temporal` to pull in example stubs and install the matching UNS plugins (add `--overwrite` to refresh templates).
36
36
  - Run `uns-kit configure-python` to copy the Python gateway client template (examples, scripts, proto).
37
37
  - Commit your new project and start building!