Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
61e8ad3
refactor(write): hoist lang/style/tone enums to module const
sjsyrek Apr 24, 2026
e4647db
feat(write): accept JA/KO/ZH/zh-Hans target languages
sjsyrek Apr 24, 2026
5ad59fb
feat(write): annotate 4xx errors and lock in ES/IT/FR/PT tone/style
sjsyrek Apr 24, 2026
40ca0bc
feat(api): add Style Rules CRUD client methods
sjsyrek Apr 24, 2026
877244e
feat(cli): style-rules create/show/update/delete subcommands
sjsyrek Apr 24, 2026
945989d
feat(api): add Custom Instructions CRUD client methods
sjsyrek Apr 24, 2026
4c1e914
feat(cli): style-rules custom-instructions subcommands
sjsyrek Apr 24, 2026
e23bfd5
docs: changelog unreleased, api.md, examples for write + style rules
sjsyrek Apr 24, 2026
04dd5c2
fix(test): force no-api-key path in style-rules flag-validation tests
sjsyrek Apr 24, 2026
fc36948
test(cli): unit coverage for register-style-rules action handlers
sjsyrek Apr 24, 2026
2a2cd7d
fix(style-rules): correct configured_rules data shape and add --forma…
sjsyrek Apr 25, 2026
03a8e9d
fix(style-rules): resolve custom instructions by label via server-ass…
sjsyrek Apr 25, 2026
bb5d9e8
fix(examples): make example 15 idempotent across re-runs
sjsyrek Apr 25, 2026
75479b9
fix(examples): use relative --output path in example 30 to sidestep m…
sjsyrek Apr 25, 2026
b88ea12
fix(examples): example 32 silent exits, JSON-validation pipe, and buc…
sjsyrek Apr 25, 2026
950d2a7
fix(examples): example 32 hangs on interactive TTY at --dry-run --force
sjsyrek Apr 25, 2026
b447e49
fix(sync): assertPathWithinRoot follows symlinks before containment c…
sjsyrek Apr 25, 2026
1275362
docs(changelog): note sync symlink-resolution fix under Fixed
sjsyrek Apr 25, 2026
1ba8fcf
fix(cli): --format table now actually renders for languages, cache st…
sjsyrek Apr 25, 2026
dd7591c
docs(test): document the known nock/@mswjs HTTPINCOMINGMESSAGE leak
sjsyrek Apr 25, 2026
b08a4f6
fix(api): sanitize server-returned error messages before terminal int…
sjsyrek Apr 25, 2026
208494e
docs(changelog): fix categorization and update-instruction signature …
sjsyrek Apr 25, 2026
d723d17
chore(release): v1.2.0
sjsyrek Apr 25, 2026
1ef1dcb
Merge branch 'feat/api-parity-1.2.0' into 'main'
sjsyrek Apr 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.2.0] - 2026-04-25

### Added

- **write**: Japanese (`ja`), Korean (`ko`), and Simplified Chinese (`zh`, `zh-Hans`) are now accepted target languages for `deepl write`.
- **write**: `--tone` and `--style` now apply to Spanish (`es`), Italian (`it`), French (`fr`), and Portuguese (`pt`, `pt-BR`, `pt-PT`) in addition to the previously supported locales. The full value lists on `--tone` and `--style` are unchanged — the same 9 styles and 9 tones are accepted, and the mutual-exclusion rule between `--style` and `--tone` is unchanged. See `docs/API.md` for supported target-language / style / tone combinations.
- **write**: 4xx responses from the Write API that arrive while `--style` or `--tone` is set now carry an explicit recovery hint pointing at `docs/API.md` for supported target-language / style / tone combinations.
- **style-rules**: Full CRUD — `deepl style-rules create|show|update|delete` alongside the existing `list`. `create` requires `--name` and `--language`. `update` accepts `--name` for a rename and `--rules` for replacing configured rules (PUT `/configured_rules`); at least one is required. `--rules` takes a JSON object of category → settings, e.g. `'{"punctuation":{"quotation_mark":"use_guillemets"}}'` — matching the DeepL API's two-level rule shape. `delete` supports `-y`/`--yes`, `--dry-run`, and a TTY confirmation prompt. All new subcommands support `--format text|json`. Text-format output sanitizes control characters from rule names, configured-rule keys/values, and instruction text.
- **style-rules**: Custom instructions management — `deepl style-rules instructions <style-rule-id>` (list, synthesized from the detailed `show` response), plus `add-instruction <style-id> <label> <prompt>`, `update-instruction <style-id> <label> <prompt>`, and `remove-instruction <style-id> <label>` subcommands. `remove-instruction` ships `-y`/`--yes`, `--dry-run`, and a TTY confirmation prompt (custom instructions are user-authored text and deserve confirmation before deletion).
- **style-rules**: `deepl style-rules list` and `deepl style-rules instructions <style-rule-id>` accept `--format table` for aligned column output via `cli-table3`, matching the existing `translate`, `languages`, `usage`, and `cache` commands. In non-TTY output (pipe, redirect, CI), table falls back to plain text with a `WARN` line on stderr — same pattern used by `deepl translate --format table`.
- **examples**: `examples/35-style-rules-crud.sh` — end-to-end style-rules workflow (create → show → update rules → add custom instruction → update instruction → remove instruction → delete rule).
- **examples**: `examples/36-write-extended-languages.sh` — `deepl write` with Japanese, Korean, and Simplified Chinese targets and tone / style applied to Spanish, Italian, French, and Portuguese variants.

