@the-i18n-kit/cli 5.0.0 → 6.0.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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -1
  3. package/dist/{_shared-DFyy3sh8.js → _shared-CkMAydRl.js} +2 -2
  4. package/dist/{_shared-DFyy3sh8.js.map → _shared-CkMAydRl.js.map} +1 -1
  5. package/dist/{add-C-qs8iBa.js → add-BBoVUNEq.js} +4 -4
  6. package/dist/{add-C-qs8iBa.js.map → add-BBoVUNEq.js.map} +1 -1
  7. package/dist/bin.js +1 -6
  8. package/dist/bin.js.map +1 -1
  9. package/dist/{check-BI1orjXb.js → check-IosaN_EM.js} +4 -4
  10. package/dist/{check-BI1orjXb.js.map → check-IosaN_EM.js.map} +1 -1
  11. package/dist/cli-CnZNGUGu.js +70 -0
  12. package/dist/cli-CnZNGUGu.js.map +1 -0
  13. package/dist/config/framework/stubs/{next-intl-routing-Cwaa3f4R.d.ts → next-intl-routing-lExp4Q3r.d.ts} +1 -1
  14. package/dist/config/framework/stubs/{next-intl-routing-Cwaa3f4R.d.ts.map → next-intl-routing-lExp4Q3r.d.ts.map} +1 -1
  15. package/dist/config/framework/stubs/{unplugin-vue-i18n-1ud7-Ly8.d.ts → unplugin-vue-i18n-mH7YdYqA.d.ts} +1 -1
  16. package/dist/config/framework/stubs/{unplugin-vue-i18n-1ud7-Ly8.d.ts.map → unplugin-vue-i18n-mH7YdYqA.d.ts.map} +1 -1
  17. package/dist/{define-config-CLgKN-7N.d.ts → define-config-BeHvSYBP.d.ts} +1 -1
  18. package/dist/{define-config-BaZ1dZph.d.ts → define-config-DW-rgsU6.d.ts} +1 -1
  19. package/dist/{define-config-BaZ1dZph.d.ts.map → define-config-DW-rgsU6.d.ts.map} +1 -1
  20. package/dist/define-config.d.ts +1 -1
  21. package/dist/{detect-7cVfsNJP.js → detect-3jjcQJs1.js} +4 -4
  22. package/dist/{detect-7cVfsNJP.js.map → detect-3jjcQJs1.js.map} +1 -1
  23. package/dist/{empty-DB8Hl_8u.js → empty-D3La0enO.js} +4 -4
  24. package/dist/{empty-DB8Hl_8u.js.map → empty-D3La0enO.js.map} +1 -1
  25. package/dist/{find-duplicates-CmEqBfby.js → find-duplicates-BlQoJDgu.js} +4 -4
  26. package/dist/{find-duplicates-CmEqBfby.js.map → find-duplicates-BlQoJDgu.js.map} +1 -1
  27. package/dist/{get-BE21Iy6N.js → get-Cr5O5zJX.js} +4 -4
  28. package/dist/{get-BE21Iy6N.js.map → get-Cr5O5zJX.js.map} +1 -1
  29. package/dist/{index-DCG3dOdC.d.ts → index-w-EQZEZF.d.ts} +3 -22
  30. package/dist/index-w-EQZEZF.d.ts.map +1 -0
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +3 -4
  33. package/dist/{init-BtzmhsOM.js → init-BRBMkcI0.js} +4 -4
  34. package/dist/{init-BtzmhsOM.js.map → init-BRBMkcI0.js.map} +1 -1
  35. package/dist/{list-dirs-DUQbwvlE.js → list-dirs-BMuByyuX.js} +4 -4
  36. package/dist/{list-dirs-DUQbwvlE.js.map → list-dirs-BMuByyuX.js.map} +1 -1
  37. package/dist/{missing-CcYrgd54.js → missing-CBOk4ZgD.js} +4 -4
  38. package/dist/{missing-CcYrgd54.js.map → missing-CBOk4ZgD.js.map} +1 -1
  39. package/dist/{move-B2px6Ck-.js → move-DOdPKnOV.js} +4 -4
  40. package/dist/{move-B2px6Ck-.js.map → move-DOdPKnOV.js.map} +1 -1
  41. package/dist/{operations-CTo44gPu.js → operations-PfRSTepi.js} +3 -3
  42. package/dist/{operations-CTo44gPu.js.map → operations-PfRSTepi.js.map} +1 -1
  43. package/dist/{providers-CHE2ffi6.js → providers-CAsU_aV1.js} +6 -7
  44. package/dist/providers-CAsU_aV1.js.map +1 -0
  45. package/dist/{remove-DTbqM4bR.js → remove-CVBjJV9h.js} +4 -4
  46. package/dist/{remove-DTbqM4bR.js.map → remove-CVBjJV9h.js.map} +1 -1
  47. package/dist/{remove-orphans-Dfzu7hRz.js → remove-orphans-BaKEISxA.js} +4 -4
  48. package/dist/{remove-orphans-Dfzu7hRz.js.map → remove-orphans-BaKEISxA.js.map} +1 -1
  49. package/dist/{rename-C_rFKkU4.js → rename-CN2ajUeb.js} +4 -4
  50. package/dist/{rename-C_rFKkU4.js.map → rename-CN2ajUeb.js.map} +1 -1
  51. package/dist/{scaffold-CUimlNqR.js → scaffold-CUKUFuEy.js} +4 -4
  52. package/dist/{scaffold-CUimlNqR.js.map → scaffold-CUKUFuEy.js.map} +1 -1
  53. package/dist/{scan-BkOIYhjZ.js → scan-Bhv0Nmy3.js} +4 -4
  54. package/dist/{scan-BkOIYhjZ.js.map → scan-Bhv0Nmy3.js.map} +1 -1
  55. package/dist/{search-0io1BcsY.js → search-CrkLgz3m.js} +4 -4
  56. package/dist/{search-0io1BcsY.js.map → search-CrkLgz3m.js.map} +1 -1
  57. package/dist/{status-Bp-SzjTN.js → status-DprS-qDC.js} +4 -4
  58. package/dist/{status-Bp-SzjTN.js.map → status-DprS-qDC.js.map} +1 -1
  59. package/dist/{translate-cCb2Z57w.js → translate-Cs6EdbzA.js} +4 -4
  60. package/dist/{translate-cCb2Z57w.js.map → translate-Cs6EdbzA.js.map} +1 -1
  61. package/dist/{translate-key-DHLWEmrA.js → translate-key-BBZpYQjL.js} +4 -4
  62. package/dist/{translate-key-DHLWEmrA.js.map → translate-key-BBZpYQjL.js.map} +1 -1
  63. package/dist/{update-DV4ijRVB.js → update-BQuGxSSy.js} +4 -4
  64. package/dist/{update-DV4ijRVB.js.map → update-BQuGxSSy.js.map} +1 -1
  65. package/dist/{write-Czzm_Nb0.js → write-B8z6I5Vf.js} +4 -4
  66. package/dist/{write-Czzm_Nb0.js.map → write-B8z6I5Vf.js.map} +1 -1
  67. package/package.json +8 -8
  68. package/dist/cli-DsFuVJNP.js +0 -74
  69. package/dist/cli-DsFuVJNP.js.map +0 -1
  70. package/dist/index-DCG3dOdC.d.ts.map +0 -1
  71. package/dist/providers-CHE2ffi6.js.map +0 -1
  72. package/dist/rename-notice-BV8HNX3O.js +0 -25
  73. package/dist/rename-notice-BV8HNX3O.js.map +0 -1
  74. package/dist/rename-notice-Cx1vuTRz.js +0 -2
  75. /package/dist/{bin-DrKRgnr9.d.ts → bin-g05vSfAz.d.ts} +0 -0
@@ -178,10 +178,9 @@ async function loadTypedConfig(projectDir) {
178
178
  };
179
179
  }
180
180
  /**
181
- * The specifier the documentation tells you to import from. Both spellings
182
- * publish the same code at the same versions (#315), so a config file may
183
- * legitimately name either — but only one of them belongs in a message that
184
- * tells someone what to write.
181
+ * The specifier the documentation tells you to import from. Configs written
182
+ * before the rename may still name the old one; it keeps resolving, but only
183
+ * the new one belongs in a message that tells someone what to write.
185
184
  */
186
185
  const CONFIG_SPECIFIER = "@the-i18n-kit/cli/config";
187
186
  const DEPRECATED_CONFIG_SPECIFIER = "the-i18n-cli/config";
@@ -196,8 +195,8 @@ const DEPRECATED_CONFIG_SPECIFIER = "the-i18n-cli/config";
196
195
  * ourselves costs nothing and is the same function either way: identity.
197
196
  *
198
197
  * Both spellings are aliased: the rescue path is worth nothing if it covers a
199
- * name the docs no longer recommend, and the deprecated one has to keep working
200
- * until the window in #344 closes.
198
+ * name the docs no longer recommend, and the deprecated one keeps working so
199
+ * existing configs do not break.
201
200
  *
202
201
  * Returns no alias when the entry cannot be located, which leaves normal
203
202
  * resolution to find the installed package.
