@intlayer/cli 9.5.11 → 9.6.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 (90) hide show
  1. package/dist/cjs/_virtual/_utils_asset.cjs +1 -1
  2. package/dist/cjs/build.cjs +2 -2
  3. package/dist/cjs/build.cjs.map +1 -1
  4. package/dist/cjs/ci.cjs +1 -1
  5. package/dist/cjs/cli.cjs +51 -12
  6. package/dist/cjs/cli.cjs.map +1 -1
  7. package/dist/cjs/extract.cjs +1 -1
  8. package/dist/cjs/fill/fill.cjs +25 -9
  9. package/dist/cjs/fill/fill.cjs.map +1 -1
  10. package/dist/cjs/getTargetDictionary.cjs +1 -1
  11. package/dist/cjs/index.cjs +15 -1
  12. package/dist/cjs/init.cjs +98 -27
  13. package/dist/cjs/init.cjs.map +1 -1
  14. package/dist/cjs/initChromeExtension.cjs +26 -3
  15. package/dist/cjs/initChromeExtension.cjs.map +1 -1
  16. package/dist/cjs/initInfra.cjs +6 -0
  17. package/dist/cjs/initInfra.cjs.map +1 -1
  18. package/dist/cjs/initMCP.cjs +18 -7
  19. package/dist/cjs/initMCP.cjs.map +1 -1
  20. package/dist/cjs/initSkills.cjs +37 -34
  21. package/dist/cjs/initSkills.cjs.map +1 -1
  22. package/dist/cjs/listContentDeclaration.cjs +1 -1
  23. package/dist/cjs/listProjects.cjs +1 -1
  24. package/dist/cjs/pull.cjs +1 -1
  25. package/dist/cjs/push/push.cjs +1 -1
  26. package/dist/cjs/reviewDoc/reviewDoc.cjs +1 -1
  27. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs +1 -1
  28. package/dist/cjs/scan.cjs +1 -1
  29. package/dist/cjs/scan.cjs.map +1 -1
  30. package/dist/cjs/translateDoc/translateDoc.cjs +1 -1
  31. package/dist/cjs/upgrade.cjs +1 -1
  32. package/dist/cjs/utils/getParentPackageJSON.cjs +1 -1
  33. package/dist/cjs/utils/isInteractiveTerminal.cjs +12 -0
  34. package/dist/cjs/utils/isInteractiveTerminal.cjs.map +1 -0
  35. package/dist/cjs/utils/parseChoice.cjs +21 -0
  36. package/dist/cjs/utils/parseChoice.cjs.map +1 -0
  37. package/dist/esm/_virtual/_utils_asset.mjs +1 -1
  38. package/dist/esm/build.mjs +1 -1
  39. package/dist/esm/build.mjs.map +1 -1
  40. package/dist/esm/ci.mjs +1 -1
  41. package/dist/esm/cli.mjs +51 -12
  42. package/dist/esm/cli.mjs.map +1 -1
  43. package/dist/esm/extract.mjs +1 -1
  44. package/dist/esm/fill/fill.mjs +25 -9
  45. package/dist/esm/fill/fill.mjs.map +1 -1
  46. package/dist/esm/getTargetDictionary.mjs +1 -1
  47. package/dist/esm/index.mjs +5 -4
  48. package/dist/esm/init.mjs +93 -28
  49. package/dist/esm/init.mjs.map +1 -1
  50. package/dist/esm/initChromeExtension.mjs +24 -4
  51. package/dist/esm/initChromeExtension.mjs.map +1 -1
  52. package/dist/esm/initInfra.mjs +6 -0
  53. package/dist/esm/initInfra.mjs.map +1 -1
  54. package/dist/esm/initMCP.mjs +21 -11
  55. package/dist/esm/initMCP.mjs.map +1 -1
  56. package/dist/esm/initSkills.mjs +37 -36
  57. package/dist/esm/initSkills.mjs.map +1 -1
  58. package/dist/esm/listContentDeclaration.mjs +1 -1
  59. package/dist/esm/listProjects.mjs +1 -1
  60. package/dist/esm/pull.mjs +1 -1
  61. package/dist/esm/push/push.mjs +1 -1
  62. package/dist/esm/reviewDoc/reviewDoc.mjs +1 -1
  63. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs +1 -1
  64. package/dist/esm/scan.mjs +1 -1
  65. package/dist/esm/scan.mjs.map +1 -1
  66. package/dist/esm/translateDoc/translateDoc.mjs +1 -1
  67. package/dist/esm/upgrade.mjs +1 -1
  68. package/dist/esm/utils/getParentPackageJSON.mjs +1 -1
  69. package/dist/esm/utils/isInteractiveTerminal.mjs +11 -0
  70. package/dist/esm/utils/isInteractiveTerminal.mjs.map +1 -0
  71. package/dist/esm/utils/parseChoice.mjs +20 -0
  72. package/dist/esm/utils/parseChoice.mjs.map +1 -0
  73. package/dist/types/fill/fill.d.ts +22 -1
  74. package/dist/types/fill/fill.d.ts.map +1 -1
  75. package/dist/types/fill/index.d.ts +2 -2
  76. package/dist/types/index.d.ts +6 -5
  77. package/dist/types/init.d.ts +36 -0
  78. package/dist/types/init.d.ts.map +1 -1
  79. package/dist/types/initChromeExtension.d.ts +19 -1
  80. package/dist/types/initChromeExtension.d.ts.map +1 -1
  81. package/dist/types/initInfra.d.ts.map +1 -1
  82. package/dist/types/initMCP.d.ts +16 -2
  83. package/dist/types/initMCP.d.ts.map +1 -1
  84. package/dist/types/initSkills.d.ts +23 -6
  85. package/dist/types/initSkills.d.ts.map +1 -1
  86. package/dist/types/utils/isInteractiveTerminal.d.ts +9 -0
  87. package/dist/types/utils/isInteractiveTerminal.d.ts.map +1 -0
  88. package/dist/types/utils/parseChoice.d.ts +13 -0
  89. package/dist/types/utils/parseChoice.d.ts.map +1 -0
  90. package/package.json +18 -13
@@ -1,43 +1,25 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
3
3
  const require_loadPrompts = require('./loadPrompts.cjs');
4
+ const require_utils_isInteractiveTerminal = require('./utils/isInteractiveTerminal.cjs');
5
+ const require_utils_parseChoice = require('./utils/parseChoice.cjs');
4
6
  const require_init = require('./init.cjs');
5
- let node_path = require("node:path");
6
7
  let _intlayer_engine_cli = require("@intlayer/engine/cli");
7
- let node_fs = require("node:fs");
8
8
  let enquirer = require("enquirer");
9
9
  enquirer = require_runtime.__toESM(enquirer);
10
10
 
11
11
  //#region src/initSkills.ts
12
- const PLATFORM_CHECKS = _intlayer_engine_cli.PLATFORMS.filter((platform) => _intlayer_engine_cli.PLATFORMS_METADATA[platform]?.check).map((platform) => ({
13
- check: _intlayer_engine_cli.PLATFORMS_METADATA[platform]?.check ?? (() => false),
14
- platform
15
- }));
16
12
  const PLATFORM_OPTIONS = _intlayer_engine_cli.PLATFORMS.map((platform) => ({
17
13
  value: platform,
18
14
  label: _intlayer_engine_cli.PLATFORMS_METADATA[platform]?.label ?? "",
19
15
  hint: `(${_intlayer_engine_cli.PLATFORMS_METADATA[platform]?.dir})`
20
16
  }));
21
- const getDetectedPlatform = () => PLATFORM_CHECKS.find(({ check }) => check())?.platform;
22
- const getDependencies = (root) => {
23
- try {
24
- const packageJsonPath = (0, node_path.join)(root, "package.json");
25
- if (!(0, node_fs.existsSync)(packageJsonPath)) return {};
26
- const { dependencies = {}, devDependencies = {} } = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8"));
27
- return {
28
- ...dependencies,
29
- ...devDependencies
30
- };
31
- } catch {
32
- return {};
33
- }
34
- };
35
17
  /**
36
18
  * Asks which AI platform the user is using, preselecting the detected one.
37
19
  * Resolves to `undefined` when the prompt is cancelled.
38
20
  */