### Fixed

- **languages / cache stats / usage**: `--format table` now actually renders a `cli-table3` table on these commands. Previously the flag was advertised in `--help` but the action handler only branched on `'json'`, so `--format table` silently produced text output. Same non-TTY fallback as the other table commands.
- **sync**: `deepl sync export --output <path>` (and other sync surfaces that call `assertPathWithinRoot`) no longer reject valid output paths under a project root that contains a symlink in its ancestor chain. The containment check now resolves both sides through `fs.realpath` before comparing, so a project under macOS `/tmp` (a symlink to `/private/tmp`) — or any other symlinked directory — works regardless of which form the user types or the engine captures. Symlink-based escape attempts (a symlink inside the project pointing outside) are now also rejected as a defense-in-depth bonus.

### Security

- **api**: Server-returned error messages are now passed through `sanitizeForTerminal` before being interpolated into the user-facing `API error: …` and `Server error (5xx): …` strings emitted from `src/api/http-client.ts`. Defense-in-depth against a buggy or malicious server scribbling ANSI escape codes or other terminal control characters on the user's terminal via the error path. Mirrors the existing TMS-client hardening; no change to error-message wording when the server payload is well-formed.

## [1.1.0] - 2026-04-23

### Added
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.0
1.2.0
228 changes: 208 additions & 20 deletions docs/API.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# DeepL CLI - API Reference

**Version**: 1.1.0
**Last Updated**: April 23, 2026
**Version**: 1.2.0
**Last Updated**: April 25, 2026

Complete reference for all DeepL CLI commands, options, and configuration.

Expand Down Expand Up @@ -664,15 +664,15 @@ deepl write [OPTIONS] TEXT

#### Description

Enhance text quality with AI-powered grammar checking, style improvement, and tone adjustment. Supports 8 languages.
Enhance text quality with AI-powered grammar checking, style improvement, and tone adjustment. Supports 14 target languages.

**File Detection:** The command automatically detects if the text argument is a file path. If a file exists at that path, it operates on the file; otherwise, it treats the argument as text to improve.

#### Options

**Language:**

- `--lang, -l LANG` - Target language: `de`, `en`, `en-GB`, `en-US`, `es`, `fr`, `it`, `pt`, `pt-BR`, `pt-PT`. Optional — omit to auto-detect the language and rephrase in the original language.
- `--lang, -l LANG` - Target language: `de`, `en`, `en-GB`, `en-US`, `es`, `fr`, `it`, `ja`, `ko`, `pt`, `pt-BR`, `pt-PT`, `zh`, `zh-Hans`. Optional — omit to auto-detect the language and rephrase in the original language.
- `--to LANG` - Long-only alias of `--lang`. Accepts the same language values. Provided for muscle-memory consistency with `deepl translate --to`; the short form `-t` is intentionally **not** bound here (it would collide with `deepl translate -t, --to`). Specifying both `--to` and `--lang` with different values exits with a `ValidationError`.

**Style Options (mutually exclusive with tone):**
Expand All @@ -695,6 +695,16 @@ Enhance text quality with AI-powered grammar checking, style improvement, and to
- `diplomatic` - More careful and tactful
- `prefer_enthusiastic`, `prefer_friendly`, etc. - Soft preferences

**Supported target-language / style-and-tone combinations:**