@@ -505,4 +504,4 @@ async function createTranslateFn(config) {
505
504
  //#endregion
506
505
  export { resolveProviderBaseUrl as a, validateProjectConfig as c, createTranslateFn as i, log as l, TranslateProviderError as n, CONFIG_FILENAME as o, classifyProviderError as r, loadProjectConfig as s, BASE_URL_ENV as t };
507
506
 
508
- //# sourceMappingURL=providers-CHE2ffi6.js.map
507
+ //# sourceMappingURL=providers-CAsU_aV1.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers-CAsU_aV1.js","names":[],"sources":["../src/utils/logger.ts","../src/config/schema.ts","../src/config/typed-config.ts","../src/config/project-config.ts","../src/llm/providers.ts"],"sourcesContent":["import { createConsola } from 'consola'\n\n// All diagnostics go to stderr — stdout is reserved for command results so\n// that `the-i18n-cli … | jq` and CI `$(…)` captures always receive pure JSON.\nexport const log = createConsola({\n stdout: process.stderr,\n stderr: process.stderr,\n}).withTag('the-i18n-cli')\n","/**\n * The one definition of the declared half of the kit's configuration —\n * everything a human tells the tool, as opposed to what it can see for itself.\n *\n * `.i18n-mcp.json`, `i18n-kit.config.ts` and the `i18nKit` block in\n * `nuxt.config.ts` all accept this same shape, and all validate against this\n * same schema. The `ProjectConfig` interface in `./types.ts` documents it;\n * the guard at the bottom of this file fails the build if the two drift.\n *\n * The `.describe()` text on each field is the published documentation of that\n * field: `./schema-json.ts` converts this schema into `packages/mcp/schema.json`,\n * which editors read for validation and tooltips. Write it for a user looking\n * at their own config file, not for a reader of this module.\n */\nimport { z } from 'zod'\nimport type { ProjectConfig } from './types.js'\nimport { log } from '../utils/logger.js'\n\nconst nonEmptyString = z.string().min(1).refine(s => s.trim().length > 0, 'Must not be empty or whitespace-only')\n\nconst layerRuleSchema = z.object({\n layer: z.string()\n .describe('Layer name (e.g., \\'root\\', \\'app-admin\\', \\'app-shop\\', \\'lang\\').'),\n description: z.string()\n .describe('What this layer contains — key namespaces, domain scope, etc.'),\n when: z.string()\n .describe('Natural-language rule describing when a key should go in this layer.'),\n})\n\nconst localeDirEntrySchema = z.union([\n nonEmptyString\n .describe('Relative path to a locale directory. Layer name defaults to \\'default\\'.'),\n z.object({\n path: nonEmptyString.describe('Relative path to a locale directory.'),\n layer: nonEmptyString.describe('Layer name for this locale directory.'),\n }),\n])\n\nexport const projectConfigSchema = z.object({\n $schema: z.string()\n .describe('Path or URL to the JSON schema for IDE autocompletion.')\n .optional(),\n framework: z.string()\n .describe(\n 'Force framework detection instead of auto-detecting from project structure. '\n + 'Any registered adapter name is accepted — the suggestions are the adapters '\n + 'that ship today, not the only permitted values.',\n )\n .optional(),\n context: z.string()\n .describe(\n 'Free-form project background for the agent — business domain, user base, brand '\n + 'voice, anything that helps the agent understand the project.',\n )\n .optional(),\n layerRules: z.array(layerRuleSchema)\n .describe(\n 'Rules that help the agent decide which layer a new translation key belongs to. '\n + 'The agent interprets the natural-language \\'when\\' field.',\n )\n .optional(),\n glossary: z.record(z.string(), z.string())\n .describe(\n 'Term dictionary for consistent translations. Keys are source terms, values '\n + 'describe the required translation or usage note.',\n )\n .optional(),\n translationPrompt: z.string()\n .describe(\n 'System prompt prepended to all translation requests (provider mode and agent-mode '\n + 'fallback contexts). Sets tone, style, and constraints.',\n )\n .optional(),\n localeNotes: z.record(z.string(), z.string())\n .describe(\n 'Per-locale context included in translation prompts. Keys are locale codes (e.g., '\n + '\\'de-DE\\', \\'en-US\\', \\'de-DE-formal\\'), values describe register, regional '\n + 'conventions, or other locale-specific guidance.',\n )\n .optional(),\n examples: z.array(\n // `key` is required because the prompt builder renders `- ${ex.key}: …`\n // unconditionally: an entry without one reached the provider as the literal\n // string \"undefined\", offered as a translation key to imitate (#367).\n z.object({\n key: z.string().describe('The dot-path translation key this example demonstrates (e.g., \\'common.actions.save\\').'),\n note: z.string().describe('Optional style comment shown alongside the example.').optional(),\n })\n .catchall(z.string())\n .describe(\n 'A single translation example. \\'key\\' holds the dot-path translation key (e.g., '\n + '\\'common.actions.save\\'), \\'note\\' an optional style comment, and every other '\n + 'property is a locale code (e.g., \\'de-DE\\', \\'en-US\\') mapped to its translated value.',\n ),\n )\n .describe(\n 'Few-shot translation examples that demonstrate the project\\'s style. The agent uses '\n + 'these as reference when generating translations.',\n )\n .optional(),\n orphanScan: z.record(z.string(), z.object({\n ignorePatterns: z.array(z.string())\n .describe(\n 'Glob patterns for translation keys to exclude from orphan detection (e.g., '\n + '\"common.datetime.months.*\"). Use * to match a single key segment, ** to match any depth.',\n )\n .optional(),\n // passthrough for backwards-compat with deprecated keys like includeParentLayer\n }).passthrough())\n .describe(\n 'Per-layer configuration for orphan key detection. Map each layer name to its settings. '\n + 'Scan directories are automatically determined from each layer\\'s root directory.',\n )\n .optional(),\n localeDirs: z.array(localeDirEntrySchema)\n .describe(\n 'Locale directories for the generic adapter. Each entry is a path string (layer defaults '\n + 'to \\'default\\') or an object with \\'path\\' and \\'layer\\' properties.',\n )\n .optional(),\n defaultLocale: nonEmptyString\n .describe('Default locale code. Required for generic adapter activation.')\n .optional(),\n locales: z.array(nonEmptyString)\n .describe(\n 'Explicit list of locale codes to operate on. If absent, locales are auto-discovered '\n + 'from files on disk.',\n )\n .optional(),\n protectedLocales: z.array(nonEmptyString)\n .describe(\n 'Human-maintained locales excluded from automatic translation. Entries may be any locale '\n + 'ref (code, language tag, or file name); entries that do not match a known locale are '\n + 'ignored with a warning. Explicitly naming a protected locale in targetLocales overrides '\n + 'the protection (with a warning).',\n )\n .optional(),\n reportOutput: z.union([\n z.literal(true)\n .describe('Set to true to write reports to the default \\'.i18n-reports/\\' directory.'),\n nonEmptyString\n .describe('Custom directory path (relative to project root) where diagnostic tool reports are written.'),\n ])\n .describe(\n 'Enable file output for diagnostic tools (get_missing_translations, search_translations, '\n + 'find_orphan_keys, remove_orphan_keys). When set, each tool writes its full JSON report to '\n + '<reportOutput>/<toolName>.json and returns only a summary in the MCP response. Set to true '\n + 'for the default \\'.i18n-reports/\\' directory, or a string for a custom path.',\n )\n .optional(),\n localeFileFormat: z.enum(['json', 'php-array'])\n .describe(\n 'Override the auto-detected locale file format. Useful when both formats exist or '\n + 'auto-detection picks wrong.',\n )\n .optional(),\n providerBaseUrl: nonEmptyString\n .describe(\n 'Base URL for the LLM provider — gateways, self-hosted model servers and corporate proxies '\n + 'that speak the provider\\'s own protocol. Overrides the endpoint only, not the request shape '\n + 'or auth header. Overridden by the I18N_BASE_URL environment variable and by --baseUrl. '\n + 'Not supported by the \"google\" provider, which rejects it rather than ignoring it.',\n )\n .optional(),\n // Deprecated with the removal of MCP sampling: accepted so existing config\n // files keep validating (the schema is strict), warned about, and ignored.\n samplingPreferences: z.unknown()\n .meta({\n deprecated: true,\n description:\n 'Deprecated and ignored — MCP sampling was removed. Still accepted so existing config '\n + 'files keep validating. Configure a provider instead (e.g., I18N_PROVIDER/I18N_MODEL).',\n })\n .optional(),\n}).strict()\n\n/**\n * Validate a candidate project config against the published schema, returning\n * the formatted issue list rather than throwing. `init` uses this to refuse to\n * emit a config the tool would then reject; the loaders turn the same failure\n * into a ConfigError naming the file it came from.\n */\nexport function validateProjectConfig(\n value: unknown,\n): { ok: true, data: ProjectConfig } | { ok: false, error: string } {\n const result = projectConfigSchema.safeParse(value)\n if (result.success) return { ok: true, data: result.data as ProjectConfig }\n\n const error = result.error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join('.') : '(root)'\n return ` ${path}: ${issue.message}`\n })\n .join('\\n')\n return { ok: false, error }\n}\n\n/**\n * Strip the deprecated keys the schema still accepts, warning once, so that a\n * config keeps validating without the value going on to mean anything. Applied\n * by every loader — a key ignored in `.i18n-mcp.json` but silently honoured in\n * `i18n-kit.config.ts` would be exactly the divergence this file exists to\n * prevent.\n */\nexport function dropDeprecatedKeys(config: ProjectConfig, source: string): ProjectConfig {\n const data = config as ProjectConfig & { samplingPreferences?: unknown }\n if ('samplingPreferences' in data) {\n log.warn(\n `${source}: \"samplingPreferences\" is deprecated and ignored — `\n + 'MCP sampling was removed. Configure a provider (e.g. I18N_PROVIDER/I18N_MODEL) instead.',\n )\n delete data.samplingPreferences\n }\n return data\n}\n\n// ─── Drift guard ────────────────────────────────────────────────\n//\n// A key added to the interface but not the schema is rejected at runtime as an\n// unknown key (the schema is strict) — a typed config that the editor accepts\n// and the CLI refuses. A key added to the schema but not the interface is\n// accepted at runtime and invisible in an editor. Both are the drift this\n// module exists to prevent, so both are a type error here.\n//\n// `$schema` is JSON-only, and `samplingPreferences` is accepted purely so old\n// files keep validating; neither belongs in the documented type.\n\ntype SchemaKey = Exclude<keyof z.infer<typeof projectConfigSchema>, '$schema' | 'samplingPreferences'>\n\n/** Fails to compile unless `T` is `never`, naming the offending key when not. */\ntype NoDrift<T extends never> = T\n\ntype _SchemaCoversType = NoDrift<Exclude<keyof ProjectConfig, SchemaKey>>\ntype _TypeCoversSchema = NoDrift<Exclude<SchemaKey, keyof ProjectConfig>>\n","/**\n * Loading `i18n-kit.config.ts` — the same configuration `.i18n-mcp.json`\n * carries, written in TypeScript so the editor checks it.\n *\n * Read with no build step, which is the whole point: a `protectedLocales` entry\n * that only takes effect once something has been built is a locale that quietly\n * goes unprotected in a fresh checkout. Everything here runs off the source\n * file as it sits on disk.\n */\nimport { existsSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { ProjectConfig } from './types.js'\nimport { dropDeprecatedKeys, validateProjectConfig } from './schema.js'\nimport { ConfigError, toErrorMessage } from '../utils/errors.js'\nimport { log } from '../utils/logger.js'\n\n/**\n * Candidate names, in the order a directory is searched. `.ts` first because\n * it is the one worth having; the rest exist so a JavaScript project is not\n * told to adopt TypeScript to get a typed config.\n */\nconst TYPED_CONFIG_FILENAMES = [\n 'i18n-kit.config.ts',\n 'i18n-kit.config.mts',\n 'i18n-kit.config.js',\n 'i18n-kit.config.mjs',\n 'i18n-kit.config.cjs',\n 'i18n-kit.config.cts',\n] as const\n\n/**\n * Walk up from `startDir` for the nearest `i18n-kit.config.*`, the same way\n * `findConfigFile` looks for `.i18n-mcp.json`. Both stop at the first\n * directory that has one, so a config in an app directory shadows one in the\n * repository root rather than merging with it.\n */\nexport function findTypedConfigFile(startDir: string): string | null {\n // Absolute from the outset: a relative `--projectDir` would otherwise yield\n // a relative config path, and jiti reads a relative path as a bare module\n // specifier — resolved against node_modules, where it is never found.\n let dir = resolve(startDir)\n while (true) {\n const found = TYPED_CONFIG_FILENAMES\n .map(name => join(dir, name))\n .filter(path => existsSync(path))\n\n if (found[0]) {\n if (found.length > 1) {\n log.warn(\n `Several i18n-kit configs in ${dir} (${found.map(p => p.slice(dir.length + 1)).join(', ')}). `\n + `Using ${found[0].slice(dir.length + 1)} — delete the others, they are being ignored.`,\n )\n }\n return found[0]\n }\n\n const parent = dirname(dir)\n if (parent === dir) break // filesystem root\n dir = parent\n }\n return null\n}\n\n/**\n * Load and validate the nearest typed config, or null when there isn't one.\n *\n * Throws, unlike the framework-config readers this issue also adds. Those read\n * a file someone wrote for another tool, so failing to read it means falling\n * back to a guess. This file exists only for the kit: if it throws, or declares\n * a key that does not exist, carrying on without it is precisely the silent\n * non-application the typed config was added to prevent.\n */\nexport async function loadTypedConfig(\n projectDir: string,\n): Promise<{ path: string, config: ProjectConfig } | null> {\n const path = findTypedConfigFile(projectDir)\n if (!path) {\n log.debug(`No i18n-kit.config.* found in ${projectDir} or any parent directory — skipping`)\n return null\n }\n\n log.info(`Found typed project config: ${path}`)\n\n let module: unknown\n try {\n const { createJiti } = await import('jiti')\n // interopDefault stays off so that a file with only named exports is\n // distinguishable from one with a default: with the interop on, the whole\n // namespace comes back and the schema rejects it as a config full of\n // unknown keys, which explains nothing.\n const jiti = createJiti(import.meta.url, {\n interopDefault: false,\n alias: selfAlias(),\n })\n module = await jiti.import(path)\n }\n catch (error) {\n throw new ConfigError(\n `Failed to load ${path}: ${toErrorMessage(error)}`,\n )\n }\n\n if (module === null || typeof module !== 'object') {\n throw new ConfigError(\n `${path} must export its config as the default export — `\n + 'write `export default defineI18nKitConfig({ ... })`.',\n )\n }\n\n // In a `.cts` file `module.exports` comes back as the module itself, with no\n // `default` to unwrap — so for the CommonJS spellings, that object *is* the\n // config. Everywhere else a missing `default` means a file of named exports,\n // and saying so beats letting the schema report a config full of unknown keys.\n const commonJs = path.endsWith('.cts') || path.endsWith('.cjs')\n if (!('default' in module) && !commonJs) {\n throw new ConfigError(\n `${path} must export its config as the default export — `\n + 'write `export default defineI18nKitConfig({ ... })`.',\n )\n }\n\n const exported = 'default' in module ? (module as { default: unknown }).default : module\n\n if (exported === null || typeof exported !== 'object' || Array.isArray(exported)) {\n throw new ConfigError(\n `${path} must export a config object as its default export — got ${describe(exported)}. `\n + `Wrap it in defineI18nKitConfig() from \"${CONFIG_SPECIFIER}\".`,\n )\n }\n\n const result = validateProjectConfig(exported)\n if (!result.ok) {\n throw new ConfigError(`Invalid ${path}:\\n${result.error}`)\n }\n\n return { path, config: dropDeprecatedKeys(result.data, path) }\n}\n\n/**\n * The specifier the documentation tells you to import from. Configs written\n * before the rename may still name the old one; it keeps resolving, but only\n * the new one belongs in a message that tells someone what to write.\n */\nconst CONFIG_SPECIFIER = '@the-i18n-kit/cli/config'\nconst DEPRECATED_CONFIG_SPECIFIER = 'the-i18n-cli/config'\n\n/**\n * Point the `/config` specifiers at the copy of `defineI18nKitConfig` that is\n * already running.\n *\n * A config file imports it for the types, and types only need the package\n * installed at author time — but the CLI is often run through `npx` in a\n * project that never added it as a dependency, where a bare import would be\n * unresolvable and take the whole config down with it. Resolving it to\n * ourselves costs nothing and is the same function either way: identity.\n *\n * Both spellings are aliased: the rescue path is worth nothing if it covers a\n * name the docs no longer recommend, and the deprecated one keeps working so\n * existing configs do not break.\n *\n * Returns no alias when the entry cannot be located, which leaves normal\n * resolution to find the installed package.\n */\nfunction selfAlias(): Record<string, string> {\n // `./` when running from the bundle, where define-config.js sits beside the\n // chunk this code ends up in; `../` when running from source, as tests do.\n for (const candidate of ['./define-config.js', '../define-config.js', '../define-config.ts']) {\n const path = fileURLToPath(new URL(candidate, import.meta.url))\n if (existsSync(path)) {\n return { [CONFIG_SPECIFIER]: path, [DEPRECATED_CONFIG_SPECIFIER]: path }\n }\n }\n return {}\n}\n\nfunction describe(value: unknown): string {\n if (value === null) return 'null'\n if (Array.isArray(value)) return 'an array'\n if (typeof value === 'function') return 'a function (call defineI18nKitConfig, do not pass it)'\n return typeof value\n}\n","import { readFile } from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport type { ProjectConfig } from './types.js'\nimport { dropDeprecatedKeys, validateProjectConfig } from './schema.js'\nimport { loadTypedConfig } from './typed-config.js'\nimport { ConfigError, toErrorMessage } from '../utils/errors.js'\nimport { log } from '../utils/logger.js'\n\nexport const CONFIG_FILENAME = '.i18n-mcp.json'\n\n// The schema itself lives in ./schema.ts, shared with the typed-config loader.\n// Re-exported here because this has been its import path since before there\n// was a second config format.\nexport { validateProjectConfig } from './schema.js'\n\n// ─── Config file discovery ──────────────────────────────────────\n\n/**\n * Walk up the directory tree from `startDir` to find the nearest config file.\n * Returns the full path if found, null otherwise.\n */\nexport function findConfigFile(startDir: string): string | null {\n let dir = startDir\n while (true) {\n const candidate = join(dir, CONFIG_FILENAME)\n if (existsSync(candidate)) {\n return candidate\n }\n const parent = dirname(dir)\n if (parent === dir) break // filesystem root\n dir = parent\n }\n return null\n}\n\n/**\n * Load the project's declared configuration, from either place it can be\n * declared: `i18n-kit.config.ts` and `.i18n-mcp.json`. Both are searched from\n * projectDir upwards, the way ESLint, Prettier and tsconfig resolve configs.\n *\n * Returns null when neither exists — the case that must keep behaving exactly\n * as it did before there were two. Throws ConfigError when either file is\n * present but unusable, or when the two disagree.\n *\n * Every adapter funnels through here, which is what makes the typed config\n * work for all of them rather than for whichever one was taught about it.\n */\nexport async function loadProjectConfig(projectDir: string): Promise<ProjectConfig | null> {\n const [json, typed] = await Promise.all([\n loadJsonConfig(projectDir),\n loadTypedConfig(projectDir),\n ])\n\n if (!typed) return json?.config ?? null\n if (!json) return typed.config\n\n const conflicts = conflictingKeys(typed.config, json.config)\n if (conflicts.length > 0) {\n throw new ConfigError(\n `${conflicts.join(', ')} ${conflicts.length === 1 ? 'is' : 'are'} declared in both\\n`\n + ` ${typed.path}\\n`\n + ` ${json.path}\\n`\n + 'Neither wins — a reader cannot tell which one took effect. '\n + 'Delete the duplicate from one of them.',\n )\n }\n\n // Disjoint by the check above, so the spread order only decides where\n // untouched keys come from, not who wins.\n return { ...json.config, ...typed.config }\n}\n\n/** Keys present in both files, ignoring the JSON-only `$schema`. */\nfunction conflictingKeys(typed: ProjectConfig, json: ProjectConfig): string[] {\n const a = typed as Record<string, unknown>\n const b = json as Record<string, unknown>\n return Object.keys(a).filter(key => key !== '$schema' && a[key] !== undefined && b[key] !== undefined)\n}\n\n/**\n * Load the optional `.i18n-mcp.json`, with the path it was found at.\n * Returns null if no config file is found in any ancestor.\n * Throws ConfigError if a file is found but is invalid.\n */\nasync function loadJsonConfig(\n projectDir: string,\n): Promise<{ path: string, config: ProjectConfig } | null> {\n log.debug(`Searching for ${CONFIG_FILENAME} starting from: ${projectDir}`)\n\n const configPath = findConfigFile(projectDir)\n\n if (!configPath) {\n log.info(`No ${CONFIG_FILENAME} found in ${projectDir} or any parent directory — skipping`)\n return null\n }\n\n log.info(`Found project config: ${configPath}`)\n\n let raw: string\n try {\n raw = await readFile(configPath, 'utf-8')\n } catch (error) {\n throw new ConfigError(\n `Failed to read ${CONFIG_FILENAME}: ${toErrorMessage(error)}`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (error) {\n throw new ConfigError(\n `Invalid JSON in ${CONFIG_FILENAME}: ${toErrorMessage(error)}`,\n )\n }\n\n const result = validateProjectConfig(parsed)\n\n if (!result.ok) {\n throw new ConfigError(`Invalid ${CONFIG_FILENAME}:\\n${result.error}`)\n }\n\n log.debug(`Project config loaded successfully from ${configPath}`)\n\n return { path: configPath, config: dropDeprecatedKeys(result.data, CONFIG_FILENAME) }\n}\n","import type { TranslateFn, TranslateRequest, TranslateResponse } from '../core/types.js'\n\nexport type LlmProvider = 'openai' | 'anthropic' | 'google'\n\n// ─── Error classification ───────────────────────────────────────\n\n/** How a provider failure should be handled by the caller. */\nexport type TranslateProviderErrorKind = 'auth' | 'rate-limit' | 'provider' | 'config'\n\n/**\n * A classified provider failure. `auth` errors are not retryable (the caller\n * should abort the run), `rate-limit` errors should be retried with backoff,\n * and `provider` covers everything else (server errors, network, …).\n * `config` marks an unusable provider setup and is raised while building the\n * TranslateFn, before any request exists — so it surfaces from the command\n * and never reaches the retry loop.\n */\nexport class TranslateProviderError extends Error {\n public readonly kind: TranslateProviderErrorKind\n public readonly status?: number\n\n constructor(message: string, kind: TranslateProviderErrorKind, status?: number) {\n super(message)\n this.name = 'TranslateProviderError'\n this.kind = kind\n this.status = status\n }\n}\n\n/** Defensively extract an HTTP status code from an unknown SDK error shape. */\nfunction extractStatus(error: unknown): number | undefined {\n if (error === null || typeof error !== 'object') return undefined\n const e = error as { status?: unknown, response?: unknown }\n if (typeof e.status === 'number') return e.status\n if (e.response !== null && typeof e.response === 'object') {\n const status = (e.response as { status?: unknown }).status\n if (typeof status === 'number') return status\n }\n return undefined\n}\n\n/**\n * Classify an SDK error into a TranslateProviderError:\n * 401/403 → auth, 429 → rate-limit, anything else → provider.\n * Already-classified errors pass through unchanged.\n */\nexport function classifyProviderError(error: unknown): TranslateProviderError {\n if (error instanceof TranslateProviderError) return error\n const status = extractStatus(error)\n const message = error instanceof Error ? error.message : String(error)\n const kind: TranslateProviderErrorKind\n = status === 401 || status === 403 ? 'auth'\n : status === 429 ? 'rate-limit'\n : 'provider'\n return new TranslateProviderError(message, kind, status)\n}\n\nexport interface LlmProviderConfig {\n provider: LlmProvider\n model: string\n /** Override API key. Falls back to env vars */\n apiKey?: string\n /** Base URL override for proxies / compatible APIs */\n baseUrl?: string\n}\n\n/** Environment variable carrying the provider base URL override. */\nexport const BASE_URL_ENV = 'I18N_BASE_URL'\n\n/**\n * Resolve the provider base URL from its three sources, highest precedence\n * first: an explicit flag, the I18N_BASE_URL env var, then the project\n * config's `providerBaseUrl`.\n *\n * Blank values count as unset. Shells produce them routinely — `--baseUrl\n * \"$UNSET\"` or an exported-but-empty variable — and a blank must not shadow a\n * real endpoint configured further down the chain. A blank in the config file\n * is a different case: it cannot arise by accident, so the strict schema\n * rejects it at load time rather than letting it reach this function.\n */\nexport function resolveProviderBaseUrl(sources: {\n flag?: string\n env?: string\n config?: string\n}): string | undefined {\n for (const value of [sources.flag, sources.env, sources.config]) {\n if (typeof value === 'string' && value.trim().length > 0) return value.trim()\n }\n return undefined\n}\n\nconst ENV_KEY_MAP: Record<LlmProvider, string> = {\n openai: 'OPENAI_API_KEY',\n anthropic: 'ANTHROPIC_API_KEY',\n google: 'GEMINI_API_KEY',\n}\n\nfunction resolveApiKey(provider: LlmProvider, configKey?: string): string {\n if (configKey) return configKey\n const envKey = ENV_KEY_MAP[provider]\n const key = process.env[envKey]\n if (!key) {\n throw new Error(\n `No API key found for provider \"${provider}\". Set ${envKey} environment variable or pass apiKey in config.`,\n )\n }\n return key\n}\n\nasync function createOpenAiTranslateFn(config: LlmProviderConfig): Promise<TranslateFn> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic import of optional peer dep\n let OpenAI: any\n try {\n OpenAI = (await import('openai')).default as any // eslint-disable-line @typescript-eslint/no-explicit-any\n } catch {\n throw new Error(\n 'Provider \"openai\" requires the \"openai\" package. Install it with:\\n npm install openai\\n pnpm add openai\\n yarn add openai',\n )\n }\n\n const apiKey = resolveApiKey('openai', config.apiKey)\n const client = new OpenAI({ apiKey, baseURL: config.baseUrl })\n\n return async (opts: TranslateRequest): Promise<TranslateResponse> => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- SDK types not available\n let response: any\n try {\n response = await client.chat.completions.create({\n model: config.model,\n messages: [\n { role: 'system', content: opts.systemPrompt },\n { role: 'user', content: opts.userMessage },\n ],\n max_tokens: opts.maxTokens,\n temperature: 0,\n response_format: { type: 'json_object' },\n })\n } catch (error) {\n throw classifyProviderError(error)\n }\n\n const choice = response.choices?.[0]\n const text = choice?.message?.content ?? ''\n const truncated = choice?.finish_reason === 'length'\n return { text, model: response.model || config.model, ...(truncated ? { truncated: true } : {}) }\n }\n}\n\nasync function createGoogleTranslateFn(config: LlmProviderConfig): Promise<TranslateFn> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic import of optional peer dep\n let GoogleGenAI: any\n try {\n GoogleGenAI = (await import('@google/genai')).GoogleGenAI as any // eslint-disable-line @typescript-eslint/no-explicit-any\n } catch {\n throw new Error(\n 'Provider \"google\" requires the \"@google/genai\" package. Install it with:\\n npm install @google/genai\\n pnpm add @google/genai\\n yarn add @google/genai',\n )\n }\n\n const apiKey = resolveApiKey('google', config.apiKey)\n const client = new GoogleGenAI({ apiKey })\n\n return async (opts: TranslateRequest): Promise<TranslateResponse> => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- SDK types not available\n let response: any\n try {\n response = await client.models.generateContent({\n model: config.model,\n contents: opts.userMessage,\n config: {\n systemInstruction: opts.systemPrompt,\n maxOutputTokens: opts.maxTokens,\n temperature: 0,\n responseMimeType: 'application/json',\n },\n })\n } catch (error) {\n throw classifyProviderError(error)\n }\n\n const text = response.text ?? ''\n const truncated = response.candidates?.[0]?.finishReason === 'MAX_TOKENS'\n return { text, model: response.modelVersion || config.model, ...(truncated ? { truncated: true } : {}) }\n }\n}\n\nasync function createAnthropicTranslateFn(config: LlmProviderConfig): Promise<TranslateFn> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic import of optional peer dep\n let Anthropic: any\n try {\n Anthropic = (await import('@anthropic-ai/sdk')).Anthropic as any // eslint-disable-line @typescript-eslint/no-explicit-any\n } catch {\n throw new Error(\n 'Provider \"anthropic\" requires the \"@anthropic-ai/sdk\" package. Install it with:\\n npm install @anthropic-ai/sdk\\n pnpm add @anthropic-ai/sdk\\n yarn add @anthropic-ai/sdk',\n )\n }\n\n const apiKey = resolveApiKey('anthropic', config.apiKey)\n const client = new Anthropic({ apiKey, baseURL: config.baseUrl })\n\n return async (opts: TranslateRequest): Promise<TranslateResponse> => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- SDK types not available\n let response: any\n try {\n response = await client.messages.create({\n model: config.model,\n system: opts.systemPrompt,\n messages: [{ role: 'user', content: opts.userMessage }],\n max_tokens: opts.maxTokens,\n temperature: 0,\n })\n } catch (error) {\n throw classifyProviderError(error)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- SDK types not available\n const textBlock = response.content?.find((block: any) => block.type === 'text')\n const text = textBlock?.text ?? ''\n const truncated = response.stop_reason === 'max_tokens'\n return { text, model: response.model || config.model, ...(truncated ? { truncated: true } : {}) }\n }\n}\n\n/**\n * Create a TranslateFn from an LLM provider config.\n * Throws if the provider SDK is not installed or API key is missing.\n */\nexport async function createTranslateFn(config: LlmProviderConfig): Promise<TranslateFn> {\n // @google/genai exposes no endpoint override, so a base URL here would be\n // silently ignored and translate against the wrong endpoint. Refuse loudly.\n if (config.provider === 'google' && config.baseUrl) {\n throw new TranslateProviderError(\n 'Provider \"google\" does not support a custom base URL. '\n + `Remove --baseUrl / ${BASE_URL_ENV} / providerBaseUrl, or use an OpenAI-compatible provider.`,\n 'config',\n )\n }\n\n switch (config.provider) {\n case 'openai':\n return createOpenAiTranslateFn(config)\n case 'anthropic':\n return createAnthropicTranslateFn(config)\n case 'google':\n return createGoogleTranslateFn(config)\n default: {\n const _exhaustive: never = config.provider\n throw new Error(`Unknown provider: ${_exhaustive}`)\n }\n }\n}\n"],"mappings":";;;;;;;;AAIA,MAAa,MAAM,cAAc;CAC/B,QAAQ,QAAQ;CAChB,QAAQ,QAAQ;CACjB,CAAC,CAAC,QAAQ,eAAe;;;;;;;;;;;;;;;;;ACW1B,MAAM,iBAAiB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAO,MAAK,EAAE,MAAM,CAAC,SAAS,GAAG,uCAAuC;AAEjH,MAAM,kBAAkB,EAAE,OAAO;CAC/B,OAAO,EAAE,QAAQ,CACd,SAAS,8DAAsE;CAClF,aAAa,EAAE,QAAQ,CACpB,SAAS,gEAAgE;CAC5E,MAAM,EAAE,QAAQ,CACb,SAAS,uEAAuE;CACpF,CAAC;AAEF,MAAM,uBAAuB,EAAE,MAAM,CACnC,eACG,SAAS,yEAA2E,EACvF,EAAE,OAAO;CACP,MAAM,eAAe,SAAS,uCAAuC;CACrE,OAAO,eAAe,SAAS,wCAAwC;CACxE,CAAC,CACH,CAAC;AAEF,MAAa,sBAAsB,EAAE,OAAO;CAC1C,SAAS,EAAE,QAAQ,CAChB,SAAS,yDAAyD,CAClE,UAAU;CACb,WAAW,EAAE,QAAQ,CAClB,SACC,yMAGD,CACA,UAAU;CACb,SAAS,EAAE,QAAQ,CAChB,SACC,8IAED,CACA,UAAU;CACb,YAAY,EAAE,MAAM,gBAAgB,CACjC,SACC,yIAED,CACA,UAAU;CACb,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CACvC,SACC,8HAED,CACA,UAAU;CACb,mBAAmB,EAAE,QAAQ,CAC1B,SACC,2IAED,CACA,UAAU;CACb,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAC1C,SACC,yMAGD,CACA,UAAU;CACb,UAAU,EAAE,MAIV,EAAE,OAAO;EACP,KAAK,EAAE,QAAQ,CAAC,SAAS,wFAA0F;EACnH,MAAM,EAAE,QAAQ,CAAC,SAAS,sDAAsD,CAAC,UAAU;EAC5F,CAAC,CACC,SAAS,EAAE,QAAQ,CAAC,CACpB,SACC,6OAGD,CACJ,CACE,SACC,sIAED,CACA,UAAU;CACb,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,EACxC,gBAAgB,EAAE,MAAM,EAAE,QAAQ,CAAC,CAChC,SACC,wKAED,CACA,UAAU,EAEd,CAAC,CAAC,aAAa,CAAC,CACd,SACC,yKAED,CACA,UAAU;CACb,YAAY,EAAE,MAAM,qBAAqB,CACtC,SACC,yJAED,CACA,UAAU;CACb,eAAe,eACZ,SAAS,gEAAgE,CACzE,UAAU;CACb,SAAS,EAAE,MAAM,eAAe,CAC7B,SACC,0GAED,CACA,UAAU;CACb,kBAAkB,EAAE,MAAM,eAAe,CACtC,SACC,wSAID,CACA,UAAU;CACb,cAAc,EAAE,MAAM,CACpB,EAAE,QAAQ,KAAK,CACZ,SAAS,0EAA4E,EACxF,eACG,SAAS,8FAA8F,CAC3G,CAAC,CACC,SACC,0VAID,CACA,UAAU;CACb,kBAAkB,EAAE,KAAK,CAAC,QAAQ,YAAY,CAAC,CAC5C,SACC,+GAED,CACA,UAAU;CACb,iBAAiB,eACd,SACC,kWAID,CACA,UAAU;CAGb,qBAAqB,EAAE,SAAS,CAC7B,KAAK;EACJ,YAAY;EACZ,aACE;EAEH,CAAC,CACD,UAAU;CACd,CAAC,CAAC,QAAQ;;;;;;;AAQX,SAAgB,sBACd,OACkE;CAClE,MAAM,SAAS,oBAAoB,UAAU,MAAM;AACnD,KAAI,OAAO,QAAS,QAAO;EAAE,IAAI;EAAM,MAAM,OAAO;EAAuB;AAQ3E,QAAO;EAAE,IAAI;EAAO,OANN,OAAO,MAAM,OACxB,KAAK,UAAU;AAEd,UAAO,KADM,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,IAAI,GAAG,SAC3C,IAAI,MAAM;IAC3B,CACD,KAAK,KAAK;EACc;;;;;;;;;AAU7B,SAAgB,mBAAmB,QAAuB,QAA+B;CACvF,MAAM,OAAO;AACb,KAAI,yBAAyB,MAAM;AACjC,MAAI,KACF,GAAG,OAAO,6IAEX;AACD,SAAO,KAAK;;AAEd,QAAO;;;;;;;;;;;;;;;;;;AC/LT,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAQD,SAAgB,oBAAoB,UAAiC;CAInE,IAAI,MAAM,QAAQ,SAAS;AAC3B,QAAO,MAAM;EACX,MAAM,QAAQ,uBACX,KAAI,SAAQ,KAAK,KAAK,KAAK,CAAC,CAC5B,QAAO,SAAQ,WAAW,KAAK,CAAC;AAEnC,MAAI,MAAM,IAAI;AACZ,OAAI,MAAM,SAAS,EACjB,KAAI,KACF,+BAA+B,IAAI,IAAI,MAAM,KAAI,MAAK,EAAE,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,WAC/E,MAAM,GAAG,MAAM,IAAI,SAAS,EAAE,CAAC,+CAC3C;AAEH,UAAO,MAAM;;EAGf,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,IAAK;AACpB,QAAM;;AAER,QAAO;;;;;;;;;;;AAYT,eAAsB,gBACpB,YACyD;CACzD,MAAM,OAAO,oBAAoB,WAAW;AAC5C,KAAI,CAAC,MAAM;AACT,MAAI,MAAM,iCAAiC,WAAW,qCAAqC;AAC3F,SAAO;;AAGT,KAAI,KAAK,+BAA+B,OAAO;CAE/C,IAAI;AACJ,KAAI;EACF,MAAM,EAAE,eAAe,MAAM,OAAO;AASpC,WAAS,MAJI,WAAW,OAAO,KAAK,KAAK;GACvC,gBAAgB;GAChB,OAAO,WAAW;GACnB,CAAC,CACkB,OAAO,KAAK;UAE3B,OAAO;AACZ,QAAM,IAAI,YACR,kBAAkB,KAAK,IAAI,eAAe,MAAM,GACjD;;AAGH,KAAI,WAAW,QAAQ,OAAO,WAAW,SACvC,OAAM,IAAI,YACR,GAAG,KAAK,wGAET;CAOH,MAAM,WAAW,KAAK,SAAS,OAAO,IAAI,KAAK,SAAS,OAAO;AAC/D,KAAI,EAAE,aAAa,WAAW,CAAC,SAC7B,OAAM,IAAI,YACR,GAAG,KAAK,wGAET;CAGH,MAAM,WAAW,aAAa,SAAU,OAAgC,UAAU;AAElF,KAAI,aAAa,QAAQ,OAAO,aAAa,YAAY,MAAM,QAAQ,SAAS,CAC9E,OAAM,IAAI,YACR,GAAG,KAAK,2DAA2D,SAAS,SAAS,CAAC,2CAC1C,iBAAiB,IAC9D;CAGH,MAAM,SAAS,sBAAsB,SAAS;AAC9C,KAAI,CAAC,OAAO,GACV,OAAM,IAAI,YAAY,WAAW,KAAK,KAAK,OAAO,QAAQ;AAG5D,QAAO;EAAE;EAAM,QAAQ,mBAAmB,OAAO,MAAM,KAAK;EAAE;;;;;;;AAQhE,MAAM,mBAAmB;AACzB,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;AAmBpC,SAAS,YAAoC;AAG3C,MAAK,MAAM,aAAa;EAAC;EAAsB;EAAuB;EAAsB,EAAE;EAC5F,MAAM,OAAO,cAAc,IAAI,IAAI,WAAW,OAAO,KAAK,IAAI,CAAC;AAC/D,MAAI,WAAW,KAAK,CAClB,QAAO;IAAG,mBAAmB;IAAO,8BAA8B;GAAM;;AAG5E,QAAO,EAAE;;AAGX,SAAS,SAAS,OAAwB;AACxC,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO;AACjC,KAAI,OAAO,UAAU,WAAY,QAAO;AACxC,QAAO,OAAO;;;;AC3KhB,MAAa,kBAAkB;;;;;AAa/B,SAAgB,eAAe,UAAiC;CAC9D,IAAI,MAAM;AACV,QAAO,MAAM;EACX,MAAM,YAAY,KAAK,KAAK,gBAAgB;AAC5C,MAAI,WAAW,UAAU,CACvB,QAAO;EAET,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,IAAK;AACpB,QAAM;;AAER,QAAO;;;;;;;;;;;;;;AAeT,eAAsB,kBAAkB,YAAmD;CACzF,MAAM,CAAC,MAAM,SAAS,MAAM,QAAQ,IAAI,CACtC,eAAe,WAAW,EAC1B,gBAAgB,WAAW,CAC5B,CAAC;AAEF,KAAI,CAAC,MAAO,QAAO,MAAM,UAAU;AACnC,KAAI,CAAC,KAAM,QAAO,MAAM;CAExB,MAAM,YAAY,gBAAgB,MAAM,QAAQ,KAAK,OAAO;AAC5D,KAAI,UAAU,SAAS,EACrB,OAAM,IAAI,YACR,GAAG,UAAU,KAAK,KAAK,CAAC,GAAG,UAAU,WAAW,IAAI,OAAO,MAAM,uBAC1D,MAAM,KAAK,MACX,KAAK,KAAK,qGAGlB;AAKH,QAAO;EAAE,GAAG,KAAK;EAAQ,GAAG,MAAM;EAAQ;;;AAI5C,SAAS,gBAAgB,OAAsB,MAA+B;CAC5E,MAAM,IAAI;CACV,MAAM,IAAI;AACV,QAAO,OAAO,KAAK,EAAE,CAAC,QAAO,QAAO,QAAQ,aAAa,EAAE,SAAS,KAAA,KAAa,EAAE,SAAS,KAAA,EAAU;;;;;;;AAQxG,eAAe,eACb,YACyD;AACzD,KAAI,MAAM,iBAAiB,gBAAgB,kBAAkB,aAAa;CAE1E,MAAM,aAAa,eAAe,WAAW;AAE7C,KAAI,CAAC,YAAY;AACf,MAAI,KAAK,MAAM,gBAAgB,YAAY,WAAW,qCAAqC;AAC3F,SAAO;;AAGT,KAAI,KAAK,yBAAyB,aAAa;CAE/C,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,SAAS,YAAY,QAAQ;UAClC,OAAO;AACd,QAAM,IAAI,YACR,kBAAkB,gBAAgB,IAAI,eAAe,MAAM,GAC5D;;CAGH,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,IAAI;UACjB,OAAO;AACd,QAAM,IAAI,YACR,mBAAmB,gBAAgB,IAAI,eAAe,MAAM,GAC7D;;CAGH,MAAM,SAAS,sBAAsB,OAAO;AAE5C,KAAI,CAAC,OAAO,GACV,OAAM,IAAI,YAAY,WAAW,gBAAgB,KAAK,OAAO,QAAQ;AAGvE,KAAI,MAAM,2CAA2C,aAAa;AAElE,QAAO;EAAE,MAAM;EAAY,QAAQ,mBAAmB,OAAO,MAAM,gBAAgB;EAAE;;;;;;;;;;;;AC5GvF,IAAa,yBAAb,cAA4C,MAAM;CAChD;CACA;CAEA,YAAY,SAAiB,MAAkC,QAAiB;AAC9E,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,OAAO;AACZ,OAAK,SAAS;;;;AAKlB,SAAS,cAAc,OAAoC;AACzD,KAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO,KAAA;CACxD,MAAM,IAAI;AACV,KAAI,OAAO,EAAE,WAAW,SAAU,QAAO,EAAE;AAC3C,KAAI,EAAE,aAAa,QAAQ,OAAO,EAAE,aAAa,UAAU;EACzD,MAAM,SAAU,EAAE,SAAkC;AACpD,MAAI,OAAO,WAAW,SAAU,QAAO;;;;;;;;AAU3C,SAAgB,sBAAsB,OAAwC;AAC5E,KAAI,iBAAiB,uBAAwB,QAAO;CACpD,MAAM,SAAS,cAAc,MAAM;AAMnC,QAAO,IAAI,uBALK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAElE,WAAW,OAAO,WAAW,MAAM,SACjC,WAAW,MAAM,eACf,YACyC,OAAO;;;AAa1D,MAAa,eAAe;;;;;;;;;;;;AAa5B,SAAgB,uBAAuB,SAIhB;AACrB,MAAK,MAAM,SAAS;EAAC,QAAQ;EAAM,QAAQ;EAAK,QAAQ;EAAO,CAC7D,KAAI,OAAO,UAAU,YAAY,MAAM,MAAM,CAAC,SAAS,EAAG,QAAO,MAAM,MAAM;;AAKjF,MAAM,cAA2C;CAC/C,QAAQ;CACR,WAAW;CACX,QAAQ;CACT;AAED,SAAS,cAAc,UAAuB,WAA4B;AACxE,KAAI,UAAW,QAAO;CACtB,MAAM,SAAS,YAAY;CAC3B,MAAM,MAAM,QAAQ,IAAI;AACxB,KAAI,CAAC,IACH,OAAM,IAAI,MACR,kCAAkC,SAAS,SAAS,OAAO,iDAC5D;AAEH,QAAO;;AAGT,eAAe,wBAAwB,QAAiD;CAEtF,IAAI;AACJ,KAAI;AACF,YAAU,MAAM,OAAO,WAAW;SAC5B;AACN,QAAM,IAAI,MACR,oIACD;;CAGH,MAAM,SAAS,cAAc,UAAU,OAAO,OAAO;CACrD,MAAM,SAAS,IAAI,OAAO;EAAE;EAAQ,SAAS,OAAO;EAAS,CAAC;AAE9D,QAAO,OAAO,SAAuD;EAEnE,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,OAAO,KAAK,YAAY,OAAO;IAC9C,OAAO,OAAO;IACd,UAAU,CACR;KAAE,MAAM;KAAU,SAAS,KAAK;KAAc,EAC9C;KAAE,MAAM;KAAQ,SAAS,KAAK;KAAa,CAC5C;IACD,YAAY,KAAK;IACjB,aAAa;IACb,iBAAiB,EAAE,MAAM,eAAe;IACzC,CAAC;WACK,OAAO;AACd,SAAM,sBAAsB,MAAM;;EAGpC,MAAM,SAAS,SAAS,UAAU;EAClC,MAAM,OAAO,QAAQ,SAAS,WAAW;EACzC,MAAM,YAAY,QAAQ,kBAAkB;AAC5C,SAAO;GAAE;GAAM,OAAO,SAAS,SAAS,OAAO;GAAO,GAAI,YAAY,EAAE,WAAW,MAAM,GAAG,EAAE;GAAG;;;AAIrG,eAAe,wBAAwB,QAAiD;CAEtF,IAAI;AACJ,KAAI;AACF,iBAAe,MAAM,OAAO,kBAAkB;SACxC;AACN,QAAM,IAAI,MACR,gKACD;;CAGH,MAAM,SAAS,cAAc,UAAU,OAAO,OAAO;CACrD,MAAM,SAAS,IAAI,YAAY,EAAE,QAAQ,CAAC;AAE1C,QAAO,OAAO,SAAuD;EAEnE,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,OAAO,OAAO,gBAAgB;IAC7C,OAAO,OAAO;IACd,UAAU,KAAK;IACf,QAAQ;KACN,mBAAmB,KAAK;KACxB,iBAAiB,KAAK;KACtB,aAAa;KACb,kBAAkB;KACnB;IACF,CAAC;WACK,OAAO;AACd,SAAM,sBAAsB,MAAM;;EAGpC,MAAM,OAAO,SAAS,QAAQ;EAC9B,MAAM,YAAY,SAAS,aAAa,IAAI,iBAAiB;AAC7D,SAAO;GAAE;GAAM,OAAO,SAAS,gBAAgB,OAAO;GAAO,GAAI,YAAY,EAAE,WAAW,MAAM,GAAG,EAAE;GAAG;;;AAI5G,eAAe,2BAA2B,QAAiD;CAEzF,IAAI;AACJ,KAAI;AACF,eAAa,MAAM,OAAO,sBAAsB;SAC1C;AACN,QAAM,IAAI,MACR,mLACD;;CAGH,MAAM,SAAS,cAAc,aAAa,OAAO,OAAO;CACxD,MAAM,SAAS,IAAI,UAAU;EAAE;EAAQ,SAAS,OAAO;EAAS,CAAC;AAEjE,QAAO,OAAO,SAAuD;EAEnE,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,OAAO,SAAS,OAAO;IACtC,OAAO,OAAO;IACd,QAAQ,KAAK;IACb,UAAU,CAAC;KAAE,MAAM;KAAQ,SAAS,KAAK;KAAa,CAAC;IACvD,YAAY,KAAK;IACjB,aAAa;IACd,CAAC;WACK,OAAO;AACd,SAAM,sBAAsB,MAAM;;EAKpC,MAAM,QADY,SAAS,SAAS,MAAM,UAAe,MAAM,SAAS,OAAO,GACvD,QAAQ;EAChC,MAAM,YAAY,SAAS,gBAAgB;AAC3C,SAAO;GAAE;GAAM,OAAO,SAAS,SAAS,OAAO;GAAO,GAAI,YAAY,EAAE,WAAW,MAAM,GAAG,EAAE;GAAG;;;;;;;AAQrG,eAAsB,kBAAkB,QAAiD;AAGvF,KAAI,OAAO,aAAa,YAAY,OAAO,QACzC,OAAM,IAAI,uBACR,4EACwB,aAAa,4DACrC,SACD;AAGH,SAAQ,OAAO,UAAf;EACE,KAAK,SACH,QAAO,wBAAwB,OAAO;EACxC,KAAK,YACH,QAAO,2BAA2B,OAAO;EAC3C,KAAK,SACH,QAAO,wBAAwB,OAAO;EACxC,SAAS;GACP,MAAM,cAAqB,OAAO;AAClC,SAAM,IAAI,MAAM,qBAAqB,cAAc"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { u as removeTranslations } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { u as removeTranslations } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { o as splitList, t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { o as splitList, t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/remove.ts
7
7
  var remove_default = createCommand({
8
8
  name: "remove",
@@ -38,4 +38,4 @@ var remove_default = createCommand({
38
38
  //#endregion
39
39
  export { remove_default as default };
40
40
 
41
- //# sourceMappingURL=remove-DTbqM4bR.js.map
41
+ //# sourceMappingURL=remove-CVBjJV9h.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"remove-DTbqM4bR.js","names":[],"sources":["../src/commands/remove.ts"],"sourcesContent":["import { createCommand, splitList } from './_shared.js'\nimport { removeTranslations } from '../core/operations.js'\n\nexport default createCommand({\n name: 'remove',\n description: 'Remove translation keys from all locale files in a layer',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n keys: { type: 'string', description: 'Comma-separated key paths to remove', required: true },\n dryRun: { type: 'boolean', description: 'Preview changes without writing', default: false },\n },\n async run(args) {\n const keys = splitList(args.keys) ?? []\n if (keys.length === 0) {\n throw new Error('No keys provided. Pass comma-separated key paths via --keys')\n }\n return removeTranslations({ layer: args.layer, keys, dryRun: args.dryRun, projectDir: args.projectDir })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,MAAM;GAAE,MAAM;GAAU,aAAa;GAAuC,UAAU;GAAM;EAC5F,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAmC,SAAS;GAAO;EAC5F;CACD,MAAM,IAAI,MAAM;EACd,MAAM,OAAO,UAAU,KAAK,KAAK,IAAI,EAAE;AACvC,MAAI,KAAK,WAAW,EAClB,OAAM,IAAI,MAAM,8DAA8D;AAEhF,SAAO,mBAAmB;GAAE,OAAO,KAAK;GAAO;GAAM,QAAQ,KAAK;GAAQ,YAAY,KAAK;GAAY,CAAC;;CAE3G,CAAC"}
1
+ {"version":3,"file":"remove-CVBjJV9h.js","names":[],"sources":["../src/commands/remove.ts"],"sourcesContent":["import { createCommand, splitList } from './_shared.js'\nimport { removeTranslations } from '../core/operations.js'\n\nexport default createCommand({\n name: 'remove',\n description: 'Remove translation keys from all locale files in a layer',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n keys: { type: 'string', description: 'Comma-separated key paths to remove', required: true },\n dryRun: { type: 'boolean', description: 'Preview changes without writing', default: false },\n },\n async run(args) {\n const keys = splitList(args.keys) ?? []\n if (keys.length === 0) {\n throw new Error('No keys provided. Pass comma-separated key paths via --keys')\n }\n return removeTranslations({ layer: args.layer, keys, dryRun: args.dryRun, projectDir: args.projectDir })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,MAAM;GAAE,MAAM;GAAU,aAAa;GAAuC,UAAU;GAAM;EAC5F,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAmC,SAAS;GAAO;EAC5F;CACD,MAAM,IAAI,MAAM;EACd,MAAM,OAAO,UAAU,KAAK,KAAK,IAAI,EAAE;AACvC,MAAI,KAAK,WAAW,EAClB,OAAM,IAAI,MAAM,8DAA8D;AAEhF,SAAO,mBAAmB;GAAE,OAAO,KAAK;GAAO;GAAM,QAAQ,KAAK;GAAQ,YAAY,KAAK;GAAY,CAAC;;CAE3G,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { i as removeOrphanKeys } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { i as removeOrphanKeys } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/remove-orphans.ts
7
7
  var remove_orphans_default = createCommand({
8
8
  name: "remove-orphans",
@@ -54,4 +54,4 @@ var remove_orphans_default = createCommand({
54
54
  //#endregion
55
55
  export { remove_orphans_default as default };
56
56
 
57
- //# sourceMappingURL=remove-orphans-Dfzu7hRz.js.map
57
+ //# sourceMappingURL=remove-orphans-BaKEISxA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"remove-orphans-Dfzu7hRz.js","names":[],"sources":["../src/commands/remove-orphans.ts"],"sourcesContent":["import { createCommand } from './_shared.js'\nimport { removeOrphanKeys } from '../core/operations.js'\n\nexport default createCommand({\n name: 'remove-orphans',\n description: 'Find and remove orphan translation keys not referenced in source code',\n args: {\n layer: { type: 'string', description: 'Filter to a specific layer' },\n locale: { type: 'string', description: 'Locale to check (default: project default)' },\n dryRun: { type: 'boolean', description: 'Preview without removing (default: true)', default: true },\n outputFile: { type: 'string', description: 'Write full output to this file path and return only a summary (useful for large outputs)' },\n codequalityOutput: { type: 'string', description: 'Also write the orphan findings as a GitLab Code Quality (CodeClimate) JSON report to this file path' },\n failOnOrphans: { type: 'boolean', description: 'Exit 2 when any orphan key is found (CI gate)', default: false },\n },\n gates: [{ flag: 'failOnOrphans', counter: 'orphanCount', threshold: 0 }],\n async run(args) {\n return removeOrphanKeys({\n layer: args.layer,\n locale: args.locale,\n dryRun: args.dryRun,\n projectDir: args.projectDir,\n outputFile: args.outputFile,\n codequalityOutput: args.codequalityOutput,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,yBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8B;EACpE,QAAQ;GAAE,MAAM;GAAU,aAAa;GAA8C;EACrF,QAAQ;GAAE,MAAM;GAAW,aAAa;GAA4C,SAAS;GAAM;EACnG,YAAY;GAAE,MAAM;GAAU,aAAa;GAA4F;EACvI,mBAAmB;GAAE,MAAM;GAAU,aAAa;GAAuG;EACzJ,eAAe;GAAE,MAAM;GAAW,aAAa;GAAiD,SAAS;GAAO;EACjH;CACD,OAAO,CAAC;EAAE,MAAM;EAAiB,SAAS;EAAe,WAAW;EAAG,CAAC;CACxE,MAAM,IAAI,MAAM;AACd,SAAO,iBAAiB;GACtB,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,YAAY,KAAK;GACjB,YAAY,KAAK;GACjB,mBAAmB,KAAK;GACzB,CAAC;;CAEL,CAAC"}
1
+ {"version":3,"file":"remove-orphans-BaKEISxA.js","names":[],"sources":["../src/commands/remove-orphans.ts"],"sourcesContent":["import { createCommand } from './_shared.js'\nimport { removeOrphanKeys } from '../core/operations.js'\n\nexport default createCommand({\n name: 'remove-orphans',\n description: 'Find and remove orphan translation keys not referenced in source code',\n args: {\n layer: { type: 'string', description: 'Filter to a specific layer' },\n locale: { type: 'string', description: 'Locale to check (default: project default)' },\n dryRun: { type: 'boolean', description: 'Preview without removing (default: true)', default: true },\n outputFile: { type: 'string', description: 'Write full output to this file path and return only a summary (useful for large outputs)' },\n codequalityOutput: { type: 'string', description: 'Also write the orphan findings as a GitLab Code Quality (CodeClimate) JSON report to this file path' },\n failOnOrphans: { type: 'boolean', description: 'Exit 2 when any orphan key is found (CI gate)', default: false },\n },\n gates: [{ flag: 'failOnOrphans', counter: 'orphanCount', threshold: 0 }],\n async run(args) {\n return removeOrphanKeys({\n layer: args.layer,\n locale: args.locale,\n dryRun: args.dryRun,\n projectDir: args.projectDir,\n outputFile: args.outputFile,\n codequalityOutput: args.codequalityOutput,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,yBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8B;EACpE,QAAQ;GAAE,MAAM;GAAU,aAAa;GAA8C;EACrF,QAAQ;GAAE,MAAM;GAAW,aAAa;GAA4C,SAAS;GAAM;EACnG,YAAY;GAAE,MAAM;GAAU,aAAa;GAA4F;EACvI,mBAAmB;GAAE,MAAM;GAAU,aAAa;GAAuG;EACzJ,eAAe;GAAE,MAAM;GAAW,aAAa;GAAiD,SAAS;GAAO;EACjH;CACD,OAAO,CAAC;EAAE,MAAM;EAAiB,SAAS;EAAe,WAAW;EAAG,CAAC;CACxE,MAAM,IAAI,MAAM;AACd,SAAO,iBAAiB;GACtB,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,YAAY,KAAK;GACjB,YAAY,KAAK;GACjB,mBAAmB,KAAK;GACzB,CAAC;;CAEL,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { d as renameTranslationKey } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { d as renameTranslationKey } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/rename.ts
7
7
  var rename_default = createCommand({
8
8
  name: "rename",
@@ -42,4 +42,4 @@ var rename_default = createCommand({
42
42
  //#endregion
43
43
  export { rename_default as default };
44
44
 
45
- //# sourceMappingURL=rename-C_rFKkU4.js.map
45
+ //# sourceMappingURL=rename-CN2ajUeb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rename-C_rFKkU4.js","names":[],"sources":["../src/commands/rename.ts"],"sourcesContent":["import { createCommand } from './_shared.js'\nimport { renameTranslationKey } from '../core/operations.js'\n\nexport default createCommand({\n name: 'rename',\n description: 'Rename/move a translation key across all locale files',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n oldKey: { type: 'string', description: 'Current key path', required: true },\n newKey: { type: 'string', description: 'New key path', required: true },\n dryRun: { type: 'boolean', description: 'Preview changes without writing', default: false },\n },\n async run(args) {\n return renameTranslationKey({\n layer: args.layer,\n oldKey: args.oldKey,\n newKey: args.newKey,\n dryRun: args.dryRun,\n projectDir: args.projectDir,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,QAAQ;GAAE,MAAM;GAAU,aAAa;GAAoB,UAAU;GAAM;EAC3E,QAAQ;GAAE,MAAM;GAAU,aAAa;GAAgB,UAAU;GAAM;EACvE,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAmC,SAAS;GAAO;EAC5F;CACD,MAAM,IAAI,MAAM;AACd,SAAO,qBAAqB;GAC1B,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,YAAY,KAAK;GAClB,CAAC;;CAEL,CAAC"}
1
+ {"version":3,"file":"rename-CN2ajUeb.js","names":[],"sources":["../src/commands/rename.ts"],"sourcesContent":["import { createCommand } from './_shared.js'\nimport { renameTranslationKey } from '../core/operations.js'\n\nexport default createCommand({\n name: 'rename',\n description: 'Rename/move a translation key across all locale files',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n oldKey: { type: 'string', description: 'Current key path', required: true },\n newKey: { type: 'string', description: 'New key path', required: true },\n dryRun: { type: 'boolean', description: 'Preview changes without writing', default: false },\n },\n async run(args) {\n return renameTranslationKey({\n layer: args.layer,\n oldKey: args.oldKey,\n newKey: args.newKey,\n dryRun: args.dryRun,\n projectDir: args.projectDir,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,QAAQ;GAAE,MAAM;GAAU,aAAa;GAAoB,UAAU;GAAM;EAC3E,QAAQ;GAAE,MAAM;GAAU,aAAa;GAAgB,UAAU;GAAM;EACvE,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAmC,SAAS;GAAO;EAC5F;CACD,MAAM,IAAI,MAAM;AACd,SAAO,qBAAqB;GAC1B,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,YAAY,KAAK;GAClB,CAAC;;CAEL,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { f as scaffoldLocaleFiles } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { f as scaffoldLocaleFiles } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { o as splitList, t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { o as splitList, t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/scaffold.ts
7
7
  var scaffold_default = createCommand({
8
8
  name: "scaffold",
@@ -36,4 +36,4 @@ var scaffold_default = createCommand({
36
36
  //#endregion
37
37
  export { scaffold_default as default };
38
38
 
39
- //# sourceMappingURL=scaffold-CUimlNqR.js.map
39
+ //# sourceMappingURL=scaffold-CUKUFuEy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold-CUimlNqR.js","names":[],"sources":["../src/commands/scaffold.ts"],"sourcesContent":["import { createCommand, splitList } from './_shared.js'\nimport { scaffoldLocaleFiles } from '../core/operations.js'\n\nexport default createCommand({\n name: 'scaffold',\n description: 'Create empty locale files for new languages',\n args: {\n locales: { type: 'string', description: 'Comma-separated locale codes to scaffold' },\n layer: { type: 'string', description: 'Filter to a specific layer' },\n dryRun: { type: 'boolean', description: 'Preview without writing', default: false },\n },\n async run(args) {\n const locales = splitList(args.locales)\n if (locales && locales.length === 0) {\n throw new Error('No locales provided. Pass comma-separated locale codes via --locales')\n }\n return scaffoldLocaleFiles({ locales, layer: args.layer, dryRun: args.dryRun, projectDir: args.projectDir })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,mBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,SAAS;GAAE,MAAM;GAAU,aAAa;GAA4C;EACpF,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8B;EACpE,QAAQ;GAAE,MAAM;GAAW,aAAa;GAA2B,SAAS;GAAO;EACpF;CACD,MAAM,IAAI,MAAM;EACd,MAAM,UAAU,UAAU,KAAK,QAAQ;AACvC,MAAI,WAAW,QAAQ,WAAW,EAChC,OAAM,IAAI,MAAM,uEAAuE;AAEzF,SAAO,oBAAoB;GAAE;GAAS,OAAO,KAAK;GAAO,QAAQ,KAAK;GAAQ,YAAY,KAAK;GAAY,CAAC;;CAE/G,CAAC"}
1
+ {"version":3,"file":"scaffold-CUKUFuEy.js","names":[],"sources":["../src/commands/scaffold.ts"],"sourcesContent":["import { createCommand, splitList } from './_shared.js'\nimport { scaffoldLocaleFiles } from '../core/operations.js'\n\nexport default createCommand({\n name: 'scaffold',\n description: 'Create empty locale files for new languages',\n args: {\n locales: { type: 'string', description: 'Comma-separated locale codes to scaffold' },\n layer: { type: 'string', description: 'Filter to a specific layer' },\n dryRun: { type: 'boolean', description: 'Preview without writing', default: false },\n },\n async run(args) {\n const locales = splitList(args.locales)\n if (locales && locales.length === 0) {\n throw new Error('No locales provided. Pass comma-separated locale codes via --locales')\n }\n return scaffoldLocaleFiles({ locales, layer: args.layer, dryRun: args.dryRun, projectDir: args.projectDir })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,mBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,SAAS;GAAE,MAAM;GAAU,aAAa;GAA4C;EACpF,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8B;EACpE,QAAQ;GAAE,MAAM;GAAW,aAAa;GAA2B,SAAS;GAAO;EACpF;CACD,MAAM,IAAI,MAAM;EACd,MAAM,UAAU,UAAU,KAAK,QAAQ;AACvC,MAAI,WAAW,QAAQ,WAAW,EAChC,OAAM,IAAI,MAAM,uEAAuE;AAEzF,SAAO,oBAAoB;GAAE;GAAS,OAAO,KAAK;GAAO,QAAQ,KAAK;GAAQ,YAAY,KAAK;GAAY,CAAC;;CAE/G,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { a as scanCodeUsage } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { a as scanCodeUsage } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { o as splitList, t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { o as splitList, t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/scan.ts
7
7
  var scan_default = createCommand({
8
8
  name: "scan",
@@ -28,4 +28,4 @@ var scan_default = createCommand({
28
28
  //#endregion
29
29
  export { scan_default as default };
30
30
 
31
- //# sourceMappingURL=scan-BkOIYhjZ.js.map
31
+ //# sourceMappingURL=scan-Bhv0Nmy3.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scan-BkOIYhjZ.js","names":[],"sources":["../src/commands/scan.ts"],"sourcesContent":["import { createCommand, splitList } from './_shared.js'\nimport { scanCodeUsage } from '../core/operations.js'\n\nexport default createCommand({\n name: 'scan',\n description: 'Scan source code for translation key usage (file paths + line numbers)',\n args: {\n keys: { type: 'string', description: 'Comma-separated keys to filter by' },\n outputFile: { type: 'string', description: 'Write full output to file, return summary only' },\n },\n async run(args) {\n return scanCodeUsage({\n keys: splitList(args.keys),\n projectDir: args.projectDir,\n outputFile: args.outputFile,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,eAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,MAAM;GAAE,MAAM;GAAU,aAAa;GAAqC;EAC1E,YAAY;GAAE,MAAM;GAAU,aAAa;GAAkD;EAC9F;CACD,MAAM,IAAI,MAAM;AACd,SAAO,cAAc;GACnB,MAAM,UAAU,KAAK,KAAK;GAC1B,YAAY,KAAK;GACjB,YAAY,KAAK;GAClB,CAAC;;CAEL,CAAC"}
1
+ {"version":3,"file":"scan-Bhv0Nmy3.js","names":[],"sources":["../src/commands/scan.ts"],"sourcesContent":["import { createCommand, splitList } from './_shared.js'\nimport { scanCodeUsage } from '../core/operations.js'\n\nexport default createCommand({\n name: 'scan',\n description: 'Scan source code for translation key usage (file paths + line numbers)',\n args: {\n keys: { type: 'string', description: 'Comma-separated keys to filter by' },\n outputFile: { type: 'string', description: 'Write full output to file, return summary only' },\n },\n async run(args) {\n return scanCodeUsage({\n keys: splitList(args.keys),\n projectDir: args.projectDir,\n outputFile: args.outputFile,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,eAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,MAAM;GAAE,MAAM;GAAU,aAAa;GAAqC;EAC1E,YAAY;GAAE,MAAM;GAAU,aAAa;GAAkD;EAC9F;CACD,MAAM,IAAI,MAAM;AACd,SAAO,cAAc;GACnB,MAAM,UAAU,KAAK,KAAK;GAC1B,YAAY,KAAK;GACjB,YAAY,KAAK;GAClB,CAAC;;CAEL,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { k as searchTranslations } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { k as searchTranslations } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/search.ts
7
7
  var search_default = createCommand({
8
8
  name: "search",
@@ -46,4 +46,4 @@ var search_default = createCommand({
46
46
  //#endregion
47
47
  export { search_default as default };
48
48
 
49
- //# sourceMappingURL=search-0io1BcsY.js.map
49
+ //# sourceMappingURL=search-CrkLgz3m.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-0io1BcsY.js","names":[],"sources":["../src/commands/search.ts"],"sourcesContent":["import { createCommand } from './_shared.js'\nimport { searchTranslations } from '../core/operations.js'\n\nexport default createCommand({\n name: 'search',\n description: 'Search translation files by key or value',\n args: {\n query: { type: 'string', description: 'Search query (matched case-insensitively against keys and values)', required: true },\n in: { type: 'string', description: 'Where to search: keys, values, or both', default: 'both' },\n layer: { type: 'string', description: 'Filter to a specific layer' },\n locale: { type: 'string', description: 'Filter to a specific locale' },\n },\n async run(args) {\n const validSearchIn = ['keys', 'values', 'both'] as const\n const searchIn = validSearchIn.includes(args.in as typeof validSearchIn[number])\n ? (args.in as 'keys' | 'values' | 'both')\n : undefined\n if (args.in && !searchIn) {\n throw new Error(`Invalid --in value: \"${args.in}\". Must be one of: keys, values, both`)\n }\n return searchTranslations({\n query: args.query,\n searchIn,\n layer: args.layer,\n locale: args.locale,\n projectDir: args.projectDir,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAqE,UAAU;GAAM;EAC3H,IAAI;GAAE,MAAM;GAAU,aAAa;GAA0C,SAAS;GAAQ;EAC9F,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8B;EACpE,QAAQ;GAAE,MAAM;GAAU,aAAa;GAA+B;EACvE;CACD,MAAM,IAAI,MAAM;EAEd,MAAM,WADgB;GAAC;GAAQ;GAAU;GAAO,CACjB,SAAS,KAAK,GAAmC,GAC3E,KAAK,KACN,KAAA;AACJ,MAAI,KAAK,MAAM,CAAC,SACd,OAAM,IAAI,MAAM,wBAAwB,KAAK,GAAG,uCAAuC;AAEzF,SAAO,mBAAmB;GACxB,OAAO,KAAK;GACZ;GACA,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,YAAY,KAAK;GAClB,CAAC;;CAEL,CAAC"}
1
+ {"version":3,"file":"search-CrkLgz3m.js","names":[],"sources":["../src/commands/search.ts"],"sourcesContent":["import { createCommand } from './_shared.js'\nimport { searchTranslations } from '../core/operations.js'\n\nexport default createCommand({\n name: 'search',\n description: 'Search translation files by key or value',\n args: {\n query: { type: 'string', description: 'Search query (matched case-insensitively against keys and values)', required: true },\n in: { type: 'string', description: 'Where to search: keys, values, or both', default: 'both' },\n layer: { type: 'string', description: 'Filter to a specific layer' },\n locale: { type: 'string', description: 'Filter to a specific locale' },\n },\n async run(args) {\n const validSearchIn = ['keys', 'values', 'both'] as const\n const searchIn = validSearchIn.includes(args.in as typeof validSearchIn[number])\n ? (args.in as 'keys' | 'values' | 'both')\n : undefined\n if (args.in && !searchIn) {\n throw new Error(`Invalid --in value: \"${args.in}\". Must be one of: keys, values, both`)\n }\n return searchTranslations({\n query: args.query,\n searchIn,\n layer: args.layer,\n locale: args.locale,\n projectDir: args.projectDir,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAqE,UAAU;GAAM;EAC3H,IAAI;GAAE,MAAM;GAAU,aAAa;GAA0C,SAAS;GAAQ;EAC9F,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8B;EACpE,QAAQ;GAAE,MAAM;GAAU,aAAa;GAA+B;EACvE;CACD,MAAM,IAAI,MAAM;EAEd,MAAM,WADgB;GAAC;GAAQ;GAAU;GAAO,CACjB,SAAS,KAAK,GAAmC,GAC3E,KAAK,KACN,KAAA;AACJ,MAAI,KAAK,MAAM,CAAC,SACd,OAAM,IAAI,MAAM,wBAAwB,KAAK,GAAG,uCAAuC;AAEzF,SAAO,mBAAmB;GACxB,OAAO,KAAK;GACZ;GACA,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,YAAY,KAAK;GAClB,CAAC;;CAEL,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { o as getTranslationStatus } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { o as getTranslationStatus } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/status.ts
7
7
  var status_default = createCommand({
8
8
  name: "status",
@@ -42,4 +42,4 @@ var status_default = createCommand({
42
42
  //#endregion
43
43
  export { status_default as default };
44
44
 
45
- //# sourceMappingURL=status-Bp-SzjTN.js.map
45
+ //# sourceMappingURL=status-DprS-qDC.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"status-Bp-SzjTN.js","names":[],"sources":["../src/commands/status.ts"],"sourcesContent":["import { createCommand } from './_shared.js'\nimport { getTranslationStatus } from '../core/operations.js'\n\nexport default createCommand({\n name: 'status',\n description: 'Translation coverage per locale and per layer, with an overall completion percentage',\n args: {\n layer: { type: 'string', description: 'Filter to a specific layer' },\n ref: { type: 'string', description: 'Reference locale (default: project default)' },\n outputFile: { type: 'string', description: 'Write the full breakdown to this file path and return only a summary' },\n failUnder: { type: 'string', description: 'Exit 2 when overall completion is below this percentage (CI gate)' },\n },\n // Threshold comes from the flag's own value; `direction: below` is what\n // makes this a floor rather than a ceiling (see resolveExitCode).\n gates: [{ flag: 'failUnder', counter: 'completionPercent', direction: 'below' }],\n async run(args) {\n return getTranslationStatus({\n layer: args.layer,\n referenceLocale: args.ref,\n projectDir: args.projectDir,\n outputFile: args.outputFile,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8B;EACpE,KAAK;GAAE,MAAM;GAAU,aAAa;GAA+C;EACnF,YAAY;GAAE,MAAM;GAAU,aAAa;GAAwE;EACnH,WAAW;GAAE,MAAM;GAAU,aAAa;GAAqE;EAChH;CAGD,OAAO,CAAC;EAAE,MAAM;EAAa,SAAS;EAAqB,WAAW;EAAS,CAAC;CAChF,MAAM,IAAI,MAAM;AACd,SAAO,qBAAqB;GAC1B,OAAO,KAAK;GACZ,iBAAiB,KAAK;GACtB,YAAY,KAAK;GACjB,YAAY,KAAK;GAClB,CAAC;;CAEL,CAAC"}
1
+ {"version":3,"file":"status-DprS-qDC.js","names":[],"sources":["../src/commands/status.ts"],"sourcesContent":["import { createCommand } from './_shared.js'\nimport { getTranslationStatus } from '../core/operations.js'\n\nexport default createCommand({\n name: 'status',\n description: 'Translation coverage per locale and per layer, with an overall completion percentage',\n args: {\n layer: { type: 'string', description: 'Filter to a specific layer' },\n ref: { type: 'string', description: 'Reference locale (default: project default)' },\n outputFile: { type: 'string', description: 'Write the full breakdown to this file path and return only a summary' },\n failUnder: { type: 'string', description: 'Exit 2 when overall completion is below this percentage (CI gate)' },\n },\n // Threshold comes from the flag's own value; `direction: below` is what\n // makes this a floor rather than a ceiling (see resolveExitCode).\n gates: [{ flag: 'failUnder', counter: 'completionPercent', direction: 'below' }],\n async run(args) {\n return getTranslationStatus({\n layer: args.layer,\n referenceLocale: args.ref,\n projectDir: args.projectDir,\n outputFile: args.outputFile,\n })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8B;EACpE,KAAK;GAAE,MAAM;GAAU,aAAa;GAA+C;EACnF,YAAY;GAAE,MAAM;GAAU,aAAa;GAAwE;EACnH,WAAW;GAAE,MAAM;GAAU,aAAa;GAAqE;EAChH;CAGD,OAAO,CAAC;EAAE,MAAM;EAAa,SAAS;EAAqB,WAAW;EAAS,CAAC;CAChF,MAAM,IAAI,MAAM;AACd,SAAO,qBAAqB;GAC1B,OAAO,KAAK;GACZ,iBAAiB,KAAK;GACtB,YAAY,KAAK;GACjB,YAAY,KAAK;GAClB,CAAC;;CAEL,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { M as translateMissing } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { M as translateMissing } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { a as resolveProviderTranslateFn, i as providerArgs, o as splitList, t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { a as resolveProviderTranslateFn, i as providerArgs, o as splitList, t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/translate.ts
7
7
  /**
8
8
  * Locales that lost at least one key, sorted for a stable message.
@@ -89,4 +89,4 @@ var translate_default = createCommand({
89
89
  //#endregion
90
90
  export { translate_default as default, localesWithFailures };
91
91
 
92
- //# sourceMappingURL=translate-cCb2Z57w.js.map
92
+ //# sourceMappingURL=translate-Cs6EdbzA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"translate-cCb2Z57w.js","names":[],"sources":["../src/commands/translate.ts"],"sourcesContent":["import { createCommand, splitList, providerArgs, resolveProviderTranslateFn } from './_shared.js'\nimport { translateMissing } from '../core/operations.js'\nimport type { TranslateMissingOutcome, TranslateMissingResult } from '../core/types.js'\n\n/**\n * Locales that lost at least one key, sorted for a stable message.\n * A bare `totalFailed: 141` says nothing about which languages shipped\n * incomplete — this is what makes the count actionable in a CI log.\n * Reads the full and the compact shapes, since either may reach here.\n */\nexport function localesWithFailures(result: TranslateMissingOutcome): string[] {\n const perLayer: TranslateMissingResult[] = 'layers' in result\n ? Object.values(result.layers)\n : [result]\n\n const locales = new Set<string>()\n for (const layer of perLayer) {\n for (const [locale, entry] of Object.entries(layer.results ?? {})) {\n if (entry.failed.length > 0) locales.add(locale)\n }\n for (const entry of layer.summary.byLocale ?? []) {\n if (entry.failed > 0) locales.add(entry.locale)\n }\n }\n return [...locales].sort()\n}\n\nexport default createCommand({\n name: 'translate',\n description: 'Find missing translations and translate them via LLM. Requires --provider and --model for auto-translation.',\n args: {\n layer: { type: 'string', description: 'Layer name (default: all locale-backed layers, aggregated)' },\n ref: { type: 'string', description: 'Reference locale (default: project default)' },\n targets: { type: 'string', description: 'Comma-separated target locales (default: all except ref)' },\n keys: { type: 'string', description: 'Comma-separated keys to translate (default: all missing)' },\n batchSize: { type: 'string', description: 'Batch size (default: 50)' },\n ...providerArgs,\n dryRun: { type: 'boolean', description: 'Preview what would be translated', default: false },\n failOnFailed: { type: 'boolean', description: 'Exit 2 when any key failed to translate (CI gate)', default: false },\n },\n // Without this gate a partly failed run is indistinguishable from a clean one:\n // isTotalFailure only reports exit 1 when NOTHING was translated, so a run that\n // wrote 795 keys and lost 141 exits 0 and its partial result gets committed.\n // Opt-in rather than default, because providers fail transiently and a red\n // pipeline on every flake is a red pipeline nobody reads.\n gates: [{ flag: 'failOnFailed', counter: 'totalFailed', threshold: 0 }],\n async run(args) {\n let batchSize: number | undefined\n if (args.batchSize) {\n const num = Number(args.batchSize)\n if (!Number.isInteger(num) || num <= 0 || String(num) !== args.batchSize) {\n throw new Error(`Invalid --batchSize: \"${args.batchSize}\". Must be a positive integer`)\n }\n batchSize = num\n }\n\n const translateFn = await resolveProviderTranslateFn(args)\n\n const result = await translateMissing({\n layer: args.layer,\n referenceLocale: args.ref,\n targetLocales: splitList(args.targets),\n keys: splitList(args.keys),\n batchSize,\n dryRun: args.dryRun,\n projectDir: args.projectDir,\n translateFn,\n })\n\n // CLI-owned guidance: agent mode here just means no provider was given.\n // Single-layer and all-layers results both carry summary.mode, so this\n // needs no narrowing.\n if (result.summary.mode === 'agent') {\n result.summary.message = 'No provider configured — nothing was translated. Pass --provider and --model '\n + '(API key via --apiKey or the OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY env vars) '\n + 'to translate automatically.'\n } else if (result.summary.totalFailed > 0) {\n // A partial failure exits 0 unless --fail-on-failed was passed, so without\n // this the only trace of it is a count nobody reads. Re-running is the\n // fix: the keys are still missing, so the next run retries them.\n const affected = localesWithFailures(result)\n result.summary.message = `${result.summary.totalFailed} key(s) failed to translate`\n + (affected.length > 0 ? ` (locales: ${affected.join(', ')})` : '')\n + '. Those keys remain missing — re-run to retry them. '\n + 'Pass --fail-on-failed to make this exit 2 in CI.'\n }\n return result\n },\n})\n"],"mappings":";;;;;;;;;;;;AAUA,SAAgB,oBAAoB,QAA2C;CAC7E,MAAM,WAAqC,YAAY,SACnD,OAAO,OAAO,OAAO,OAAO,GAC5B,CAAC,OAAO;CAEZ,MAAM,0BAAU,IAAI,KAAa;AACjC,MAAK,MAAM,SAAS,UAAU;AAC5B,OAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,MAAM,WAAW,EAAE,CAAC,CAC/D,KAAI,MAAM,OAAO,SAAS,EAAG,SAAQ,IAAI,OAAO;AAElD,OAAK,MAAM,SAAS,MAAM,QAAQ,YAAY,EAAE,CAC9C,KAAI,MAAM,SAAS,EAAG,SAAQ,IAAI,MAAM,OAAO;;AAGnD,QAAO,CAAC,GAAG,QAAQ,CAAC,MAAM;;AAG5B,IAAA,oBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8D;EACpG,KAAK;GAAE,MAAM;GAAU,aAAa;GAA+C;EACnF,SAAS;GAAE,MAAM;GAAU,aAAa;GAA4D;EACpG,MAAM;GAAE,MAAM;GAAU,aAAa;GAA4D;EACjG,WAAW;GAAE,MAAM;GAAU,aAAa;GAA4B;EACtE,GAAG;EACH,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAoC,SAAS;GAAO;EAC5F,cAAc;GAAE,MAAM;GAAW,aAAa;GAAqD,SAAS;GAAO;EACpH;CAMD,OAAO,CAAC;EAAE,MAAM;EAAgB,SAAS;EAAe,WAAW;EAAG,CAAC;CACvE,MAAM,IAAI,MAAM;EACd,IAAI;AACJ,MAAI,KAAK,WAAW;GAClB,MAAM,MAAM,OAAO,KAAK,UAAU;AAClC,OAAI,CAAC,OAAO,UAAU,IAAI,IAAI,OAAO,KAAK,OAAO,IAAI,KAAK,KAAK,UAC7D,OAAM,IAAI,MAAM,yBAAyB,KAAK,UAAU,+BAA+B;AAEzF,eAAY;;EAGd,MAAM,cAAc,MAAM,2BAA2B,KAAK;EAE1D,MAAM,SAAS,MAAM,iBAAiB;GACpC,OAAO,KAAK;GACZ,iBAAiB,KAAK;GACtB,eAAe,UAAU,KAAK,QAAQ;GACtC,MAAM,UAAU,KAAK,KAAK;GAC1B;GACA,QAAQ,KAAK;GACb,YAAY,KAAK;GACjB;GACD,CAAC;AAKF,MAAI,OAAO,QAAQ,SAAS,QAC1B,QAAO,QAAQ,UAAU;WAGhB,OAAO,QAAQ,cAAc,GAAG;GAIzC,MAAM,WAAW,oBAAoB,OAAO;AAC5C,UAAO,QAAQ,UAAU,GAAG,OAAO,QAAQ,YAAY,gCAClD,SAAS,SAAS,IAAI,cAAc,SAAS,KAAK,KAAK,CAAC,KAAK,MAC9D;;AAGN,SAAO;;CAEV,CAAC"}
1
+ {"version":3,"file":"translate-Cs6EdbzA.js","names":[],"sources":["../src/commands/translate.ts"],"sourcesContent":["import { createCommand, splitList, providerArgs, resolveProviderTranslateFn } from './_shared.js'\nimport { translateMissing } from '../core/operations.js'\nimport type { TranslateMissingOutcome, TranslateMissingResult } from '../core/types.js'\n\n/**\n * Locales that lost at least one key, sorted for a stable message.\n * A bare `totalFailed: 141` says nothing about which languages shipped\n * incomplete — this is what makes the count actionable in a CI log.\n * Reads the full and the compact shapes, since either may reach here.\n */\nexport function localesWithFailures(result: TranslateMissingOutcome): string[] {\n const perLayer: TranslateMissingResult[] = 'layers' in result\n ? Object.values(result.layers)\n : [result]\n\n const locales = new Set<string>()\n for (const layer of perLayer) {\n for (const [locale, entry] of Object.entries(layer.results ?? {})) {\n if (entry.failed.length > 0) locales.add(locale)\n }\n for (const entry of layer.summary.byLocale ?? []) {\n if (entry.failed > 0) locales.add(entry.locale)\n }\n }\n return [...locales].sort()\n}\n\nexport default createCommand({\n name: 'translate',\n description: 'Find missing translations and translate them via LLM. Requires --provider and --model for auto-translation.',\n args: {\n layer: { type: 'string', description: 'Layer name (default: all locale-backed layers, aggregated)' },\n ref: { type: 'string', description: 'Reference locale (default: project default)' },\n targets: { type: 'string', description: 'Comma-separated target locales (default: all except ref)' },\n keys: { type: 'string', description: 'Comma-separated keys to translate (default: all missing)' },\n batchSize: { type: 'string', description: 'Batch size (default: 50)' },\n ...providerArgs,\n dryRun: { type: 'boolean', description: 'Preview what would be translated', default: false },\n failOnFailed: { type: 'boolean', description: 'Exit 2 when any key failed to translate (CI gate)', default: false },\n },\n // Without this gate a partly failed run is indistinguishable from a clean one:\n // isTotalFailure only reports exit 1 when NOTHING was translated, so a run that\n // wrote 795 keys and lost 141 exits 0 and its partial result gets committed.\n // Opt-in rather than default, because providers fail transiently and a red\n // pipeline on every flake is a red pipeline nobody reads.\n gates: [{ flag: 'failOnFailed', counter: 'totalFailed', threshold: 0 }],\n async run(args) {\n let batchSize: number | undefined\n if (args.batchSize) {\n const num = Number(args.batchSize)\n if (!Number.isInteger(num) || num <= 0 || String(num) !== args.batchSize) {\n throw new Error(`Invalid --batchSize: \"${args.batchSize}\". Must be a positive integer`)\n }\n batchSize = num\n }\n\n const translateFn = await resolveProviderTranslateFn(args)\n\n const result = await translateMissing({\n layer: args.layer,\n referenceLocale: args.ref,\n targetLocales: splitList(args.targets),\n keys: splitList(args.keys),\n batchSize,\n dryRun: args.dryRun,\n projectDir: args.projectDir,\n translateFn,\n })\n\n // CLI-owned guidance: agent mode here just means no provider was given.\n // Single-layer and all-layers results both carry summary.mode, so this\n // needs no narrowing.\n if (result.summary.mode === 'agent') {\n result.summary.message = 'No provider configured — nothing was translated. Pass --provider and --model '\n + '(API key via --apiKey or the OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY env vars) '\n + 'to translate automatically.'\n } else if (result.summary.totalFailed > 0) {\n // A partial failure exits 0 unless --fail-on-failed was passed, so without\n // this the only trace of it is a count nobody reads. Re-running is the\n // fix: the keys are still missing, so the next run retries them.\n const affected = localesWithFailures(result)\n result.summary.message = `${result.summary.totalFailed} key(s) failed to translate`\n + (affected.length > 0 ? ` (locales: ${affected.join(', ')})` : '')\n + '. Those keys remain missing — re-run to retry them. '\n + 'Pass --fail-on-failed to make this exit 2 in CI.'\n }\n return result\n },\n})\n"],"mappings":";;;;;;;;;;;;AAUA,SAAgB,oBAAoB,QAA2C;CAC7E,MAAM,WAAqC,YAAY,SACnD,OAAO,OAAO,OAAO,OAAO,GAC5B,CAAC,OAAO;CAEZ,MAAM,0BAAU,IAAI,KAAa;AACjC,MAAK,MAAM,SAAS,UAAU;AAC5B,OAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,MAAM,WAAW,EAAE,CAAC,CAC/D,KAAI,MAAM,OAAO,SAAS,EAAG,SAAQ,IAAI,OAAO;AAElD,OAAK,MAAM,SAAS,MAAM,QAAQ,YAAY,EAAE,CAC9C,KAAI,MAAM,SAAS,EAAG,SAAQ,IAAI,MAAM,OAAO;;AAGnD,QAAO,CAAC,GAAG,QAAQ,CAAC,MAAM;;AAG5B,IAAA,oBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAA8D;EACpG,KAAK;GAAE,MAAM;GAAU,aAAa;GAA+C;EACnF,SAAS;GAAE,MAAM;GAAU,aAAa;GAA4D;EACpG,MAAM;GAAE,MAAM;GAAU,aAAa;GAA4D;EACjG,WAAW;GAAE,MAAM;GAAU,aAAa;GAA4B;EACtE,GAAG;EACH,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAoC,SAAS;GAAO;EAC5F,cAAc;GAAE,MAAM;GAAW,aAAa;GAAqD,SAAS;GAAO;EACpH;CAMD,OAAO,CAAC;EAAE,MAAM;EAAgB,SAAS;EAAe,WAAW;EAAG,CAAC;CACvE,MAAM,IAAI,MAAM;EACd,IAAI;AACJ,MAAI,KAAK,WAAW;GAClB,MAAM,MAAM,OAAO,KAAK,UAAU;AAClC,OAAI,CAAC,OAAO,UAAU,IAAI,IAAI,OAAO,KAAK,OAAO,IAAI,KAAK,KAAK,UAC7D,OAAM,IAAI,MAAM,yBAAyB,KAAK,UAAU,+BAA+B;AAEzF,eAAY;;EAGd,MAAM,cAAc,MAAM,2BAA2B,KAAK;EAE1D,MAAM,SAAS,MAAM,iBAAiB;GACpC,OAAO,KAAK;GACZ,iBAAiB,KAAK;GACtB,eAAe,UAAU,KAAK,QAAQ;GACtC,MAAM,UAAU,KAAK,KAAK;GAC1B;GACA,QAAQ,KAAK;GACb,YAAY,KAAK;GACjB;GACD,CAAC;AAKF,MAAI,OAAO,QAAQ,SAAS,QAC1B,QAAO,QAAQ,UAAU;WAGhB,OAAO,QAAQ,cAAc,GAAG;GAIzC,MAAM,WAAW,oBAAoB,OAAO;AAC5C,UAAO,QAAQ,UAAU,GAAG,OAAO,QAAQ,YAAY,gCAClD,SAAS,SAAS,IAAI,cAAc,SAAS,KAAK,KAAK,CAAC,KAAK,MAC9D;;AAGN,SAAO;;CAEV,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { j as translateKey } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { j as translateKey } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { a as resolveProviderTranslateFn, i as providerArgs, o as splitList, t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { a as resolveProviderTranslateFn, i as providerArgs, o as splitList, t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/translate-key.ts
7
7
  var translate_key_default = createCommand({
8
8
  name: "translate-key",
@@ -70,4 +70,4 @@ var translate_key_default = createCommand({
70
70
  //#endregion
71
71
  export { translate_key_default as default };
72
72
 
73
- //# sourceMappingURL=translate-key-DHLWEmrA.js.map
73
+ //# sourceMappingURL=translate-key-BBZpYQjL.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"translate-key-DHLWEmrA.js","names":[],"sources":["../src/commands/translate-key.ts"],"sourcesContent":["import { createCommand, splitList, providerArgs, resolveProviderTranslateFn } from './_shared.js'\nimport { translateKey } from '../core/operations.js'\n\nexport default createCommand({\n name: 'translate-key',\n description: 'Translate a single key from a source locale into target locales. Supports LLM translation with --provider.',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n key: { type: 'string', description: 'Dot-separated translation key', required: true },\n sourceLocale: { type: 'string', description: 'Source locale code/language/file', required: true },\n sourceValue: { type: 'string', description: 'Optional source value to write before translating' },\n targets: { type: 'string', description: 'Comma-separated target locales, or \"all\"/omitted for all except source' },\n overwrite: { type: 'boolean', description: 'Overwrite existing target translations (default true)', default: true },\n dryRun: { type: 'boolean', description: 'Preview without writing or translating', default: false },\n includePreview: { type: 'boolean', description: 'Include translated values in output', default: false },\n ...providerArgs,\n },\n async run(args) {\n const targetLocales = args.targets === 'all' ? 'all' : splitList(args.targets)\n\n const translateFn = await resolveProviderTranslateFn(args)\n\n const result = await translateKey({\n layer: args.layer,\n key: args.key,\n sourceLocale: args.sourceLocale,\n sourceValue: args.sourceValue,\n targetLocales,\n overwrite: args.overwrite,\n dryRun: args.dryRun,\n includePreview: args.includePreview,\n projectDir: args.projectDir,\n translateFn,\n })\n\n // CLI-owned guidance: agent mode here just means no provider was given\n if (result.mode === 'agent' && result.skipped.some(s => s.reason === 'no-provider')) {\n result.message = 'No provider configured — nothing was translated. Pass --provider and --model '\n + '(API key via --apiKey or the OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY env vars) '\n + 'to translate automatically.'\n }\n return result\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,wBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,KAAK;GAAE,MAAM;GAAU,aAAa;GAAiC,UAAU;GAAM;EACrF,cAAc;GAAE,MAAM;GAAU,aAAa;GAAoC,UAAU;GAAM;EACjG,aAAa;GAAE,MAAM;GAAU,aAAa;GAAqD;EACjG,SAAS;GAAE,MAAM;GAAU,aAAa;GAA0E;EAClH,WAAW;GAAE,MAAM;GAAW,aAAa;GAAyD,SAAS;GAAM;EACnH,QAAQ;GAAE,MAAM;GAAW,aAAa;GAA0C,SAAS;GAAO;EAClG,gBAAgB;GAAE,MAAM;GAAW,aAAa;GAAuC,SAAS;GAAO;EACvG,GAAG;EACJ;CACD,MAAM,IAAI,MAAM;EACd,MAAM,gBAAgB,KAAK,YAAY,QAAQ,QAAQ,UAAU,KAAK,QAAQ;EAE9E,MAAM,cAAc,MAAM,2BAA2B,KAAK;EAE1D,MAAM,SAAS,MAAM,aAAa;GAChC,OAAO,KAAK;GACZ,KAAK,KAAK;GACV,cAAc,KAAK;GACnB,aAAa,KAAK;GAClB;GACA,WAAW,KAAK;GAChB,QAAQ,KAAK;GACb,gBAAgB,KAAK;GACrB,YAAY,KAAK;GACjB;GACD,CAAC;AAGF,MAAI,OAAO,SAAS,WAAW,OAAO,QAAQ,MAAK,MAAK,EAAE,WAAW,cAAc,CACjF,QAAO,UAAU;AAInB,SAAO;;CAEV,CAAC"}
1
+ {"version":3,"file":"translate-key-BBZpYQjL.js","names":[],"sources":["../src/commands/translate-key.ts"],"sourcesContent":["import { createCommand, splitList, providerArgs, resolveProviderTranslateFn } from './_shared.js'\nimport { translateKey } from '../core/operations.js'\n\nexport default createCommand({\n name: 'translate-key',\n description: 'Translate a single key from a source locale into target locales. Supports LLM translation with --provider.',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n key: { type: 'string', description: 'Dot-separated translation key', required: true },\n sourceLocale: { type: 'string', description: 'Source locale code/language/file', required: true },\n sourceValue: { type: 'string', description: 'Optional source value to write before translating' },\n targets: { type: 'string', description: 'Comma-separated target locales, or \"all\"/omitted for all except source' },\n overwrite: { type: 'boolean', description: 'Overwrite existing target translations (default true)', default: true },\n dryRun: { type: 'boolean', description: 'Preview without writing or translating', default: false },\n includePreview: { type: 'boolean', description: 'Include translated values in output', default: false },\n ...providerArgs,\n },\n async run(args) {\n const targetLocales = args.targets === 'all' ? 'all' : splitList(args.targets)\n\n const translateFn = await resolveProviderTranslateFn(args)\n\n const result = await translateKey({\n layer: args.layer,\n key: args.key,\n sourceLocale: args.sourceLocale,\n sourceValue: args.sourceValue,\n targetLocales,\n overwrite: args.overwrite,\n dryRun: args.dryRun,\n includePreview: args.includePreview,\n projectDir: args.projectDir,\n translateFn,\n })\n\n // CLI-owned guidance: agent mode here just means no provider was given\n if (result.mode === 'agent' && result.skipped.some(s => s.reason === 'no-provider')) {\n result.message = 'No provider configured — nothing was translated. Pass --provider and --model '\n + '(API key via --apiKey or the OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY env vars) '\n + 'to translate automatically.'\n }\n return result\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,wBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,KAAK;GAAE,MAAM;GAAU,aAAa;GAAiC,UAAU;GAAM;EACrF,cAAc;GAAE,MAAM;GAAU,aAAa;GAAoC,UAAU;GAAM;EACjG,aAAa;GAAE,MAAM;GAAU,aAAa;GAAqD;EACjG,SAAS;GAAE,MAAM;GAAU,aAAa;GAA0E;EAClH,WAAW;GAAE,MAAM;GAAW,aAAa;GAAyD,SAAS;GAAM;EACnH,QAAQ;GAAE,MAAM;GAAW,aAAa;GAA0C,SAAS;GAAO;EAClG,gBAAgB;GAAE,MAAM;GAAW,aAAa;GAAuC,SAAS;GAAO;EACvG,GAAG;EACJ;CACD,MAAM,IAAI,MAAM;EACd,MAAM,gBAAgB,KAAK,YAAY,QAAQ,QAAQ,UAAU,KAAK,QAAQ;EAE9E,MAAM,cAAc,MAAM,2BAA2B,KAAK;EAE1D,MAAM,SAAS,MAAM,aAAa;GAChC,OAAO,KAAK;GACZ,KAAK,KAAK;GACV,cAAc,KAAK;GACnB,aAAa,KAAK;GAClB;GACA,WAAW,KAAK;GAChB,QAAQ,KAAK;GACb,gBAAgB,KAAK;GACrB,YAAY,KAAK;GACjB;GACD,CAAC;AAGF,MAAI,OAAO,SAAS,WAAW,OAAO,QAAQ,MAAK,MAAK,EAAE,WAAW,cAAc,CACjF,QAAO,UAAU;AAInB,SAAO;;CAEV,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { p as updateTranslations } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { p as updateTranslations } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { r as parseJsonArg, t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { r as parseJsonArg, t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/update.ts
7
7
  var update_default = createCommand({
8
8
  name: "update",
@@ -37,4 +37,4 @@ var update_default = createCommand({
37
37
  //#endregion
38
38
  export { update_default as default };
39
39
 
40
- //# sourceMappingURL=update-DV4ijRVB.js.map
40
+ //# sourceMappingURL=update-BQuGxSSy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-DV4ijRVB.js","names":[],"sources":["../src/commands/update.ts"],"sourcesContent":["import { createCommand, parseJsonArg } from './_shared.js'\nimport { updateTranslations } from '../core/operations.js'\n\nexport default createCommand({\n name: 'update',\n description: 'Update existing translation keys (skips keys that do not exist)',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n translations: { type: 'string', description: 'JSON: { \"key\": { \"en\": \"val\", \"de\": \"val\" } }', required: true },\n dryRun: { type: 'boolean', description: 'Preview changes without writing', default: false },\n },\n async run(args) {\n const translations = parseJsonArg<Record<string, Record<string, string>>>(args.translations, 'translations')\n return updateTranslations({ layer: args.layer, translations, dryRun: args.dryRun, projectDir: args.projectDir })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,cAAc;GAAE,MAAM;GAAU,aAAa;GAAiD,UAAU;GAAM;EAC9G,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAmC,SAAS;GAAO;EAC5F;CACD,MAAM,IAAI,MAAM;EACd,MAAM,eAAe,aAAqD,KAAK,cAAc,eAAe;AAC5G,SAAO,mBAAmB;GAAE,OAAO,KAAK;GAAO;GAAc,QAAQ,KAAK;GAAQ,YAAY,KAAK;GAAY,CAAC;;CAEnH,CAAC"}
1
+ {"version":3,"file":"update-BQuGxSSy.js","names":[],"sources":["../src/commands/update.ts"],"sourcesContent":["import { createCommand, parseJsonArg } from './_shared.js'\nimport { updateTranslations } from '../core/operations.js'\n\nexport default createCommand({\n name: 'update',\n description: 'Update existing translation keys (skips keys that do not exist)',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n translations: { type: 'string', description: 'JSON: { \"key\": { \"en\": \"val\", \"de\": \"val\" } }', required: true },\n dryRun: { type: 'boolean', description: 'Preview changes without writing', default: false },\n },\n async run(args) {\n const translations = parseJsonArg<Record<string, Record<string, string>>>(args.translations, 'translations')\n return updateTranslations({ layer: args.layer, translations, dryRun: args.dryRun, projectDir: args.projectDir })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,iBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,cAAc;GAAE,MAAM;GAAU,aAAa;GAAiD,UAAU;GAAM;EAC9G,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAmC,SAAS;GAAO;EAC5F;CACD,MAAM,IAAI,MAAM;EACd,MAAM,eAAe,aAAqD,KAAK,cAAc,eAAe;AAC5G,SAAO,mBAAmB;GAAE,OAAO,KAAK;GAAO;GAAc,QAAQ,KAAK;GAAQ,YAAY,KAAK;GAAY,CAAC;;CAEnH,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import "./stdout-guard-sJHFWVem.js";
2
- import { m as writeTranslations } from "./operations-CTo44gPu.js";
3
- import "./providers-CHE2ffi6.js";
2
+ import { m as writeTranslations } from "./operations-PfRSTepi.js";
3
+ import "./providers-CAsU_aV1.js";
4
4
  import "./php-reader-3Fgw80zK.js";
5
- import { r as parseJsonArg, t as createCommand } from "./_shared-DFyy3sh8.js";
5
+ import { r as parseJsonArg, t as createCommand } from "./_shared-CkMAydRl.js";
6
6
  //#region src/commands/write.ts
7
7
  var write_default = createCommand({
8
8
  name: "write",
@@ -44,4 +44,4 @@ var write_default = createCommand({
44
44
  //#endregion
45
45
  export { write_default as default };
46
46
 
47
- //# sourceMappingURL=write-Czzm_Nb0.js.map
47
+ //# sourceMappingURL=write-B8z6I5Vf.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"write-Czzm_Nb0.js","names":[],"sources":["../src/commands/write.ts"],"sourcesContent":["import { createCommand, parseJsonArg } from './_shared.js'\nimport { writeTranslations } from '../core/operations.js'\n\nexport default createCommand({\n name: 'write',\n description: 'Write translation keys (add/update/upsert). Default mode: upsert.',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n translations: { type: 'string', description: 'JSON: { \"key\": { \"en\": \"val\", \"de\": \"val\" } }', required: true },\n mode: { type: 'string', description: 'Write mode: add | update | upsert (default: upsert)', default: 'upsert' },\n dryRun: { type: 'boolean', description: 'Preview changes without writing', default: false },\n },\n async run(args) {\n const translations = parseJsonArg<Record<string, Record<string, string>>>(args.translations, 'translations')\n const mode = args.mode as 'add' | 'update' | 'upsert'\n return writeTranslations({ layer: args.layer, translations, mode, dryRun: args.dryRun, projectDir: args.projectDir })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,gBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,cAAc;GAAE,MAAM;GAAU,aAAa;GAAiD,UAAU;GAAM;EAC9G,MAAM;GAAE,MAAM;GAAU,aAAa;GAAuD,SAAS;GAAU;EAC/G,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAmC,SAAS;GAAO;EAC5F;CACD,MAAM,IAAI,MAAM;EACd,MAAM,eAAe,aAAqD,KAAK,cAAc,eAAe;EAC5G,MAAM,OAAO,KAAK;AAClB,SAAO,kBAAkB;GAAE,OAAO,KAAK;GAAO;GAAc;GAAM,QAAQ,KAAK;GAAQ,YAAY,KAAK;GAAY,CAAC;;CAExH,CAAC"}
1
+ {"version":3,"file":"write-B8z6I5Vf.js","names":[],"sources":["../src/commands/write.ts"],"sourcesContent":["import { createCommand, parseJsonArg } from './_shared.js'\nimport { writeTranslations } from '../core/operations.js'\n\nexport default createCommand({\n name: 'write',\n description: 'Write translation keys (add/update/upsert). Default mode: upsert.',\n args: {\n layer: { type: 'string', description: 'Layer name', required: true },\n translations: { type: 'string', description: 'JSON: { \"key\": { \"en\": \"val\", \"de\": \"val\" } }', required: true },\n mode: { type: 'string', description: 'Write mode: add | update | upsert (default: upsert)', default: 'upsert' },\n dryRun: { type: 'boolean', description: 'Preview changes without writing', default: false },\n },\n async run(args) {\n const translations = parseJsonArg<Record<string, Record<string, string>>>(args.translations, 'translations')\n const mode = args.mode as 'add' | 'update' | 'upsert'\n return writeTranslations({ layer: args.layer, translations, mode, dryRun: args.dryRun, projectDir: args.projectDir })\n },\n})\n"],"mappings":";;;;;;AAGA,IAAA,gBAAe,cAAc;CAC3B,MAAM;CACN,aAAa;CACb,MAAM;EACJ,OAAO;GAAE,MAAM;GAAU,aAAa;GAAc,UAAU;GAAM;EACpE,cAAc;GAAE,MAAM;GAAU,aAAa;GAAiD,UAAU;GAAM;EAC9G,MAAM;GAAE,MAAM;GAAU,aAAa;GAAuD,SAAS;GAAU;EAC/G,QAAQ;GAAE,MAAM;GAAW,aAAa;GAAmC,SAAS;GAAO;EAC5F;CACD,MAAM,IAAI,MAAM;EACd,MAAM,eAAe,aAAqD,KAAK,cAAc,eAAe;EAC5G,MAAM,OAAO,KAAK;AAClB,SAAO,kBAAkB;GAAE,OAAO,KAAK;GAAO;GAAc;GAAM,QAAQ,KAAK;GAAQ,YAAY,KAAK;GAAY,CAAC;;CAExH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@the-i18n-kit/cli",
3
- "version": "5.0.0",
3
+ "version": "6.0.0",
4
4
  "description": "Find missing translations, remove dead keys, rename across all locales at once — for Nuxt, Laravel, Vue, React/Next.js, or any project with JSON/PHP locale files",
5
5
  "keywords": [
6
6
  "i18n",
@@ -41,12 +41,6 @@
41
41
  "files": [
42
42
  "dist"
43
43
  ],
44
- "scripts": {
45
- "build": "tsdown",
46
- "dev": "tsdown --watch",
47
- "test": "vitest run",
48
- "typecheck": "tsc --noEmit"
49
- },
50
44
  "dependencies": {
51
45
  "citty": "^0.2.2",
52
46
  "consola": "^3.4.2",
@@ -92,5 +86,11 @@
92
86
  "tsdown": "^0.12.0",
93
87
  "vitest": "^3.2.0",
94
88
  "yaml": "2.8.2"
89
+ },
90
+ "scripts": {
91
+ "build": "tsdown",
92
+ "dev": "tsdown --watch",
93
+ "test": "vitest run",
94
+ "typecheck": "tsc --noEmit"
95
95
  }
96
- }
96
+ }
@@ -1,74 +0,0 @@
1
- import "./stdout-guard-sJHFWVem.js";
2
- import "./providers-CHE2ffi6.js";
3
- import { n as emitErrorResult } from "./_shared-DFyy3sh8.js";
4
- import { createRequire } from "node:module";
5
- import { defineCommand, runCommand, runMain } from "citty";
6
- //#region src/commands/index.ts
7
- const command = (load) => ({ load: () => load().then((m) => m.default) });
8
- const hidden = (load) => ({
9
- ...command(load),
10
- hidden: true
11
- });
12
- const commands$1 = {
13
- "init": command(() => import("./init-BtzmhsOM.js")),
14
- "detect": hidden(() => import("./detect-7cVfsNJP.js")),
15
- "list-dirs": hidden(() => import("./list-dirs-DUQbwvlE.js")),
16
- "get": command(() => import("./get-BE21Iy6N.js")),
17
- "write": command(() => import("./write-Czzm_Nb0.js")),
18
- "add": command(() => import("./add-C-qs8iBa.js")),
19
- "update": command(() => import("./update-DV4ijRVB.js")),
20
- "missing": command(() => import("./missing-CcYrgd54.js")),
21
- "status": command(() => import("./status-Bp-SzjTN.js")),
22
- "empty": hidden(() => import("./empty-DB8Hl_8u.js")),
23
- "search": command(() => import("./search-0io1BcsY.js")),
24
- "remove": command(() => import("./remove-DTbqM4bR.js")),
25
- "rename": command(() => import("./rename-C_rFKkU4.js")),
26
- "move": command(() => import("./move-B2px6Ck-.js")),
27
- "translate": command(() => import("./translate-cCb2Z57w.js")),
28
- "translate-missing": { load: () => import("./translate-cCb2Z57w.js").then((m) => {
29
- const def = m.default;
30
- return {
31
- ...def,
32
- meta: {
33
- ...def.meta,
34
- name: "translate-missing",
35
- description: "Alias of \"translate\" — matches the MCP tool translate_missing."
36
- }
37
- };
38
- }) },
39
- "translate-key": command(() => import("./translate-key-DHLWEmrA.js")),
40
- "scan": command(() => import("./scan-BkOIYhjZ.js")),
41
- "check": command(() => import("./check-BI1orjXb.js")),
42
- "find-duplicates": command(() => import("./find-duplicates-CmEqBfby.js")),
43
- "remove-orphans": command(() => import("./remove-orphans-Dfzu7hRz.js")),
44
- "scaffold": command(() => import("./scaffold-CUimlNqR.js"))
45
- };
46
- //#endregion
47
- //#region src/cli.ts
48
- const commands = Object.fromEntries(Object.entries(commands$1).filter(([, entry]) => !entry.hidden).map(([name, entry]) => [name, entry.load]));
49
- const { version, description } = createRequire(import.meta.url)("../package.json");
50
- const main = defineCommand({
51
- meta: {
52
- name: "the-i18n-cli",
53
- version,
54
- description
55
- },
56
- subCommands: commands
57
- });
58
- async function runCli() {
59
- const rawArgs = process.argv.slice(2);
60
- if (rawArgs.includes("--help") || rawArgs.includes("-h") || rawArgs.includes("--version") || rawArgs.includes("-v")) {
61
- await runMain(main);
62
- return;
63
- }
64
- try {
65
- await runCommand(main, { rawArgs });
66
- } catch (error) {
67
- emitErrorResult(error, { json: rawArgs.includes("--json") });
68
- process.exitCode = 1;
69
- }
70
- }
71
- //#endregion
72
- export { runCli };
73
-
74
- //# sourceMappingURL=cli-DsFuVJNP.js.map