39
- const promptPlatform = async () => {
40
- const detectedPlatform = getDetectedPlatform();
21
+ const promptPlatform = async (projectRoot) => {
22
+ const detectedPlatform = (0, _intlayer_engine_cli.detectPlatform)(projectRoot);
41
23
  try {
42
24
  return (await enquirer.default.prompt({
43
25
  type: "autocomplete",
@@ -55,32 +37,50 @@ const promptPlatform = async () => {
55
37
  return;
56
38
  }
57
39
  };
40
+ /** Validates a `--platform` value, ignoring case. */
41
+ const parsePlatform = (value) => require_utils_parseChoice.parseChoice(value, _intlayer_engine_cli.PLATFORMS, "--platform");
42
+ /** Validates the `--skills` values, ignoring case. */
43
+ const parseSkills = (values) => values.flatMap((value) => value.split(",")).filter((value) => value.trim() !== "").map((value) => require_utils_parseChoice.parseChoice(value, _intlayer_engine_cli.SKILLS, "--skills"));
44
+ /**
45
+ * Resolves the platform without prompting: the given one, else the detected
46
+ * one. Logs how to pass it and sets a failing exit code when neither exists.
47
+ */
48
+ const resolvePlatformWithoutPrompt = async (projectRoot, platform, command) => {
49
+ const resolvedPlatform = platform ?? (0, _intlayer_engine_cli.detectPlatform)(projectRoot);
50
+ if (!resolvedPlatform) {
51
+ (await require_loadPrompts.loadPrompts()).log.error(`No AI platform detected. Pass it explicitly: ${command} --platform <platform>\nPlatforms: ${_intlayer_engine_cli.PLATFORMS.join(", ")}`);
52
+ process.exitCode = 1;
53
+ }
54
+ return resolvedPlatform;
55
+ };
58
56
  /**
59
57
  * Installs the Intlayer documentation skills. The skills are picked first, then
60
- * the platform (unless preselected). Resolves to the platform used, so a
61
- * following step (e.g. MCP) can reuse it without asking again.
58
+ * the platform. Without a terminal (AI agents, CI), nothing is prompted: the
59
+ * skills default to the project stack and the platform to the detected one.
60
+ * Resolves to the platform used, so a following step (e.g. MCP) can reuse it
61
+ * without asking again.
62
62
  */
63
- const initSkills = async (projectRoot, preselectedPlatform) => {
63
+ const initSkills = async (projectRoot, options = {}) => {
64
64
  const p = await require_loadPrompts.loadPrompts();
65
- const root = require_init.findProjectRoot(projectRoot ? (0, node_path.resolve)(projectRoot) : process.cwd());
65
+ const root = require_init.resolveProjectRoot(projectRoot);
66
+ const isInteractive = require_utils_isInteractiveTerminal.isInteractiveTerminal();
66
67
  p.intro("Initializing Intlayer skills");
67
- const dependencies = getDependencies(root);
68
- const initialValues = (0, _intlayer_engine_cli.getInitialSkills)(dependencies);
69
- const selectedSkills = await p.multiselect({
68
+ const initialSkills = (0, _intlayer_engine_cli.getInitialSkills)(require_init.getProjectDependencies(root));
69
+ const selectedSkills = options.skills ?? (isInteractive ? await p.multiselect({
70
70
  message: "Select the documentation skills to provide to your AI:",
71
- initialValues,
71
+ initialValues: initialSkills,
72
72
  options: _intlayer_engine_cli.SKILLS.map((skill) => ({
73
73
  value: skill,
74
74
  label: skill,
75
75
  hint: _intlayer_engine_cli.SKILLS_METADATA[skill]
76
76
  })),
77
77
  required: false
78
- });
79
- if (p.isCancel(selectedSkills) || !selectedSkills || selectedSkills.length === 0) {
78
+ }) : initialSkills);
79
+ if (p.isCancel(selectedSkills) || selectedSkills.length === 0) {
80
80
  p.cancel("Operation cancelled. No skills selected.");
81
81
  return;
82
82
  }
83
- const platform = preselectedPlatform ?? await promptPlatform();
83
+ const platform = isInteractive ? options.platform ?? await promptPlatform(root) : await resolvePlatformWithoutPrompt(root, options.platform, "intlayer init skills");
84
84
  if (!platform) {
85
85
  p.cancel("Operation cancelled. No platform selected.");
86
86
  return;
@@ -94,6 +94,7 @@ const initSkills = async (projectRoot, preselectedPlatform) => {
94
94
  } catch (error) {
95
95
  s.stop("Failed to install skills");
96
96
  p.log.error(error instanceof Error ? error.message : String(error));
97
+ process.exitCode = 1;
97
98
  }
98
99
  p.outro("Intlayer skills initialization complete");
99
100
  return platform;
@@ -101,7 +102,9 @@ const initSkills = async (projectRoot, preselectedPlatform) => {
101
102
 
102
103
  //#endregion
103
104
  exports.PLATFORM_OPTIONS = PLATFORM_OPTIONS;
104
- exports.getDetectedPlatform = getDetectedPlatform;
105
105
  exports.initSkills = initSkills;
106
+ exports.parsePlatform = parsePlatform;
107
+ exports.parseSkills = parseSkills;
106
108
  exports.promptPlatform = promptPlatform;
109
+ exports.resolvePlatformWithoutPrompt = resolvePlatformWithoutPrompt;
107
110
  //# sourceMappingURL=initSkills.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"initSkills.cjs","names":["PLATFORMS","PLATFORMS_METADATA","join","existsSync","readFileSync","loadPrompts","findProjectRoot","resolve","getInitialSkills","SKILLS","SKILLS_METADATA","installSkills"],"sources":["../../src/initSkills.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport {\n getInitialSkills,\n installSkills,\n PLATFORMS,\n PLATFORMS_METADATA,\n type Platform,\n SKILLS,\n SKILLS_METADATA,\n} from '@intlayer/engine/cli';\nimport enquirer from 'enquirer';\nimport { findProjectRoot } from './init';\nimport { loadPrompts } from './loadPrompts';\n\nconst PLATFORM_CHECKS: Array<{ check: () => boolean; platform: Platform }> =\n PLATFORMS.filter((platform) => PLATFORMS_METADATA[platform]?.check).map(\n (platform) => ({\n check: PLATFORMS_METADATA[platform]?.check ?? (() => false),\n platform,\n })\n );\n\nexport const PLATFORM_OPTIONS: Array<{\n value: Platform;\n label: string;\n hint: string;\n}> = PLATFORMS.map((platform) => ({\n value: platform,\n label: PLATFORMS_METADATA[platform]?.label ?? '',\n hint: `(${PLATFORMS_METADATA[platform]?.dir})`,\n}));\n\nexport const getDetectedPlatform = (): Platform | undefined =>\n PLATFORM_CHECKS.find(({ check }) => check())?.platform;\n\nconst getDependencies = (root: string): Record<string, string> => {\n try {\n const packageJsonPath = join(root, 'package.json');\n if (!existsSync(packageJsonPath)) return {};\n\n const { dependencies = {}, devDependencies = {} } = JSON.parse(\n readFileSync(packageJsonPath, 'utf-8')\n );\n return { ...dependencies, ...devDependencies };\n } catch {\n return {};\n }\n};\n\n/**\n * Asks which AI platform the user is using, preselecting the detected one.\n * Resolves to `undefined` when the prompt is cancelled.\n */\nexport const promptPlatform = async (): Promise<Platform | undefined> => {\n const detectedPlatform = getDetectedPlatform();\n\n try {\n const response = await enquirer.prompt<{ platform: Platform }>({\n type: 'autocomplete',\n name: 'platform',\n message: 'Which platform are you using? (Type to search)',\n multiple: false,\n initial: detectedPlatform\n ? PLATFORMS.indexOf(detectedPlatform)\n : undefined,\n choices: PLATFORM_OPTIONS.map((option) => ({\n name: option.value,\n message: option.label,\n hint: option.hint,\n })),\n });\n\n return response.platform;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Installs the Intlayer documentation skills. The skills are picked first, then\n * the platform (unless preselected). Resolves to the platform used, so a\n * following step (e.g. MCP) can reuse it without asking again.\n */\nexport const initSkills = async (\n projectRoot?: string,\n preselectedPlatform?: Platform\n): Promise<Platform | undefined> => {\n const p = await loadPrompts();\n\n const root = findProjectRoot(\n projectRoot ? resolve(projectRoot) : process.cwd()\n );\n\n p.intro('Initializing Intlayer skills');\n\n const dependencies = getDependencies(root);\n const initialValues = getInitialSkills(dependencies);\n\n const selectedSkills = await p.multiselect({\n message: 'Select the documentation skills to provide to your AI:',\n initialValues,\n options: SKILLS.map((skill) => ({\n value: skill,\n label: skill,\n hint: SKILLS_METADATA[skill],\n })),\n required: false,\n });\n\n if (\n p.isCancel(selectedSkills) ||\n !selectedSkills ||\n (selectedSkills as string[]).length === 0\n ) {\n p.cancel('Operation cancelled. No skills selected.');\n return;\n }\n\n const platform = preselectedPlatform ?? (await promptPlatform());\n\n if (!platform) {\n p.cancel('Operation cancelled. No platform selected.');\n return;\n }\n\n const s = p.spinner();\n s.start('Installing skills...');\n\n try {\n const result = await installSkills(root, platform, selectedSkills);\n\n s.stop('Skills installed successfully');\n\n p.note(result, 'Success');\n } catch (error) {\n s.stop('Failed to install skills');\n p.log.error(error instanceof Error ? error.message : String(error));\n }\n\n p.outro('Intlayer skills initialization complete');\n\n return platform;\n};\n"],"mappings":";;;;;;;;;;;AAeA,MAAM,kBACJA,+BAAU,QAAQ,aAAaC,wCAAmB,SAAS,EAAE,KAAK,CAAC,CAAC,KACjE,cAAc;CACb,OAAOA,wCAAmB,SAAS,EAAE,gBAAgB;CACrD;AACF,EACF;AAEF,MAAa,mBAIRD,+BAAU,KAAK,cAAc;CAChC,OAAO;CACP,OAAOC,wCAAmB,SAAS,EAAE,SAAS;CAC9C,MAAM,IAAIA,wCAAmB,SAAS,EAAE,IAAI;AAC9C,EAAE;AAEF,MAAa,4BACX,gBAAgB,MAAM,EAAE,YAAY,MAAM,CAAC,CAAC,EAAE;AAEhD,MAAM,mBAAmB,SAAyC;CAChE,IAAI;EACF,MAAM,sBAAkBC,gBAAK,MAAM,cAAc;EACjD,IAAI,KAACC,oBAAW,eAAe,GAAG,OAAO,CAAC;EAE1C,MAAM,EAAE,eAAe,CAAC,GAAG,kBAAkB,CAAC,MAAM,KAAK,UACvDC,sBAAa,iBAAiB,OAAO,CACvC;EACA,OAAO;GAAE,GAAG;GAAc,GAAG;EAAgB;CAC/C,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;;;;AAMA,MAAa,iBAAiB,YAA2C;CACvE,MAAM,mBAAmB,oBAAoB;CAE7C,IAAI;EAgBF,QAAO,MAfgB,iBAAS,OAA+B;GAC7D,MAAM;GACN,MAAM;GACN,SAAS;GACT,UAAU;GACV,SAAS,mBACLJ,+BAAU,QAAQ,gBAAgB,IAClC;GACJ,SAAS,iBAAiB,KAAK,YAAY;IACzC,MAAM,OAAO;IACb,SAAS,OAAO;IAChB,MAAM,OAAO;GACf,EAAE;EACJ,CAAC,EAEc,CAAC;CAClB,QAAQ;EACN;CACF;AACF;;;;;;AAOA,MAAa,aAAa,OACxB,aACA,wBACkC;CAClC,MAAM,IAAI,MAAMK,gCAAY;CAE5B,MAAM,OAAOC,6BACX,kBAAcC,mBAAQ,WAAW,IAAI,QAAQ,IAAI,CACnD;CAEA,EAAE,MAAM,8BAA8B;CAEtC,MAAM,eAAe,gBAAgB,IAAI;CACzC,MAAM,oBAAgBC,uCAAiB,YAAY;CAEnD,MAAM,iBAAiB,MAAM,EAAE,YAAY;EACzC,SAAS;EACT;EACA,SAASC,4BAAO,KAAK,WAAW;GAC9B,OAAO;GACP,OAAO;GACP,MAAMC,qCAAgB;EACxB,EAAE;EACF,UAAU;CACZ,CAAC;CAED,IACE,EAAE,SAAS,cAAc,KACzB,CAAC,kBACA,eAA4B,WAAW,GACxC;EACA,EAAE,OAAO,0CAA0C;EACnD;CACF;CAEA,MAAM,WAAW,uBAAwB,MAAM,eAAe;CAE9D,IAAI,CAAC,UAAU;EACb,EAAE,OAAO,4CAA4C;EACrD;CACF;CAEA,MAAM,IAAI,EAAE,QAAQ;CACpB,EAAE,MAAM,sBAAsB;CAE9B,IAAI;EACF,MAAM,SAAS,UAAMC,oCAAc,MAAM,UAAU,cAAc;EAEjE,EAAE,KAAK,+BAA+B;EAEtC,EAAE,KAAK,QAAQ,SAAS;CAC1B,SAAS,OAAO;EACd,EAAE,KAAK,0BAA0B;EACjC,EAAE,IAAI,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;CACpE;CAEA,EAAE,MAAM,yCAAyC;CAEjD,OAAO;AACT"}
1
+ {"version":3,"file":"initSkills.cjs","names":["PLATFORMS","PLATFORMS_METADATA","detectPlatform","parseChoice","SKILLS","loadPrompts","resolveProjectRoot","isInteractiveTerminal","getInitialSkills","getProjectDependencies","SKILLS_METADATA","installSkills"],"sources":["../../src/initSkills.ts"],"sourcesContent":["import {\n detectPlatform,\n getInitialSkills,\n installSkills,\n PLATFORMS,\n PLATFORMS_METADATA,\n type Platform,\n SKILLS,\n SKILLS_METADATA,\n type Skill,\n} from '@intlayer/engine/cli';\nimport enquirer from 'enquirer';\nimport { getProjectDependencies, resolveProjectRoot } from './init';\nimport { loadPrompts } from './loadPrompts';\nimport { isInteractiveTerminal } from './utils/isInteractiveTerminal';\nimport { parseChoice } from './utils/parseChoice';\n\nexport const PLATFORM_OPTIONS: Array<{\n value: Platform;\n label: string;\n hint: string;\n}> = PLATFORMS.map((platform) => ({\n value: platform,\n label: PLATFORMS_METADATA[platform]?.label ?? '',\n hint: `(${PLATFORMS_METADATA[platform]?.dir})`,\n}));\n\n/**\n * Asks which AI platform the user is using, preselecting the detected one.\n * Resolves to `undefined` when the prompt is cancelled.\n */\nexport const promptPlatform = async (\n projectRoot: string\n): Promise<Platform | undefined> => {\n const detectedPlatform = detectPlatform(projectRoot);\n\n try {\n const response = await enquirer.prompt<{ platform: Platform }>({\n type: 'autocomplete',\n name: 'platform',\n message: 'Which platform are you using? (Type to search)',\n multiple: false,\n initial: detectedPlatform\n ? PLATFORMS.indexOf(detectedPlatform)\n : undefined,\n choices: PLATFORM_OPTIONS.map((option) => ({\n name: option.value,\n message: option.label,\n hint: option.hint,\n })),\n });\n\n return response.platform;\n } catch {\n return undefined;\n }\n};\n\n/** Options of {@link initSkills}; anything omitted is prompted for. */\nexport type InitSkillsOptions = {\n /** AI platform to install the skills for. */\n platform?: Platform;\n /** Skills to install. Defaults to the ones matching the project stack. */\n skills?: Skill[];\n};\n\n/** Validates a `--platform` value, ignoring case. */\nexport const parsePlatform = (value: string): Platform =>\n parseChoice(value, PLATFORMS, '--platform');\n\n/** Validates the `--skills` values, ignoring case. */\nexport const parseSkills = (values: string[]): Skill[] =>\n values\n .flatMap((value) => value.split(','))\n .filter((value) => value.trim() !== '')\n .map((value) => parseChoice(value, SKILLS, '--skills'));\n\n/**\n * Resolves the platform without prompting: the given one, else the detected\n * one. Logs how to pass it and sets a failing exit code when neither exists.\n */\nexport const resolvePlatformWithoutPrompt = async (\n projectRoot: string,\n platform: Platform | undefined,\n command: string\n): Promise<Platform | undefined> => {\n const resolvedPlatform = platform ?? detectPlatform(projectRoot);\n\n if (!resolvedPlatform) {\n const p = await loadPrompts();\n\n p.log.error(\n `No AI platform detected. Pass it explicitly: ${command} --platform <platform>\\nPlatforms: ${PLATFORMS.join(', ')}`\n );\n process.exitCode = 1;\n }\n\n return resolvedPlatform;\n};\n\n/**\n * Installs the Intlayer documentation skills. The skills are picked first, then\n * the platform. Without a terminal (AI agents, CI), nothing is prompted: the\n * skills default to the project stack and the platform to the detected one.\n * Resolves to the platform used, so a following step (e.g. MCP) can reuse it\n * without asking again.\n */\nexport const initSkills = async (\n projectRoot?: string,\n options: InitSkillsOptions = {}\n): Promise<Platform | undefined> => {\n const p = await loadPrompts();\n\n const root = resolveProjectRoot(projectRoot);\n const isInteractive = isInteractiveTerminal();\n\n p.intro('Initializing Intlayer skills');\n\n const initialSkills = getInitialSkills(getProjectDependencies(root));\n\n const selectedSkills =\n options.skills ??\n (isInteractive\n ? await p.multiselect({\n message: 'Select the documentation skills to provide to your AI:',\n initialValues: initialSkills,\n options: SKILLS.map((skill) => ({\n value: skill,\n label: skill,\n hint: SKILLS_METADATA[skill],\n })),\n required: false,\n })\n : initialSkills);\n\n if (p.isCancel(selectedSkills) || selectedSkills.length === 0) {\n p.cancel('Operation cancelled. No skills selected.');\n return;\n }\n\n const platform = isInteractive\n ? (options.platform ?? (await promptPlatform(root)))\n : await resolvePlatformWithoutPrompt(\n root,\n options.platform,\n 'intlayer init skills'\n );\n\n if (!platform) {\n p.cancel('Operation cancelled. No platform selected.');\n return;\n }\n\n const s = p.spinner();\n s.start('Installing skills...');\n\n try {\n const result = await installSkills(root, platform, selectedSkills);\n\n s.stop('Skills installed successfully');\n\n p.note(result, 'Success');\n } catch (error) {\n s.stop('Failed to install skills');\n p.log.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n\n p.outro('Intlayer skills initialization complete');\n\n return platform;\n};\n"],"mappings":";;;;;;;;;;;AAiBA,MAAa,mBAIRA,+BAAU,KAAK,cAAc;CAChC,OAAO;CACP,OAAOC,wCAAmB,SAAS,EAAE,SAAS;CAC9C,MAAM,IAAIA,wCAAmB,SAAS,EAAE,IAAI;AAC9C,EAAE;;;;;AAMF,MAAa,iBAAiB,OAC5B,gBACkC;CAClC,MAAM,uBAAmBC,qCAAe,WAAW;CAEnD,IAAI;EAgBF,QAAO,MAfgB,iBAAS,OAA+B;GAC7D,MAAM;GACN,MAAM;GACN,SAAS;GACT,UAAU;GACV,SAAS,mBACLF,+BAAU,QAAQ,gBAAgB,IAClC;GACJ,SAAS,iBAAiB,KAAK,YAAY;IACzC,MAAM,OAAO;IACb,SAAS,OAAO;IAChB,MAAM,OAAO;GACf,EAAE;EACJ,CAAC,EAEc,CAAC;CAClB,QAAQ;EACN;CACF;AACF;;AAWA,MAAa,iBAAiB,UAC5BG,sCAAY,OAAOH,gCAAW,YAAY;;AAG5C,MAAa,eAAe,WAC1B,OACG,SAAS,UAAU,MAAM,MAAM,GAAG,CAAC,CAAC,CACpC,QAAQ,UAAU,MAAM,KAAK,MAAM,EAAE,CAAC,CACtC,KAAK,UAAUG,sCAAY,OAAOC,6BAAQ,UAAU,CAAC;;;;;AAM1D,MAAa,+BAA+B,OAC1C,aACA,UACA,YACkC;CAClC,MAAM,mBAAmB,gBAAYF,qCAAe,WAAW;CAE/D,IAAI,CAAC,kBAAkB;EAGrB,OAFgBG,gCAAY,EAE3B,CAAC,IAAI,MACJ,gDAAgD,QAAQ,qCAAqCL,+BAAU,KAAK,IAAI,GAClH;EACA,QAAQ,WAAW;CACrB;CAEA,OAAO;AACT;;;;;;;;AASA,MAAa,aAAa,OACxB,aACA,UAA6B,CAAC,MACI;CAClC,MAAM,IAAI,MAAMK,gCAAY;CAE5B,MAAM,OAAOC,gCAAmB,WAAW;CAC3C,MAAM,gBAAgBC,0DAAsB;CAE5C,EAAE,MAAM,8BAA8B;CAEtC,MAAM,oBAAgBC,uCAAiBC,oCAAuB,IAAI,CAAC;CAEnE,MAAM,iBACJ,QAAQ,WACP,gBACG,MAAM,EAAE,YAAY;EAClB,SAAS;EACT,eAAe;EACf,SAASL,4BAAO,KAAK,WAAW;GAC9B,OAAO;GACP,OAAO;GACP,MAAMM,qCAAgB;EACxB,EAAE;EACF,UAAU;CACZ,CAAC,IACD;CAEN,IAAI,EAAE,SAAS,cAAc,KAAK,eAAe,WAAW,GAAG;EAC7D,EAAE,OAAO,0CAA0C;EACnD;CACF;CAEA,MAAM,WAAW,gBACZ,QAAQ,YAAa,MAAM,eAAe,IAAI,IAC/C,MAAM,6BACJ,MACA,QAAQ,UACR,sBACF;CAEJ,IAAI,CAAC,UAAU;EACb,EAAE,OAAO,4CAA4C;EACrD;CACF;CAEA,MAAM,IAAI,EAAE,QAAQ;CACpB,EAAE,MAAM,sBAAsB;CAE9B,IAAI;EACF,MAAM,SAAS,UAAMC,oCAAc,MAAM,UAAU,cAAc;EAEjE,EAAE,KAAK,+BAA+B;EAEtC,EAAE,KAAK,QAAQ,SAAS;CAC1B,SAAS,OAAO;EACd,EAAE,KAAK,0BAA0B;EACjC,EAAE,IAAI,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EAClE,QAAQ,WAAW;CACrB;CAEA,EAAE,MAAM,yCAAyC;CAEjD,OAAO;AACT"}
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- let node_path = require("node:path");
3
2
  let _intlayer_engine_cli = require("@intlayer/engine/cli");
3
+ let node_path = require("node:path");
4
4
  let _intlayer_config_logger = require("@intlayer/config/logger");
5
5
  let _intlayer_config_node = require("@intlayer/config/node");
6
6
  let _intlayer_engine_utils = require("@intlayer/engine/utils");
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- let node_path = require("node:path");
3
2
  let _intlayer_engine_cli = require("@intlayer/engine/cli");
3
+ let node_path = require("node:path");
4
4
 
5
5
  //#region src/listProjects.ts
6
6
  const listProjectsCommand = async (options) => {
package/dist/cjs/pull.cjs CHANGED
@@ -3,9 +3,9 @@ const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
3
3
  const require_utils_checkAccess = require('./utils/checkAccess.cjs');
4
4
  const require_push_pullLog = require('./push/pullLog.cjs');
5
5
  const require_utils_selectCmsEnvironment = require('./utils/selectCmsEnvironment.cjs');
6
- let node_path = require("node:path");
7
6
  let _intlayer_engine_cli = require("@intlayer/engine/cli");
8
7
  let node_fs = require("node:fs");
8
+ let node_path = require("node:path");
9
9
  let _intlayer_config_colors = require("@intlayer/config/colors");
10
10
  _intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
11
11
  let _intlayer_config_logger = require("@intlayer/config/logger");
@@ -4,8 +4,8 @@ const require_loadPrompts = require('../loadPrompts.cjs');
4
4
  const require_utils_checkAccess = require('../utils/checkAccess.cjs');
5
5
  const require_utils_selectCmsEnvironment = require('../utils/selectCmsEnvironment.cjs');
6
6
  const require_pushLog = require('../pushLog.cjs');
7
- let node_path = require("node:path");
8
7
  let _intlayer_engine_cli = require("@intlayer/engine/cli");
8
+ let node_path = require("node:path");
9
9
  let _intlayer_config_colors = require("@intlayer/config/colors");
10
10
  _intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
11
11
  let _intlayer_config_logger = require("@intlayer/config/logger");
@@ -8,9 +8,9 @@ const require_reviewDoc_reviewDocBlockAware = require('./reviewDocBlockAware.cjs
8
8
  const require_reviewDoc_reviewDocLog = require('./reviewDocLog.cjs');
9
9
  const require_reviewDoc_reviewDocProgress = require('./reviewDocProgress.cjs');
10
10
  const require_reviewDoc_reviewDocSynthesis = require('./reviewDocSynthesis.cjs');
11
- let node_path = require("node:path");
12
11
  let _intlayer_engine_cli = require("@intlayer/engine/cli");
13
12
  let node_fs = require("node:fs");
13
+ let node_path = require("node:path");
14
14
  let _intlayer_config_colors = require("@intlayer/config/colors");
15
15
  _intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
16
16
  let _intlayer_config_logger = require("@intlayer/config/logger");
@@ -4,8 +4,8 @@ const require__utils_asset = require('../_virtual/_utils_asset.cjs');
4
4
  const require_translateDoc_validation = require('../translateDoc/validation.cjs');
5
5
  const require_utils_chunkInference = require('../utils/chunkInference.cjs');
6
6
  const require_utils_fixChunkStartEndChars = require('../utils/fixChunkStartEndChars.cjs');
7
- let node_path = require("node:path");
8
7
  let node_fs = require("node:fs");
8
+ let node_path = require("node:path");
9
9
  let _intlayer_config_colors = require("@intlayer/config/colors");
10
10
  _intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
11
11
  let _intlayer_config_logger = require("@intlayer/config/logger");
package/dist/cjs/scan.cjs CHANGED
@@ -95,7 +95,7 @@ const scan = async (url, options = {}) => {
95
95
  appLogger(`Routing: ${(0, _intlayer_config_logger.colorize)(routingLabels[routing.strategy], _intlayer_config_colors.BLUE)} ${(0, _intlayer_config_logger.colorize)(`(${routing.evidence}${routing.defaultLocale ? `, default locale: ${routing.defaultLocale}` : ""})`, _intlayer_config_colors.GREY)}`);
96
96
  if (result.technologies.length > 0) {
97
97
  appLogger("\nStack:");
98
- for (const technology of result.technologies) appLogger(` ${(0, _intlayer_config_logger.colorize)(categoryLabels[technology.category], _intlayer_config_colors.GREY)} ${technology.name}${technology.version ? ` ${technology.version}` : ""} ${(0, _intlayer_config_logger.colorize)(`(${technology.evidence})`, _intlayer_config_colors.GREY)}`);
98
+ for (const technology of result.technologies) appLogger(` ${(0, _intlayer_config_logger.colorize)(categoryLabels[technology.category], _intlayer_config_colors.GREY)} ${technology.name}${technology.version ? ` ${technology.version}` : ""}`);
99
99
  }
100
100
  appLogger("\nChecks:");
101
101
  for (const event of result.events) logCheck(appLogger, event);
@@ -1 +1 @@
1
- {"version":3,"file":"scan.cjs","names":["colorize","ANSIColors","getCheckDetailLines","getConfiguration","getAppLogger","scanWebsite","formatSize"],"sources":["../../src/scan.ts"],"sourcesContent":["import * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport {\n type DetectedTechnology,\n formatSize,\n getCheckDetailLines,\n type ScanEvent,\n type ScanResult,\n scanWebsite,\n} from '@intlayer/engine/scan';\nimport type { ConfigurationOptions } from './cli';\n\n/** Options accepted by the {@link scan} command. */\nexport type ScanCommandOptions = {\n /** Disable the deeper puppeteer-based render scan. */\n deep?: boolean;\n /** Output the raw {@link ScanResult} as JSON instead of formatted text. */\n json?: boolean;\n configOptions?: ConfigurationOptions;\n};\n\n/** Human-readable labels for each scorable check type. */\nconst checkLabels: Record<string, string> = {\n robots_robotsPresent: 'robots.txt present',\n robots_noLocalizedUrlsForgotten: 'robots.txt keeps localized URLs crawlable',\n sitemap_sitemapPresent: 'sitemap present',\n sitemap_noLocalizedUrlsForgotten: 'sitemap lists every locale',\n sitemap_hasAlternates: 'sitemap has alternate links',\n sitemap_hasXDefault: 'sitemap has x-default',\n url_htmlLang: 'html lang attribute',\n url_htmlDir: 'html dir attribute',\n url_currentLocale: 'locale signals consistent (lang, URL, hreflang)',\n url_ogLocale: 'og:locale meta tag',\n url_hasCanonical: 'canonical link',\n url_hreflang: 'hreflang tags',\n url_hreflangReciprocal: 'hreflang alternates link back',\n url_hasXDefault: 'x-default hreflang',\n url_hasLocalizedLinks: 'localized internal links',\n url_allAnchorsLocalized: 'all internal links keep the locale',\n url_hasLangSelector: 'crawlable language switcher',\n url_unusedBundleContent: 'unused bundle locale content',\n};\n\n/** Human-readable labels of the routing strategies. */\nconst routingLabels: Record<ScanResult['routing']['strategy'], string> = {\n 'prefix-all': 'locale prefix on every URL (/en/…, /fr/…)',\n 'prefix-no-default': 'locale prefix except for the default locale',\n 'search-params': 'locale in a query parameter',\n subdomain: 'locale subdomain (fr.example.com)',\n domain: 'one domain per locale',\n 'no-prefix': 'no locale in the URL (cookie / header based)',\n unknown: 'unknown',\n};\n\n/** Human-readable labels of the technology categories. */\nconst categoryLabels: Record<DetectedTechnology['category'], string> = {\n framework: 'Framework',\n 'i18n-library': 'i18n library',\n tms: 'TMS',\n 'translation-proxy': 'Translation proxy',\n cms: 'CMS',\n};\n\n/** Maximum number of detail lines printed under a failing check. */\nconst MAX_DETAIL_LINES = 3;\n\nconst statusIcon = (status: ScanEvent['status']): string => {\n if (status === 'success') return colorize('✓', ANSIColors.GREEN);\n if (status === 'warning') return colorize('⚠', ANSIColors.YELLOW);\n return colorize('✗', ANSIColors.RED);\n};\n\nconst scoreColor = (score: number) => {\n if (score >= 80) return ANSIColors.GREEN;\n if (score >= 50) return ANSIColors.YELLOW;\n return ANSIColors.RED;\n};\n\n/** Print a check with, when it fails, a short explanation. */\nconst logCheck = (\n appLogger: ReturnType<typeof getAppLogger>,\n event: ScanEvent\n): void => {\n const type = event.type.split('\\\\')[0];\n if (!type) return;\n\n appLogger(` ${statusIcon(event.status)} ${checkLabels[type] ?? type}`);\n\n if (event.status === 'success') return;\n\n const detailLines = getCheckDetailLines(event.details?.[event.status]);\n detailLines.slice(0, MAX_DETAIL_LINES).forEach((line, index) => {\n appLogger(\n colorize(`${index === 0 ? ' ' : ' '}${line}`, ANSIColors.GREY)\n );\n });\n if (detailLines.length > MAX_DETAIL_LINES) {\n appLogger(\n colorize(\n ` … ${detailLines.length - MAX_DETAIL_LINES} more (use --json)`,\n ANSIColors.GREY\n )\n );\n }\n};\n\n/**\n * Scan a website for i18n/SEO health and bundle weight, printing a formatted\n * report (or JSON with `--json`).\n *\n * @example\n * ```sh\n * npx intlayer scan https://intlayer.org\n * ```\n */\nexport const scan = async (\n url: string,\n options: ScanCommandOptions = {}\n): Promise<void> => {\n const configuration = getConfiguration(options.configOptions);\n const appLogger = getAppLogger(configuration);\n\n const result = await scanWebsite(url, { deep: options.deep });\n\n if (options.json) {\n appLogger(JSON.stringify(result, null, 2));\n return;\n }\n\n appLogger(\n `\\n🔍 Scanned ${colorize(result.url, ANSIColors.GREY_LIGHT)} ${colorize(\n `(${result.mode} mode)`,\n ANSIColors.GREY\n )}\\n`\n );\n\n appLogger(\n `Score: ${colorize(`${result.score}/100`, scoreColor(result.score))}`\n );\n appLogger(\n `Page size: ${colorize(formatSize(result.totalPageSize), ANSIColors.BLUE)} ${colorize(\n `(HTML ${formatSize(result.htmlSize)})`,\n ANSIColors.GREY\n )}`\n );\n if (result.locales.length > 0) {\n appLogger(\n `Locales: ${colorize(result.locales.join(', '), ANSIColors.GREEN)}`\n );\n }\n\n const { routing } = result;\n appLogger(\n `Routing: ${colorize(routingLabels[routing.strategy], ANSIColors.BLUE)} ${colorize(\n `(${routing.evidence}${routing.defaultLocale ? `, default locale: ${routing.defaultLocale}` : ''})`,\n ANSIColors.GREY\n )}`\n );\n\n if (result.technologies.length > 0) {\n appLogger('\\nStack:');\n for (const technology of result.technologies) {\n appLogger(\n ` ${colorize(categoryLabels[technology.category], ANSIColors.GREY)} ${technology.name}${\n technology.version ? ` ${technology.version}` : ''\n } ${colorize(`(${technology.evidence})`, ANSIColors.GREY)}`\n );\n }\n }\n\n appLogger('\\nChecks:');\n for (const event of result.events) {\n logCheck(appLogger, event);\n }\n\n if (result.bundle && result.bundle.totalLocaleSize > 0) {\n const { bundle } = result;\n appLogger('\\nBundle locale weight:');\n appLogger(\n ` Translations shipped: ${colorize(formatSize(bundle.totalLocaleSize), ANSIColors.BLUE)}`\n );\n appLogger(\n ` Unused (other locales): ${colorize(\n `${formatSize(bundle.totalUnusedLocaleSize)} (${bundle.unusedPercentOfLocale}%)`,\n bundle.unusedPercentOfLocale > 30\n ? ANSIColors.RED\n : bundle.unusedPercentOfLocale > 0\n ? ANSIColors.YELLOW\n : ANSIColors.GREEN\n )}`\n );\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"scan.cjs","names":["colorize","ANSIColors","getCheckDetailLines","getConfiguration","getAppLogger","scanWebsite","formatSize"],"sources":["../../src/scan.ts"],"sourcesContent":["import * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport {\n type DetectedTechnology,\n formatSize,\n getCheckDetailLines,\n type ScanEvent,\n type ScanResult,\n scanWebsite,\n} from '@intlayer/engine/scan';\nimport type { ConfigurationOptions } from './cli';\n\n/** Options accepted by the {@link scan} command. */\nexport type ScanCommandOptions = {\n /** Disable the deeper puppeteer-based render scan. */\n deep?: boolean;\n /** Output the raw {@link ScanResult} as JSON instead of formatted text. */\n json?: boolean;\n configOptions?: ConfigurationOptions;\n};\n\n/** Human-readable labels for each scorable check type. */\nconst checkLabels: Record<string, string> = {\n robots_robotsPresent: 'robots.txt present',\n robots_noLocalizedUrlsForgotten: 'robots.txt keeps localized URLs crawlable',\n sitemap_sitemapPresent: 'sitemap present',\n sitemap_noLocalizedUrlsForgotten: 'sitemap lists every locale',\n sitemap_hasAlternates: 'sitemap has alternate links',\n sitemap_hasXDefault: 'sitemap has x-default',\n url_htmlLang: 'html lang attribute',\n url_htmlDir: 'html dir attribute',\n url_currentLocale: 'locale signals consistent (lang, URL, hreflang)',\n url_ogLocale: 'og:locale meta tag',\n url_hasCanonical: 'canonical link',\n url_hreflang: 'hreflang tags',\n url_hreflangReciprocal: 'hreflang alternates link back',\n url_hasXDefault: 'x-default hreflang',\n url_hasLocalizedLinks: 'localized internal links',\n url_allAnchorsLocalized: 'all internal links keep the locale',\n url_hasLangSelector: 'crawlable language switcher',\n url_unusedBundleContent: 'unused bundle locale content',\n};\n\n/** Human-readable labels of the routing strategies. */\nconst routingLabels: Record<ScanResult['routing']['strategy'], string> = {\n 'prefix-all': 'locale prefix on every URL (/en/…, /fr/…)',\n 'prefix-no-default': 'locale prefix except for the default locale',\n 'search-params': 'locale in a query parameter',\n subdomain: 'locale subdomain (fr.example.com)',\n domain: 'one domain per locale',\n 'no-prefix': 'no locale in the URL (cookie / header based)',\n unknown: 'unknown',\n};\n\n/** Human-readable labels of the technology categories. */\nconst categoryLabels: Record<DetectedTechnology['category'], string> = {\n framework: 'Framework',\n 'i18n-library': 'i18n library',\n tms: 'TMS',\n 'translation-proxy': 'Translation proxy',\n cms: 'CMS',\n};\n\n/** Maximum number of detail lines printed under a failing check. */\nconst MAX_DETAIL_LINES = 3;\n\nconst statusIcon = (status: ScanEvent['status']): string => {\n if (status === 'success') return colorize('✓', ANSIColors.GREEN);\n if (status === 'warning') return colorize('⚠', ANSIColors.YELLOW);\n return colorize('✗', ANSIColors.RED);\n};\n\nconst scoreColor = (score: number) => {\n if (score >= 80) return ANSIColors.GREEN;\n if (score >= 50) return ANSIColors.YELLOW;\n return ANSIColors.RED;\n};\n\n/** Print a check with, when it fails, a short explanation. */\nconst logCheck = (\n appLogger: ReturnType<typeof getAppLogger>,\n event: ScanEvent\n): void => {\n const type = event.type.split('\\\\')[0];\n if (!type) return;\n\n appLogger(` ${statusIcon(event.status)} ${checkLabels[type] ?? type}`);\n\n if (event.status === 'success') return;\n\n const detailLines = getCheckDetailLines(event.details?.[event.status]);\n detailLines.slice(0, MAX_DETAIL_LINES).forEach((line, index) => {\n appLogger(\n colorize(`${index === 0 ? ' ' : ' '}${line}`, ANSIColors.GREY)\n );\n });\n if (detailLines.length > MAX_DETAIL_LINES) {\n appLogger(\n colorize(\n ` … ${detailLines.length - MAX_DETAIL_LINES} more (use --json)`,\n ANSIColors.GREY\n )\n );\n }\n};\n\n/**\n * Scan a website for i18n/SEO health and bundle weight, printing a formatted\n * report (or JSON with `--json`).\n *\n * @example\n * ```sh\n * npx intlayer scan https://intlayer.org\n * ```\n */\nexport const scan = async (\n url: string,\n options: ScanCommandOptions = {}\n): Promise<void> => {\n const configuration = getConfiguration(options.configOptions);\n const appLogger = getAppLogger(configuration);\n\n const result = await scanWebsite(url, { deep: options.deep });\n\n if (options.json) {\n appLogger(JSON.stringify(result, null, 2));\n return;\n }\n\n appLogger(\n `\\n🔍 Scanned ${colorize(result.url, ANSIColors.GREY_LIGHT)} ${colorize(\n `(${result.mode} mode)`,\n ANSIColors.GREY\n )}\\n`\n );\n\n appLogger(\n `Score: ${colorize(`${result.score}/100`, scoreColor(result.score))}`\n );\n appLogger(\n `Page size: ${colorize(formatSize(result.totalPageSize), ANSIColors.BLUE)} ${colorize(\n `(HTML ${formatSize(result.htmlSize)})`,\n ANSIColors.GREY\n )}`\n );\n if (result.locales.length > 0) {\n appLogger(\n `Locales: ${colorize(result.locales.join(', '), ANSIColors.GREEN)}`\n );\n }\n\n const { routing } = result;\n appLogger(\n `Routing: ${colorize(routingLabels[routing.strategy], ANSIColors.BLUE)} ${colorize(\n `(${routing.evidence}${routing.defaultLocale ? `, default locale: ${routing.defaultLocale}` : ''})`,\n ANSIColors.GREY\n )}`\n );\n\n if (result.technologies.length > 0) {\n appLogger('\\nStack:');\n for (const technology of result.technologies) {\n appLogger(\n ` ${colorize(categoryLabels[technology.category], ANSIColors.GREY)} ${technology.name}${\n technology.version ? ` ${technology.version}` : ''\n }`\n );\n }\n }\n\n appLogger('\\nChecks:');\n for (const event of result.events) {\n logCheck(appLogger, event);\n }\n\n if (result.bundle && result.bundle.totalLocaleSize > 0) {\n const { bundle } = result;\n appLogger('\\nBundle locale weight:');\n appLogger(\n ` Translations shipped: ${colorize(formatSize(bundle.totalLocaleSize), ANSIColors.BLUE)}`\n );\n appLogger(\n ` Unused (other locales): ${colorize(\n `${formatSize(bundle.totalUnusedLocaleSize)} (${bundle.unusedPercentOfLocale}%)`,\n bundle.unusedPercentOfLocale > 30\n ? ANSIColors.RED\n : bundle.unusedPercentOfLocale > 0\n ? ANSIColors.YELLOW\n : ANSIColors.GREEN\n )}`\n );\n }\n};\n"],"mappings":"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"}
@@ -4,9 +4,9 @@ const require_utils_setupAI = require('../utils/setupAI.cjs');
4
4
  const require_utils_getOutputFilePath = require('../utils/getOutputFilePath.cjs');
5
5
  const require_utils_checkFileModifiedRange = require('../utils/checkFileModifiedRange.cjs');
6
6
  const require_translateDoc_translateFile = require('./translateFile.cjs');
7
- let node_path = require("node:path");
8
7
  let _intlayer_engine_cli = require("@intlayer/engine/cli");
9
8
  let node_fs = require("node:fs");
9
+ let node_path = require("node:path");
10
10
  let _intlayer_config_colors = require("@intlayer/config/colors");
11
11
  _intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
12
12
  let _intlayer_config_logger = require("@intlayer/config/logger");
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_init = require('./init.cjs');
3
- let node_path = require("node:path");
4
3
  let _intlayer_engine_cli = require("@intlayer/engine/cli");
4
+ let node_path = require("node:path");
5
5
 
6
6
  //#region src/upgrade.ts
7
7
  /**
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- let node_path = require("node:path");
3
2
  let node_fs = require("node:fs");
3
+ let node_path = require("node:path");
4
4
 
5
5
  //#region src/utils/getParentPackageJSON.ts
6
6
  const getParentPackageJSON = (startDir) => {
@@ -0,0 +1,12 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region src/utils/isInteractiveTerminal.ts
3
+ /**
4
+ * Whether the process can prompt: both stdin and stdout are attached to a
5
+ * terminal. False for AI agents, CI and piped runs, where a prompt would print
6
+ * and exit without an answer.
7
+ */
8
+ const isInteractiveTerminal = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
9
+
10
+ //#endregion
11
+ exports.isInteractiveTerminal = isInteractiveTerminal;
12
+ //# sourceMappingURL=isInteractiveTerminal.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isInteractiveTerminal.cjs","names":[],"sources":["../../../src/utils/isInteractiveTerminal.ts"],"sourcesContent":["/**\n * Whether the process can prompt: both stdin and stdout are attached to a\n * terminal. False for AI agents, CI and piped runs, where a prompt would print\n * and exit without an answer.\n */\nexport const isInteractiveTerminal = (): boolean =>\n Boolean(process.stdin.isTTY && process.stdout.isTTY);\n"],"mappings":";;;;;;;AAKA,MAAa,8BACX,QAAQ,QAAQ,MAAM,SAAS,QAAQ,OAAO,KAAK"}
@@ -0,0 +1,21 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region src/utils/parseChoice.ts
3
+ /**
4
+ * Resolves a command-line value against its allowed choices, ignoring case.
5
+ *
6
+ * @param value - Raw value passed on the command line.
7
+ * @param choices - Accepted values, in their canonical spelling.
8
+ * @param optionName - Option name used in the error message (e.g. `--platform`).
9
+ * @returns The matching choice, in its canonical spelling.
10
+ * @throws When the value matches none of the choices.
11
+ */
12
+ const parseChoice = (value, choices, optionName) => {
13
+ const normalizedValue = value.trim().toLowerCase();
14
+ const choice = choices.find((candidate) => candidate.toLowerCase() === normalizedValue);
15
+ if (!choice) throw new Error(`Invalid ${optionName} value "${value}". Expected one of: ${choices.join(", ")}.`);
16
+ return choice;
17
+ };
18
+
19
+ //#endregion
20
+ exports.parseChoice = parseChoice;
21
+ //# sourceMappingURL=parseChoice.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseChoice.cjs","names":[],"sources":["../../../src/utils/parseChoice.ts"],"sourcesContent":["/**\n * Resolves a command-line value against its allowed choices, ignoring case.\n *\n * @param value - Raw value passed on the command line.\n * @param choices - Accepted values, in their canonical spelling.\n * @param optionName - Option name used in the error message (e.g. `--platform`).\n * @returns The matching choice, in its canonical spelling.\n * @throws When the value matches none of the choices.\n */\nexport const parseChoice = <Choice extends string>(\n value: string,\n choices: readonly Choice[],\n optionName: string\n): Choice => {\n const normalizedValue = value.trim().toLowerCase();\n const choice = choices.find(\n (candidate) => candidate.toLowerCase() === normalizedValue\n );\n\n if (!choice) {\n throw new Error(\n `Invalid ${optionName} value \"${value}\". Expected one of: ${choices.join(', ')}.`\n );\n }\n\n return choice;\n};\n"],"mappings":";;;;;;;;;;;AASA,MAAa,eACX,OACA,SACA,eACW;CACX,MAAM,kBAAkB,MAAM,KAAK,CAAC,CAAC,YAAY;CACjD,MAAM,SAAS,QAAQ,MACpB,cAAc,UAAU,YAAY,MAAM,eAC7C;CAEA,IAAI,CAAC,QACH,MAAM,IAAI,MACR,WAAW,WAAW,UAAU,MAAM,sBAAsB,QAAQ,KAAK,IAAI,EAAE,EACjF;CAGF,OAAO;AACT"}
@@ -1,5 +1,5 @@
1
- import { basename, dirname, join, relative, resolve, sep } from "node:path";
2
1
  import { existsSync, readFileSync } from "node:fs";
2
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
 
5
5
  //#region \0utils:asset
@@ -1,7 +1,7 @@
1
- import { logConfigDetails } from "@intlayer/engine/cli";
2
1
  import { getConfiguration } from "@intlayer/config/node";
3
2
  import { claimCliContentWatcher, reportRedundantContentWatcher, runParallel } from "@intlayer/engine/utils";
4
3
  import { buildAndWatchIntlayer } from "@intlayer/engine/watcher";
4
+ import { logConfigDetails } from "@intlayer/engine/logConfigDetails";
5
5
 
6
6
  //#region src/build.ts
7
7
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"build.mjs","names":[],"sources":["../../src/build.ts"],"sourcesContent":["import {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { logConfigDetails } from '@intlayer/engine/cli';\nimport {\n claimCliContentWatcher,\n type ParallelHandle,\n reportRedundantContentWatcher,\n runParallel,\n} from '@intlayer/engine/utils';\nimport { buildAndWatchIntlayer } from '@intlayer/engine/watcher';\n\ntype BuildOptions = {\n watch?: boolean;\n skipPrepare?: boolean;\n with?: string | string[];\n configOptions?: GetConfigurationOptions;\n};\n\n/**\n * Get locales dictionaries .content.{json|ts|tsx|js|jsx|mjs|cjs} and build the JSON dictionaries in the .intlayer directory.\n * Watch mode available to get the change in the .content.{json|ts|tsx|js|jsx|mjs|cjs}\n */\nexport const build = async (options?: BuildOptions) => {\n const config = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n let parallelProcess: ParallelHandle | null = null;\n\n // Only watch mode keeps a watcher alive; a one-off build has nothing to\n // deduplicate. Claimed before the child is spawned so the bundler plugin\n // inside it stands down instead of racing this command.\n if (options?.watch) {\n const bundlerAlreadyWatching = await claimCliContentWatcher(\n config,\n 'intlayer build --watch'\n );\n\n if (bundlerAlreadyWatching) {\n reportRedundantContentWatcher(config, {\n cliLabel: 'intlayer build --watch',\n bundlerLabel: bundlerAlreadyWatching.label,\n });\n }\n }\n\n if (options?.with) {\n parallelProcess = runParallel(options.with);\n // Handle the promise to avoid unhandled rejection\n parallelProcess.result.catch(() => {\n // Parallel process failed or was terminated\n });\n }\n\n await buildAndWatchIntlayer({\n persistent: options?.watch ?? false,\n skipPrepare: options?.skipPrepare ?? false,\n configuration: config,\n });\n\n if (!options?.watch && parallelProcess) {\n parallelProcess.kill();\n }\n};\n"],"mappings":";;;;;;;;;;AAwBA,MAAa,QAAQ,OAAO,YAA2B;CACrD,MAAM,SAAS,iBAAiB,SAAS,aAAa;CACtD,iBAAiB,SAAS,aAAa;CAEvC,IAAI,kBAAyC;CAK7C,IAAI,SAAS,OAAO;EAClB,MAAM,yBAAyB,MAAM,uBACnC,QACA,wBACF;EAEA,IAAI,wBACF,8BAA8B,QAAQ;GACpC,UAAU;GACV,cAAc,uBAAuB;EACvC,CAAC;CAEL;CAEA,IAAI,SAAS,MAAM;EACjB,kBAAkB,YAAY,QAAQ,IAAI;EAE1C,gBAAgB,OAAO,YAAY,CAEnC,CAAC;CACH;CAEA,MAAM,sBAAsB;EAC1B,YAAY,SAAS,SAAS;EAC9B,aAAa,SAAS,eAAe;EACrC,eAAe;CACjB,CAAC;CAED,IAAI,CAAC,SAAS,SAAS,iBACrB,gBAAgB,KAAK;AAEzB"}
1
+ {"version":3,"file":"build.mjs","names":[],"sources":["../../src/build.ts"],"sourcesContent":["import {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { logConfigDetails } from '@intlayer/engine/logConfigDetails';\nimport {\n claimCliContentWatcher,\n type ParallelHandle,\n reportRedundantContentWatcher,\n runParallel,\n} from '@intlayer/engine/utils';\nimport { buildAndWatchIntlayer } from '@intlayer/engine/watcher';\n\ntype BuildOptions = {\n watch?: boolean;\n skipPrepare?: boolean;\n with?: string | string[];\n configOptions?: GetConfigurationOptions;\n};\n\n/**\n * Get locales dictionaries .content.{json|ts|tsx|js|jsx|mjs|cjs} and build the JSON dictionaries in the .intlayer directory.\n * Watch mode available to get the change in the .content.{json|ts|tsx|js|jsx|mjs|cjs}\n */\nexport const build = async (options?: BuildOptions) => {\n const config = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n let parallelProcess: ParallelHandle | null = null;\n\n // Only watch mode keeps a watcher alive; a one-off build has nothing to\n // deduplicate. Claimed before the child is spawned so the bundler plugin\n // inside it stands down instead of racing this command.\n if (options?.watch) {\n const bundlerAlreadyWatching = await claimCliContentWatcher(\n config,\n 'intlayer build --watch'\n );\n\n if (bundlerAlreadyWatching) {\n reportRedundantContentWatcher(config, {\n cliLabel: 'intlayer build --watch',\n bundlerLabel: bundlerAlreadyWatching.label,\n });\n }\n }\n\n if (options?.with) {\n parallelProcess = runParallel(options.with);\n // Handle the promise to avoid unhandled rejection\n parallelProcess.result.catch(() => {\n // Parallel process failed or was terminated\n });\n }\n\n await buildAndWatchIntlayer({\n persistent: options?.watch ?? false,\n skipPrepare: options?.skipPrepare ?? false,\n configuration: config,\n });\n\n if (!options?.watch && parallelProcess) {\n parallelProcess.kill();\n }\n};\n"],"mappings":";;;;;;;;;;AAwBA,MAAa,QAAQ,OAAO,YAA2B;CACrD,MAAM,SAAS,iBAAiB,SAAS,aAAa;CACtD,iBAAiB,SAAS,aAAa;CAEvC,IAAI,kBAAyC;CAK7C,IAAI,SAAS,OAAO;EAClB,MAAM,yBAAyB,MAAM,uBACnC,QACA,wBACF;EAEA,IAAI,wBACF,8BAA8B,QAAQ;GACpC,UAAU;GACV,cAAc,uBAAuB;EACvC,CAAC;CAEL;CAEA,IAAI,SAAS,MAAM;EACjB,kBAAkB,YAAY,QAAQ,IAAI;EAE1C,gBAAgB,OAAO,YAAY,CAEnC,CAAC;CACH;CAEA,MAAM,sBAAsB;EAC1B,YAAY,SAAS,SAAS;EAC9B,aAAa,SAAS,eAAe;EACrC,eAAe;CACjB,CAAC;CAED,IAAI,CAAC,SAAS,SAAS,iBACrB,gBAAgB,KAAK;AAEzB"}
package/dist/esm/ci.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { resolve, sep } from "node:path";
2
1
  import { detectPackageManager, listProjects } from "@intlayer/engine/cli";
2
+ import { resolve, sep } from "node:path";
3
3
  import { logger } from "@intlayer/config/logger";
4
4
  import { spawnSync } from "node:child_process";
5
5
 
package/dist/esm/cli.mjs CHANGED
@@ -153,7 +153,7 @@ const setAPI = () => {
153
153
  /**
154
154
  * INIT
155
155
  */
156
- const initCmd = program.command("init").description("Initialize Intlayer in the project").option("--project-root [projectRoot]", "Project root directory").option("--no-gitignore", "Do not add .intlayer to .gitignore").option("--no-github-actions", "Do not scaffold the fill and test GitHub Actions workflows").option("--no-framework-setup", "Do not set up the project (Intlayer config, tsconfig, bundler plugin, middleware/proxy, providers in layout/page)").option("-i, --interactive", "Interactively choose what to set up (packages, skills, MCP, VS Code extension, LSP, …)").option("--routing <routing>", "Locale routing: prefix-no-default | prefix-all | no-prefix | search-params | none").action(async (options) => {
156
+ const initCmd = program.command("init").enablePositionalOptions().description("Install the Intlayer packages and set up the framework. Use --interactive, or the init subcommands, for the other setup steps (CI, skills, MCP, VS Code, LSP, lint, CMS, …)").option("--project-root [projectRoot]", "Project root directory").option("--no-gitignore", "Do not add .intlayer to .gitignore").option("--no-github-actions", "With --interactive: do not scaffold the GitHub Actions workflows, even if selected").option("--no-framework-setup", "Do not set up the project (Intlayer config, tsconfig, bundler plugin, middleware/proxy, providers in layout/page)").option("-i, --interactive", "Interactively choose what to set up (packages, skills, MCP, VS Code extension, LSP, …). Needs a terminal").option("--routing <routing>", "Locale routing: prefix-no-default | prefix-all | no-prefix | search-params | none").action(async (options) => {
157
157
  const { init, parseLocaleRoutingChoice, getRoutingInitOptions } = await import("./init.mjs");
158
158
  return init(options.projectRoot, {
159
159
  noGitignore: options.gitignore === false,
@@ -163,21 +163,59 @@ const setAPI = () => {
163
163
  ...options.routing ? getRoutingInitOptions(parseLocaleRoutingChoice(options.routing)) : {}
164
164
  }, options.interactive === true);
165
165
  });
166
- initCmd.command("skills").description("Initialize Intlayer skills in the project").option("--project-root [projectRoot]", "Project root directory").action(async (options) => {
167
- const { initSkills } = await import("./initSkills.mjs");
168
- await initSkills(options.projectRoot);
166
+ initCmd.command("packages").description("Install the missing Intlayer packages and upgrade the outdated ones").option("--project-root [projectRoot]", "Project root directory").action(async (options) => {
167
+ const { initSteps } = await import("./init.mjs");
168
+ return initSteps(options.projectRoot, ["packages"], { upgradeToVersion: packageJson.version });
169
169
  });
170
- initCmd.command("mcp").description("Initialize Intlayer MCP server in the project").option("--project-root [projectRoot]", "Project root directory").action(async (options) => {
171
- const { initMCP } = await import("./initMCP.mjs");
172
- return initMCP(options.projectRoot);
170
+ initCmd.command("project").alias("framework").description("Set up the project: Intlayer config, tsconfig, bundler plugin, middleware/proxy, providers in layout/page and .gitignore").option("--project-root [projectRoot]", "Project root directory").option("--no-gitignore", "Do not add .intlayer to .gitignore").option("--routing <routing>", "Locale routing: prefix-no-default | prefix-all | no-prefix | search-params | none").action(async (options) => {
171
+ const { initSteps, parseLocaleRoutingChoice, getRoutingInitOptions } = await import("./init.mjs");
172
+ return initSteps(options.projectRoot, ["projectSetup"], {
173
+ noGitignore: options.gitignore === false,
174
+ ...options.routing ? getRoutingInitOptions(parseLocaleRoutingChoice(options.routing)) : {}
175
+ });
176
+ });
177
+ initCmd.command("github-actions").alias("ci").description("Scaffold the fill and test GitHub Actions workflows that run on every pull request").option("--project-root [projectRoot]", "Project root directory").action(async (options) => {
178
+ const { initSteps } = await import("./init.mjs");
179
+ return initSteps(options.projectRoot, ["githubActions"]);
180
+ });
181
+ initCmd.command("vscode-extension").alias("vscode").description("Recommend the Intlayer VS Code extension in .vscode/extensions.json").option("--project-root [projectRoot]", "Project root directory").action(async (options) => {
182
+ const { initSteps } = await import("./init.mjs");
183
+ return initSteps(options.projectRoot, ["vscodeExtension"]);
184
+ });
185
+ initCmd.command("lsp").description("Configure the Intlayer language server in .vscode/settings.json").option("--project-root [projectRoot]", "Project root directory").action(async (options) => {
186
+ const { initSteps } = await import("./init.mjs");
187
+ return initSteps(options.projectRoot, ["lsp"]);
188
+ });
189
+ initCmd.command("eslint").alias("lint").description("Enable the Intlayer lint rules (ESLint / oxlint), when the project already lints").option("--project-root [projectRoot]", "Project root directory").action(async (options) => {
190
+ const { initSteps } = await import("./init.mjs");
191
+ return initSteps(options.projectRoot, ["eslint"], { upgradeToVersion: packageJson.version });
192
+ });
193
+ initCmd.command("skills").description("Initialize Intlayer skills in the project").option("--project-root [projectRoot]", "Project root directory").option("-p, --platform <platform>", "AI platform (e.g. Claude, Cursor, VSCode). Detected when omitted").option("-s, --skills <skills...>", "Skills to install (e.g. Usage Content React). Defaults to the project stack without a terminal").action(async (options) => {
194
+ const { initSkills, parsePlatform, parseSkills } = await import("./initSkills.mjs");
195
+ await initSkills(options.projectRoot, {
196
+ platform: options.platform ? parsePlatform(options.platform) : void 0,
197
+ skills: options.skills ? parseSkills(options.skills) : void 0
198
+ });
199
+ });
200
+ initCmd.command("mcp").description("Initialize Intlayer MCP server in the project").option("--project-root [projectRoot]", "Project root directory").option("-p, --platform <platform>", "AI platform (e.g. Claude, Cursor, VSCode). Detected when omitted").option("-t, --transport <transport>", "stdio (local server, default without a terminal) | sse (hosted)").action(async (options) => {
201
+ const { initMCP, parseMCPTransport } = await import("./initMCP.mjs");
202
+ const { parsePlatform } = await import("./initSkills.mjs");
203
+ return initMCP(options.projectRoot, {
204
+ platform: options.platform ? parsePlatform(options.platform) : void 0,
205
+ transport: options.transport ? parseMCPTransport(options.transport) : void 0
206
+ });
207
+ });
208
+ initCmd.command("cms").description("Log in to the Intlayer CMS through your browser, then store the credentials in your .env").option("--project-root [projectRoot]", "Project root directory").action(async (options) => {
209
+ const { initCms } = await import("./init.mjs");
210
+ return initCms(options.projectRoot);
173
211
  });
174
- initCmd.command("infra").description("Set up the Intlayer infrastructure: desktop app, all-in-one Docker container or Docker Compose stack (runs https://intlayer.org/install.sh, or install.ps1 on Windows)").option("-m, --mode <mode>", "Skip the menu: desktop | docker | compose").action(async (options) => {
212
+ initCmd.command("infra").description("Set up the Intlayer infrastructure: desktop app, all-in-one Docker container or Docker Compose stack (runs https://intlayer.org/install.sh, or install.ps1 on Windows)").option("-m, --mode <mode>", "Skip the menu: desktop | docker | compose (required without a terminal)").action(async (options) => {
175
213
  const { initInfra, parseInfraMode } = await import("./initInfra.mjs");
176
214
  return initInfra({ mode: options.mode ? parseInfraMode(options.mode) : void 0 });
177
215
  });
178
- initCmd.command("extension").alias("chrome-extension").description("Install the Intlayer Chrome extension or Firefox add-on in your browser").action(async () => {
179
- const { initChromeExtension } = await import("./initChromeExtension.mjs");
180
- return initChromeExtension();
216
+ initCmd.command("extension").alias("chrome-extension").description("Install the Intlayer Chrome extension or Firefox add-on in your browser").option("-b, --browser <browsers...>", "Browsers to open the store page for: chrome | firefox. Without a terminal, the links are only printed").action(async (options) => {
217
+ const { initChromeExtension, parseExtensionBrowsers } = await import("./initChromeExtension.mjs");
218
+ return initChromeExtension({ browsers: options.browser ? parseExtensionBrowsers(options.browser) : void 0 });
181
219
  });
182
220
  /**
183
221
  * UPGRADE
@@ -403,7 +441,7 @@ const setAPI = () => {
403
441
  const keys = [...options.keys ?? [], ...options.key ?? []];
404
442
  const excludedKeys = [...options.excludedKeys ?? [], ...options.excludedKey ?? []];
405
443
  const dictionaries = [...options.dictionaries ?? [], ...options.dictionary ?? []];
406
- return fill({
444
+ await fill({
407
445
  ...options,
408
446
  keys: keys.length > 0 ? keys : void 0,
409
447
  excludedKeys: excludedKeys.length > 0 ? excludedKeys : void 0,
@@ -572,6 +610,7 @@ const setAPI = () => {
572
610
  const { runCI } = await import("./ci.mjs");
573
611
  process.exit(await runCI(args));
574
612
  });
613
+ program.enablePositionalOptions();
575
614
  program.parse(process.argv);
576
615
  return program;
577
616
  };