| Target language | `--style` | `--tone` |
|-----------------|:---------:|:--------:|
| `en`, `en-GB`, `en-US`, `de` | ✓ | ✓ |
| `es`, `fr`, `it`, `pt`, `pt-BR`, `pt-PT` | ✓ | ✓ |
| `ja`, `ko`, `zh`, `zh-Hans` | — | — |

When `--style` or `--tone` is set for a target language that does not support it, the server returns a 4xx; the CLI converts that response into a `ValidationError` (exit code 6) that names the unsupported combination and points back to this table.

**Output Options:**

- `--alternatives, -a` - Show all improvement alternatives
Expand Down Expand Up @@ -2000,7 +2010,7 @@ Show cache statistics (status, entries count, size, percentage used).

**Options:**

- `--format <format>` - Output format: `text`, `json`, `table` (default: `text`)
- `--format <format>` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.

##### `clear`

Expand Down Expand Up @@ -2139,7 +2149,7 @@ Display your DeepL API character usage and remaining quota. Helps you monitor co

#### Options

- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`)
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.

#### Examples

Expand Down Expand Up @@ -2215,7 +2225,7 @@ You can filter to show only source languages, only target languages, or both (de

- `--source, -s` - Show only source languages
- `--target` - Show only target languages
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`)
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.

#### Examples

Expand Down Expand Up @@ -2462,7 +2472,7 @@ deepl auth clear

### style-rules

Manage DeepL style rules (Pro API only). Style rules are created via the DeepL web UI and applied to translations using their ID.
Manage DeepL style rules (Pro API only). Style rules carry a list of configured rule ids and optional custom instructions, and apply to translations via their style id.

#### Synopsis

Expand All @@ -2481,30 +2491,208 @@ List all available style rules.
- `--detailed` - Show detailed information including configured rules and custom instructions
- `--page NUMBER` - Page number for pagination
- `--page-size NUMBER` - Number of results per page (1-25)
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.

**Examples:**

```bash
# List all style rules
deepl style-rules list

# List with details
deepl style-rules list --detailed

# JSON output
deepl style-rules list --format json

# Pagination
deepl style-rules list --format table
deepl style-rules list --page 1 --page-size 10
```

##### `create`

Create a new style rule list.

**Options:**

- `--name NAME` - Style rule name (required)
- `--language LANG` - Target language (required)
- `--rules JSON` - Configured rules as a JSON object of category → settings (optional). The DeepL API models configured rules as a two-level dictionary; arrays are not accepted. See "Configured rules shape" below.
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)

**Examples:**

```bash
deepl style-rules create --name "Corporate" --language en
deepl style-rules create --name "Québécois" --language fr \
--rules '{"punctuation":{"quotation_mark":"use_guillemets"}}'
```

**Configured rules shape:**

The DeepL API expects `configured_rules` as a nested object — a category name maps to a settings object, and each setting maps to a string value. Empty rules are `{}`. Example for fr-CA:

```json
{
"punctuation": {
"quotation_mark": "use_guillemets",
"spacing_and_punctuation": "do_not_use_space"
},
"spelling_and_grammar": {
"accents_and_cedillas": "use_even_on_capital_letters"
}
}
```

The available categories, settings, and accepted values are defined by the DeepL API; consult DeepL's API documentation for the current rule schema.

##### `show`

Show a single style rule.

**Arguments:**

- `<id>` - Style rule id (required)

**Options:**

- `--detailed` - Include configured rules and custom instructions
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)

**Examples:**

```bash
deepl style-rules show sr-abc123
deepl style-rules show sr-abc123 --detailed --format json
```

##### `update`

Update a style rule — rename and/or replace configured rules. At least one of `--name` / `--rules` is required. When both are provided, the rename (PATCH) runs first, then the rules replacement (PUT `/configured_rules`).

**Arguments:**

- `<id>` - Style rule id (required)

**Options:**

- `--name NAME` - New name
- `--rules JSON` - Replace configured rules with a JSON object of category → settings (see "Configured rules shape" under `create`)
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)

**Examples:**

```bash
deepl style-rules update sr-abc123 --name "Renamed"
deepl style-rules update sr-abc123 --rules '{"punctuation":{"quotation_mark":"use_guillemets"}}'
deepl style-rules update sr-abc123 --name "New" --rules '{"spelling_and_grammar":{"accents_and_cedillas":"use_even_on_capital_letters"}}'
```

##### `delete`

Delete a style rule. Prompts for confirmation on a TTY; use `--yes` to skip or `--dry-run` to preview.

**Arguments:**

- `<id>` - Style rule id (required)

**Options:**

- `-y`, `--yes` - Skip confirmation prompt
- `--dry-run` - Show what would be deleted without performing the operation

**Examples:**

```bash
deepl style-rules delete sr-abc123
deepl style-rules delete sr-abc123 --yes
deepl style-rules delete sr-abc123 --dry-run
```

##### `instructions`

List custom instructions attached to a style rule. Synthesized from the detailed `show` response.

**Arguments:**

- `<style-id>` - Style rule id (required)

**Options:**

- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.

**Examples:**

```bash
deepl style-rules instructions sr-abc123
deepl style-rules instructions sr-abc123 --format json
deepl style-rules instructions sr-abc123 --format table
```

##### `add-instruction`

Add a custom instruction to a style rule.

**Arguments:**

- `<style-id>` - Style rule id (required)
- `<label>` - Instruction label, unique within the rule (required)
- `<prompt>` - Instruction prompt text (required)

**Options:**

- `--source-language LANG` - Source language code (optional)
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)

**Examples:**

```bash
deepl style-rules add-instruction sr-abc123 tone "Be formal"
deepl style-rules add-instruction sr-abc123 register "Use first person" --source-language en
```

##### `update-instruction`

Update the prompt and/or source language of an existing custom instruction. The label cannot be changed (it is the identifier); rename by removing and re-adding.

**Arguments:**

- `<style-id>` - Style rule id (required)
- `<label>` - Instruction label (required)
- `<prompt>` - New instruction prompt text (required)

**Options:**

- `--source-language LANG` - Source language code (optional)
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)

**Examples:**

```bash
deepl style-rules update-instruction sr-abc123 tone "Be friendlier"
```

##### `remove-instruction`

Remove a custom instruction from a style rule. Prompts for confirmation on a TTY.

**Arguments:**

- `<style-id>` - Style rule id (required)
- `<label>` - Instruction label (required)

**Options:**

- `-y`, `--yes` - Skip confirmation prompt
- `--dry-run` - Show what would be removed without performing the operation

**Examples:**

```bash
deepl style-rules remove-instruction sr-abc123 tone --yes
deepl style-rules remove-instruction sr-abc123 tone --dry-run
```

#### Notes

- Style rules are created and managed via the DeepL web interface, not through the API
- Style rules are Pro API only and datacenter-specific (EU and US rules don't cross)
- Use the style ID from `style-rules list` with `deepl translate --style-id <uuid>`
- Style rules force the `quality_optimized` model type
- Style rules are Pro API only and datacenter-specific (EU and US rules don't cross).
- Use the style id with `deepl translate --style-id <uuid>` to apply a rule at translation time.
- Style rules force the `quality_optimized` model type.
- Text-format output of stored user strings (rule names, instruction labels, instruction prompts) passes through a terminal-escape sanitizer to prevent injection of control characters from the API response. JSON-format output preserves raw strings (JSON-escaped at the encoding layer).

### admin

Expand Down
21 changes: 21 additions & 0 deletions examples/15-glossaries.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,27 @@ echo
# BASIC GLOSSARY OPERATIONS
# ═══════════════════════════════════════════════════════

# Idempotent setup: previously-aborted runs may have left glossaries with
# these demo names on the server (the script renames tech-terms-demo →
# tech-terms-renamed → tech-final, so any of those three may be lingering),
# and multiple aborted runs can leave duplicates of the same name. Delete
# every matching glossary by UUID via the JSON listing so the run starts
# from a clean slate.
echo "0. Pre-run cleanup of any leftover demo glossaries"
if command -v jq &>/dev/null; then
DEMO_NAMES='tech-terms-demo tech-terms-renamed tech-final business-terms-demo multi-demo'
for name in $DEMO_NAMES; do
deepl glossary list --format json 2>/dev/null \
| jq -r --arg n "$name" '.[] | select(.name == $n) | .glossary_id' 2>/dev/null \
| while read -r id; do
[[ -n "$id" ]] && deepl glossary delete "$id" --yes 2>/dev/null || true
done
done
else
echo " (jq not installed — skipping pre-run cleanup; install jq for full idempotency)"
fi
echo

# Example 1: Create a glossary
echo "1. Create tech glossary (EN → DE)"
deepl glossary create tech-terms-demo en de "$SAMPLE_DIR/tech-glossary.tsv"
Expand Down
Loading