codexspec 0.6.1__tar.gz → 0.6.3__tar.gz
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.
- {codexspec-0.6.1 → codexspec-0.6.3}/PKG-INFO +41 -16
- {codexspec-0.6.1 → codexspec-0.6.3}/README.md +40 -15
- {codexspec-0.6.1 → codexspec-0.6.3}/pyproject.toml +1 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/src/codexspec/__init__.py +187 -73
- codexspec-0.6.3/src/codexspec/i18n.py +400 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/src/codexspec/translator.py +6 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/analyze.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/checklist.md +5 -5
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/clarify.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/config.md +12 -8
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/constitution.md +5 -4
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/generate-spec.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/implement-tasks.md +5 -5
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/plan-to-tasks.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/quick.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/review-code.md +5 -5
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/review-plan.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/review-spec.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/review-tasks.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/spec-to-plan.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/specify.md +6 -1
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/tasks-to-issues.md +5 -5
- codexspec-0.6.3/templates/translations/de.json +151 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/translations/en.json +2 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/translations/es.json +2 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/translations/fr.json +2 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/translations/ja.json +2 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/translations/ko.json +2 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/translations/pt-BR.json +2 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/translations/zh-CN.json +2 -0
- codexspec-0.6.1/src/codexspec/i18n.py +0 -356
- codexspec-0.6.1/templates/translations/de.json +0 -149
- {codexspec-0.6.1 → codexspec-0.6.3}/.gitignore +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/LICENSE +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/codexspec-icon.svg +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/codexspec-logo-dark.svg +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/codexspec-logo-light.svg +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/scripts/bash/check-i18n-completeness.sh +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/scripts/bash/check-i18n-structure.sh +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/scripts/bash/check-prerequisites.sh +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/scripts/bash/common.sh +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/scripts/bash/create-new-feature.sh +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/scripts/powershell/check-prerequisites.ps1 +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/scripts/powershell/common.ps1 +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/scripts/powershell/create-new-feature.ps1 +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/src/codexspec/commands/__init__.py +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/src/codexspec/commands/installer.py +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/src/codexspec/idea.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/commit-staged.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/commands/pr.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/docs/checklist-template.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/docs/constitution-template.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/docs/plan-template-detailed.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/docs/plan-template-simple.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/docs/requirements-template.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/docs/spec-template-detailed.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/docs/spec-template-simple.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/docs/tasks-template-detailed.md +0 -0
- {codexspec-0.6.1 → codexspec-0.6.3}/templates/docs/tasks-template-simple.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codexspec
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: CodexSpec - A Requirements-First SDD toolkit for Claude Code
|
|
5
5
|
Project-URL: Homepage, https://github.com/Zts0hg/codexspec
|
|
6
6
|
Project-URL: Repository, https://github.com/Zts0hg/codexspec
|
|
@@ -528,12 +528,15 @@ Implementation follows **conditional TDD workflow**:
|
|
|
528
528
|
|
|
529
529
|
| Option | Description |
|
|
530
530
|
| --------------- | ------------------------------------- |
|
|
531
|
-
| `PROJECT_NAME` | Project directory name
|
|
532
|
-
| `--here`, `-h` | Initialize in current directory
|
|
531
|
+
| `PROJECT_NAME` | Project directory name (`.` or `--here` for current dir) |
|
|
532
|
+
| `--here`, `-h` | Initialize in the current directory |
|
|
533
533
|
| `--ai`, `-a` | AI assistant to use (default: claude) |
|
|
534
|
-
| `--lang`, `-l` | Output language (e.g., en, zh-CN, ja) |
|
|
535
|
-
| `--
|
|
536
|
-
| `--
|
|
534
|
+
| `--lang`, `-l` | Output (base) language; interaction/document/commit fall back to it (e.g., en, zh-CN, ja) |
|
|
535
|
+
| `--interaction-lang` | Interaction language (LLM dialogue + CLI output); overrides `--lang` |
|
|
536
|
+
| `--document-lang` | Document language (generated spec/plan/tasks); overrides `--lang` |
|
|
537
|
+
| `--commit-lang` | Commit-message language; overrides `--lang` |
|
|
538
|
+
| `--force`, `-f` | Overwrite files + auto-confirm prompts; never regenerates `config.yml` |
|
|
539
|
+
| `--no-git` | Skip git repository initialization |
|
|
537
540
|
| `--debug`, `-d` | Enable debug output |
|
|
538
541
|
|
|
539
542
|
</details>
|
|
@@ -543,8 +546,10 @@ Implementation follows **conditional TDD workflow**:
|
|
|
543
546
|
|
|
544
547
|
| Option | Description |
|
|
545
548
|
| ------------------------- | ---------------------------- |
|
|
546
|
-
| `--set-lang`, `-l` | Set output language
|
|
547
|
-
| `--set-
|
|
549
|
+
| `--set-lang`, `-l` | Set the output (base) language |
|
|
550
|
+
| `--set-interaction-lang` | Set the interaction language |
|
|
551
|
+
| `--set-document-lang` | Set the document language |
|
|
552
|
+
| `--set-commit-lang`, `-c` | Set the commit-message language |
|
|
548
553
|
| `--list-langs` | List all supported languages |
|
|
549
554
|
|
|
550
555
|
</details>
|
|
@@ -628,28 +633,46 @@ CodexSpec is inspired by GitHub spec-kit with key differences:
|
|
|
628
633
|
|
|
629
634
|
CodexSpec supports multiple languages through **LLM dynamic translation**. No translation templates to maintain - Claude translates content at runtime based on your language configuration.
|
|
630
635
|
|
|
636
|
+
### Language Dimensions
|
|
637
|
+
|
|
638
|
+
CodexSpec splits language into four independently-configurable dimensions. `output` is the base; the others override it and fall back to it (then `en`) when unset — so you can converse with Claude in one language while keeping generated artifacts or commit messages in another.
|
|
639
|
+
|
|
640
|
+
| Dimension | `config.yml` key | Set at init | Set later | Controls | Falls back to |
|
|
641
|
+
|-----------|------------------|-------------|-----------|----------|---------------|
|
|
642
|
+
| Output (base) | `output` | `--lang` | `config --set-lang` | base for the other three | `en` |
|
|
643
|
+
| Interaction | `interaction` | `--interaction-lang` | `config --set-interaction-lang` | LLM dialogue + CLI output | output → `en` |
|
|
644
|
+
| Document | `document` | `--document-lang` | `config --set-document-lang` | generated spec/plan/tasks | output → `en` |
|
|
645
|
+
| Commit | `commit` | `--commit-lang` | `config --set-commit-lang` | git commit messages | output → `en` |
|
|
646
|
+
| Templates | `templates` | — | — | template source (always `en`) | — |
|
|
647
|
+
|
|
631
648
|
### Setting Language
|
|
632
649
|
|
|
633
650
|
**During initialization:**
|
|
634
651
|
|
|
635
652
|
```bash
|
|
636
|
-
#
|
|
653
|
+
# Chinese output (sets the output base)
|
|
637
654
|
codexspec init my-project --lang zh-CN
|
|
638
655
|
|
|
639
|
-
#
|
|
640
|
-
codexspec init my-project --lang
|
|
656
|
+
# Fully non-interactive: zh-CN base, English commit messages
|
|
657
|
+
codexspec init my-project --lang zh-CN --commit-lang en
|
|
658
|
+
|
|
659
|
+
# Set every dimension explicitly (scriptable, no prompts)
|
|
660
|
+
codexspec init my-project \
|
|
661
|
+
--interaction-lang zh-CN --document-lang en --commit-lang en
|
|
641
662
|
```
|
|
642
663
|
|
|
664
|
+
First-time init in a TTY without `--lang` (and without all three dimension flags) prompts for a base language; in a non-TTY (CI/scripts) it defaults to `en`. Re-running `init` preserves any language key you did not specify.
|
|
665
|
+
|
|
643
666
|
**After initialization:**
|
|
644
667
|
|
|
645
668
|
```bash
|
|
646
669
|
# View current configuration
|
|
647
670
|
codexspec config
|
|
648
671
|
|
|
649
|
-
# Change
|
|
672
|
+
# Change a single dimension
|
|
650
673
|
codexspec config --set-lang zh-CN
|
|
651
|
-
|
|
652
|
-
|
|
674
|
+
codexspec config --set-interaction-lang zh-CN
|
|
675
|
+
codexspec config --set-document-lang en
|
|
653
676
|
codexspec config --set-commit-lang en
|
|
654
677
|
```
|
|
655
678
|
|
|
@@ -680,8 +703,10 @@ codexspec config --set-commit-lang en
|
|
|
680
703
|
version: "1.0"
|
|
681
704
|
|
|
682
705
|
language:
|
|
683
|
-
output: "zh-CN" #
|
|
684
|
-
|
|
706
|
+
output: "zh-CN" # Base language; the three below fall back to it, then "en"
|
|
707
|
+
interaction: "zh-CN" # LLM dialogue + codexspec CLI output (optional → defaults to output)
|
|
708
|
+
document: "en" # Generated requirements/spec/plan/tasks (optional → defaults to output)
|
|
709
|
+
commit: "en" # Git commit messages (optional → defaults to output)
|
|
685
710
|
templates: "en" # Keep as "en"
|
|
686
711
|
|
|
687
712
|
project:
|
|
@@ -483,12 +483,15 @@ Implementation follows **conditional TDD workflow**:
|
|
|
483
483
|
|
|
484
484
|
| Option | Description |
|
|
485
485
|
| --------------- | ------------------------------------- |
|
|
486
|
-
| `PROJECT_NAME` | Project directory name
|
|
487
|
-
| `--here`, `-h` | Initialize in current directory
|
|
486
|
+
| `PROJECT_NAME` | Project directory name (`.` or `--here` for current dir) |
|
|
487
|
+
| `--here`, `-h` | Initialize in the current directory |
|
|
488
488
|
| `--ai`, `-a` | AI assistant to use (default: claude) |
|
|
489
|
-
| `--lang`, `-l` | Output language (e.g., en, zh-CN, ja) |
|
|
490
|
-
| `--
|
|
491
|
-
| `--
|
|
489
|
+
| `--lang`, `-l` | Output (base) language; interaction/document/commit fall back to it (e.g., en, zh-CN, ja) |
|
|
490
|
+
| `--interaction-lang` | Interaction language (LLM dialogue + CLI output); overrides `--lang` |
|
|
491
|
+
| `--document-lang` | Document language (generated spec/plan/tasks); overrides `--lang` |
|
|
492
|
+
| `--commit-lang` | Commit-message language; overrides `--lang` |
|
|
493
|
+
| `--force`, `-f` | Overwrite files + auto-confirm prompts; never regenerates `config.yml` |
|
|
494
|
+
| `--no-git` | Skip git repository initialization |
|
|
492
495
|
| `--debug`, `-d` | Enable debug output |
|
|
493
496
|
|
|
494
497
|
</details>
|
|
@@ -498,8 +501,10 @@ Implementation follows **conditional TDD workflow**:
|
|
|
498
501
|
|
|
499
502
|
| Option | Description |
|
|
500
503
|
| ------------------------- | ---------------------------- |
|
|
501
|
-
| `--set-lang`, `-l` | Set output language
|
|
502
|
-
| `--set-
|
|
504
|
+
| `--set-lang`, `-l` | Set the output (base) language |
|
|
505
|
+
| `--set-interaction-lang` | Set the interaction language |
|
|
506
|
+
| `--set-document-lang` | Set the document language |
|
|
507
|
+
| `--set-commit-lang`, `-c` | Set the commit-message language |
|
|
503
508
|
| `--list-langs` | List all supported languages |
|
|
504
509
|
|
|
505
510
|
</details>
|
|
@@ -583,28 +588,46 @@ CodexSpec is inspired by GitHub spec-kit with key differences:
|
|
|
583
588
|
|
|
584
589
|
CodexSpec supports multiple languages through **LLM dynamic translation**. No translation templates to maintain - Claude translates content at runtime based on your language configuration.
|
|
585
590
|
|
|
591
|
+
### Language Dimensions
|
|
592
|
+
|
|
593
|
+
CodexSpec splits language into four independently-configurable dimensions. `output` is the base; the others override it and fall back to it (then `en`) when unset — so you can converse with Claude in one language while keeping generated artifacts or commit messages in another.
|
|
594
|
+
|
|
595
|
+
| Dimension | `config.yml` key | Set at init | Set later | Controls | Falls back to |
|
|
596
|
+
|-----------|------------------|-------------|-----------|----------|---------------|
|
|
597
|
+
| Output (base) | `output` | `--lang` | `config --set-lang` | base for the other three | `en` |
|
|
598
|
+
| Interaction | `interaction` | `--interaction-lang` | `config --set-interaction-lang` | LLM dialogue + CLI output | output → `en` |
|
|
599
|
+
| Document | `document` | `--document-lang` | `config --set-document-lang` | generated spec/plan/tasks | output → `en` |
|
|
600
|
+
| Commit | `commit` | `--commit-lang` | `config --set-commit-lang` | git commit messages | output → `en` |
|
|
601
|
+
| Templates | `templates` | — | — | template source (always `en`) | — |
|
|
602
|
+
|
|
586
603
|
### Setting Language
|
|
587
604
|
|
|
588
605
|
**During initialization:**
|
|
589
606
|
|
|
590
607
|
```bash
|
|
591
|
-
#
|
|
608
|
+
# Chinese output (sets the output base)
|
|
592
609
|
codexspec init my-project --lang zh-CN
|
|
593
610
|
|
|
594
|
-
#
|
|
595
|
-
codexspec init my-project --lang
|
|
611
|
+
# Fully non-interactive: zh-CN base, English commit messages
|
|
612
|
+
codexspec init my-project --lang zh-CN --commit-lang en
|
|
613
|
+
|
|
614
|
+
# Set every dimension explicitly (scriptable, no prompts)
|
|
615
|
+
codexspec init my-project \
|
|
616
|
+
--interaction-lang zh-CN --document-lang en --commit-lang en
|
|
596
617
|
```
|
|
597
618
|
|
|
619
|
+
First-time init in a TTY without `--lang` (and without all three dimension flags) prompts for a base language; in a non-TTY (CI/scripts) it defaults to `en`. Re-running `init` preserves any language key you did not specify.
|
|
620
|
+
|
|
598
621
|
**After initialization:**
|
|
599
622
|
|
|
600
623
|
```bash
|
|
601
624
|
# View current configuration
|
|
602
625
|
codexspec config
|
|
603
626
|
|
|
604
|
-
# Change
|
|
627
|
+
# Change a single dimension
|
|
605
628
|
codexspec config --set-lang zh-CN
|
|
606
|
-
|
|
607
|
-
|
|
629
|
+
codexspec config --set-interaction-lang zh-CN
|
|
630
|
+
codexspec config --set-document-lang en
|
|
608
631
|
codexspec config --set-commit-lang en
|
|
609
632
|
```
|
|
610
633
|
|
|
@@ -635,8 +658,10 @@ codexspec config --set-commit-lang en
|
|
|
635
658
|
version: "1.0"
|
|
636
659
|
|
|
637
660
|
language:
|
|
638
|
-
output: "zh-CN" #
|
|
639
|
-
|
|
661
|
+
output: "zh-CN" # Base language; the three below fall back to it, then "en"
|
|
662
|
+
interaction: "zh-CN" # LLM dialogue + codexspec CLI output (optional → defaults to output)
|
|
663
|
+
document: "en" # Generated requirements/spec/plan/tasks (optional → defaults to output)
|
|
664
|
+
commit: "en" # Git commit messages (optional → defaults to output)
|
|
640
665
|
templates: "en" # Keep as "en"
|
|
641
666
|
|
|
642
667
|
project:
|
|
@@ -29,14 +29,12 @@ from .commands.installer import (
|
|
|
29
29
|
)
|
|
30
30
|
from .i18n import (
|
|
31
31
|
generate_config_content,
|
|
32
|
-
|
|
32
|
+
get_interaction_language,
|
|
33
33
|
get_language_name,
|
|
34
|
-
get_project_language,
|
|
35
34
|
get_supported_languages,
|
|
36
35
|
is_supported_language,
|
|
37
36
|
normalize_locale,
|
|
38
|
-
|
|
39
|
-
update_output_language,
|
|
37
|
+
update_language_field,
|
|
40
38
|
)
|
|
41
39
|
from .translator import translate
|
|
42
40
|
|
|
@@ -185,6 +183,16 @@ def config(
|
|
|
185
183
|
"-c",
|
|
186
184
|
help="Set the commit message language (defaults to output language)",
|
|
187
185
|
),
|
|
186
|
+
set_interaction_lang: Optional[str] = typer.Option(
|
|
187
|
+
None,
|
|
188
|
+
"--set-interaction-lang",
|
|
189
|
+
help="Set the interaction language (LLM dialogue + codexspec CLI terminal output)",
|
|
190
|
+
),
|
|
191
|
+
set_document_lang: Optional[str] = typer.Option(
|
|
192
|
+
None,
|
|
193
|
+
"--set-document-lang",
|
|
194
|
+
help="Set the document language (generated requirements/spec/plan/tasks files)",
|
|
195
|
+
),
|
|
188
196
|
list_langs: bool = typer.Option(
|
|
189
197
|
False,
|
|
190
198
|
"--list-langs",
|
|
@@ -322,6 +330,45 @@ def config(
|
|
|
322
330
|
console.print(f"[green]Commit message language set to:[/green] {normalized} ({lang_name})")
|
|
323
331
|
return
|
|
324
332
|
|
|
333
|
+
# Handle set interaction language
|
|
334
|
+
if set_interaction_lang:
|
|
335
|
+
normalized = normalize_locale(set_interaction_lang)
|
|
336
|
+
if not is_supported_language(normalized):
|
|
337
|
+
console.print(
|
|
338
|
+
f"[yellow]Warning: '{set_interaction_lang}' is not in the list of "
|
|
339
|
+
"commonly supported languages.[/yellow]"
|
|
340
|
+
)
|
|
341
|
+
console.print(
|
|
342
|
+
"It may still work if Claude supports it. "
|
|
343
|
+
"Run [cyan]codexspec config --list-langs[/cyan] to see supported languages."
|
|
344
|
+
)
|
|
345
|
+
if update_language_field(config_file, "interaction", normalized):
|
|
346
|
+
lang_name = get_language_name(normalized)
|
|
347
|
+
console.print(f"[green]Interaction language set to:[/green] {normalized} ({lang_name})")
|
|
348
|
+
else:
|
|
349
|
+
console.print("[red]Failed to update interaction language setting[/red]")
|
|
350
|
+
raise typer.Exit(1)
|
|
351
|
+
return
|
|
352
|
+
|
|
353
|
+
# Handle set document language
|
|
354
|
+
if set_document_lang:
|
|
355
|
+
normalized = normalize_locale(set_document_lang)
|
|
356
|
+
if not is_supported_language(normalized):
|
|
357
|
+
console.print(
|
|
358
|
+
f"[yellow]Warning: '{set_document_lang}' is not in the list of commonly supported languages.[/yellow]"
|
|
359
|
+
)
|
|
360
|
+
console.print(
|
|
361
|
+
"It may still work if Claude supports it. "
|
|
362
|
+
"Run [cyan]codexspec config --list-langs[/cyan] to see supported languages."
|
|
363
|
+
)
|
|
364
|
+
if update_language_field(config_file, "document", normalized):
|
|
365
|
+
lang_name = get_language_name(normalized)
|
|
366
|
+
console.print(f"[green]Document language set to:[/green] {normalized} ({lang_name})")
|
|
367
|
+
else:
|
|
368
|
+
console.print("[red]Failed to update document language setting[/red]")
|
|
369
|
+
raise typer.Exit(1)
|
|
370
|
+
return
|
|
371
|
+
|
|
325
372
|
# Display current configuration
|
|
326
373
|
console.print(
|
|
327
374
|
Panel(
|
|
@@ -340,8 +387,8 @@ def list_commands() -> None:
|
|
|
340
387
|
showing their display names and descriptions.
|
|
341
388
|
"""
|
|
342
389
|
|
|
343
|
-
# Get user's language preference
|
|
344
|
-
language =
|
|
390
|
+
# Get user's language preference (CLI terminal output uses the interaction language)
|
|
391
|
+
language = get_interaction_language()
|
|
345
392
|
|
|
346
393
|
metadata = get_installed_commands_metadata()
|
|
347
394
|
|
|
@@ -395,13 +442,35 @@ def init(
|
|
|
395
442
|
None,
|
|
396
443
|
"--lang",
|
|
397
444
|
"-l",
|
|
398
|
-
help=
|
|
445
|
+
help=(
|
|
446
|
+
"Output (base) language (e.g., en, zh-CN, ja). interaction/document/commit "
|
|
447
|
+
"fall back to it. Prompted on first-time init if not given in a TTY."
|
|
448
|
+
),
|
|
449
|
+
),
|
|
450
|
+
interaction_lang: Optional[str] = typer.Option(
|
|
451
|
+
None,
|
|
452
|
+
"--interaction-lang",
|
|
453
|
+
help=("Interaction language (LLM dialogue + codexspec CLI output); overrides --lang for this dimension."),
|
|
454
|
+
),
|
|
455
|
+
document_lang: Optional[str] = typer.Option(
|
|
456
|
+
None,
|
|
457
|
+
"--document-lang",
|
|
458
|
+
help="Document language (generated requirements/spec/plan/tasks); overrides --lang.",
|
|
459
|
+
),
|
|
460
|
+
commit_lang: Optional[str] = typer.Option(
|
|
461
|
+
None,
|
|
462
|
+
"--commit-lang",
|
|
463
|
+
help="Commit-message language; overrides --lang.",
|
|
399
464
|
),
|
|
400
465
|
force: bool = typer.Option(
|
|
401
466
|
False,
|
|
402
467
|
"--force",
|
|
403
468
|
"-f",
|
|
404
|
-
help=
|
|
469
|
+
help=(
|
|
470
|
+
"Overwrite existing files and auto-confirm prompts (skips the language "
|
|
471
|
+
"prompt and command-update/migration confirms). Language config is updated "
|
|
472
|
+
"surgically, never fully regenerated."
|
|
473
|
+
),
|
|
405
474
|
),
|
|
406
475
|
no_git: bool = typer.Option(
|
|
407
476
|
False,
|
|
@@ -428,37 +497,85 @@ def init(
|
|
|
428
497
|
codexspec init . --ai claude
|
|
429
498
|
codexspec init --here --ai claude
|
|
430
499
|
codexspec init . --force --ai claude
|
|
500
|
+
codexspec init my-project --interaction-lang en --document-lang zh-CN --commit-lang en
|
|
431
501
|
"""
|
|
432
|
-
#
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
else:
|
|
443
|
-
# Non-TTY environment: use default
|
|
444
|
-
lang = "en"
|
|
445
|
-
|
|
446
|
-
# Normalize language code early for use throughout the function
|
|
447
|
-
normalized_lang = normalize_locale(lang)
|
|
448
|
-
|
|
449
|
-
# Determine target directory
|
|
502
|
+
# When init() is invoked directly (not via the Typer CLI), typer.Option defaults
|
|
503
|
+
# arrive as OptionInfo sentinels rather than None. Coerce the language params to
|
|
504
|
+
# Optional[str] so the resolution logic below is robust in both call paths.
|
|
505
|
+
lang = lang if isinstance(lang, str) else None
|
|
506
|
+
interaction_lang = interaction_lang if isinstance(interaction_lang, str) else None
|
|
507
|
+
document_lang = document_lang if isinstance(document_lang, str) else None
|
|
508
|
+
commit_lang = commit_lang if isinstance(commit_lang, str) else None
|
|
509
|
+
|
|
510
|
+
# Determine target directory first — needed to detect an existing config.yml
|
|
511
|
+
# before resolving the language base (REQ-007).
|
|
450
512
|
if here or project_name == ".":
|
|
451
513
|
target_dir = Path.cwd()
|
|
452
514
|
elif project_name:
|
|
453
515
|
target_dir = Path.cwd() / project_name
|
|
454
516
|
else:
|
|
455
|
-
console.print(f"[red]{translate('cli.init.error_no_project_name',
|
|
517
|
+
console.print(f"[red]{translate('cli.init.error_no_project_name', 'en')}[/red]")
|
|
456
518
|
raise typer.Exit(1)
|
|
457
519
|
|
|
520
|
+
config_file = target_dir / ".codexspec" / "config.yml"
|
|
521
|
+
config_exists = config_file.exists()
|
|
522
|
+
|
|
523
|
+
def _normalize_lang_option(raw: str) -> str:
|
|
524
|
+
"""Normalize a language flag value; warn (do not error) if unsupported."""
|
|
525
|
+
normalized = normalize_locale(raw)
|
|
526
|
+
if not is_supported_language(normalized):
|
|
527
|
+
console.print(f"[yellow]Warning: '{raw}' is not in the list of commonly supported languages.[/yellow]")
|
|
528
|
+
console.print(
|
|
529
|
+
"It may still work if Claude supports it. "
|
|
530
|
+
"Run [cyan]codexspec config --list-langs[/cyan] to see supported languages."
|
|
531
|
+
)
|
|
532
|
+
return normalized
|
|
533
|
+
|
|
534
|
+
# Per-dimension overrides: each flag maps to exactly one config key, with no
|
|
535
|
+
# CLI-level precedence between them (DEC-001).
|
|
536
|
+
lang_overrides: dict[str, str] = {}
|
|
537
|
+
if interaction_lang is not None:
|
|
538
|
+
lang_overrides["interaction"] = _normalize_lang_option(interaction_lang)
|
|
539
|
+
if document_lang is not None:
|
|
540
|
+
lang_overrides["document"] = _normalize_lang_option(document_lang)
|
|
541
|
+
if commit_lang is not None:
|
|
542
|
+
lang_overrides["commit"] = _normalize_lang_option(commit_lang)
|
|
543
|
+
|
|
544
|
+
# The output base is determinable if --lang was given OR all three specific
|
|
545
|
+
# dimensions are explicit (output is then irrelevant). Otherwise it must be
|
|
546
|
+
# prompted (first-time, TTY) or defaulted (first-time, non-TTY); on an
|
|
547
|
+
# existing config with no flag, everything is preserved (REQ-007).
|
|
548
|
+
output_value: Optional[str] = None
|
|
549
|
+
if lang is not None:
|
|
550
|
+
output_value = _normalize_lang_option(lang)
|
|
551
|
+
base_determinable = output_value is not None or (
|
|
552
|
+
"interaction" in lang_overrides and "document" in lang_overrides and "commit" in lang_overrides
|
|
553
|
+
)
|
|
554
|
+
prompted_base = False
|
|
555
|
+
if base_determinable:
|
|
556
|
+
normalized_lang = normalize_locale(output_value) if output_value else "en"
|
|
557
|
+
elif config_exists:
|
|
558
|
+
normalized_lang = get_interaction_language(config_file)
|
|
559
|
+
elif sys.stdin.isatty():
|
|
560
|
+
try:
|
|
561
|
+
output_value = normalize_locale(prompt_language_selection())
|
|
562
|
+
prompted_base = True
|
|
563
|
+
normalized_lang = output_value
|
|
564
|
+
except KeyboardInterrupt:
|
|
565
|
+
console.print()
|
|
566
|
+
console.print("[yellow]Selection cancelled, using default language (en)[/yellow]")
|
|
567
|
+
output_value = "en"
|
|
568
|
+
normalized_lang = "en"
|
|
569
|
+
else:
|
|
570
|
+
output_value = "en"
|
|
571
|
+
normalized_lang = "en"
|
|
572
|
+
|
|
458
573
|
if debug:
|
|
459
574
|
console.print(f"[dim]Target directory: {target_dir}[/dim]")
|
|
460
575
|
console.print(f"[dim]AI assistant: {ai}[/dim]")
|
|
461
|
-
console.print(
|
|
576
|
+
console.print(
|
|
577
|
+
f"[dim]Language base: {output_value} (messages: {normalized_lang}); overrides: {lang_overrides}[/dim]"
|
|
578
|
+
)
|
|
462
579
|
|
|
463
580
|
# Check if directory exists and handle accordingly
|
|
464
581
|
if target_dir.exists() and not here and project_name != ".":
|
|
@@ -541,7 +658,7 @@ def init(
|
|
|
541
658
|
console.print(f"[dim]{translate('cli.init.migration_old_structure', normalized_lang)}[/dim]")
|
|
542
659
|
console.print(f"[dim]{translate('cli.init.migration_new_structure', normalized_lang)}[/dim]")
|
|
543
660
|
|
|
544
|
-
if Confirm.ask(translate("cli.init.migration_confirm", normalized_lang), default=True):
|
|
661
|
+
if force or Confirm.ask(translate("cli.init.migration_confirm", normalized_lang), default=True):
|
|
545
662
|
if migrate_old_commands(claude_dir, old_files):
|
|
546
663
|
console.print(f"[green]{translate('cli.init.migration_complete', normalized_lang)}[/green]")
|
|
547
664
|
migration_happened = True
|
|
@@ -553,14 +670,14 @@ def init(
|
|
|
553
670
|
# Install or update commands (translate after migration if user wants)
|
|
554
671
|
if migration_happened:
|
|
555
672
|
# Migration moved files, ask if user wants to update/translate them
|
|
556
|
-
if Confirm.ask(translate("cli.init.update_confirm", normalized_lang), default=True):
|
|
673
|
+
if force or Confirm.ask(translate("cli.init.update_confirm", normalized_lang), default=True):
|
|
557
674
|
count = install_commands_to_subdir(
|
|
558
675
|
codexspec_commands_dir, templates_dir, force=True, language=normalized_lang
|
|
559
676
|
)
|
|
560
677
|
console.print(f"[green]{translate('cli.init.commands_updated', normalized_lang, count=count)}[/green]")
|
|
561
678
|
elif should_update_commands(codexspec_dir):
|
|
562
679
|
console.print()
|
|
563
|
-
if Confirm.ask(translate("cli.init.update_confirm", normalized_lang), default=True):
|
|
680
|
+
if force or Confirm.ask(translate("cli.init.update_confirm", normalized_lang), default=True):
|
|
564
681
|
count = install_commands_to_subdir(
|
|
565
682
|
codexspec_commands_dir, templates_dir, force=True, language=normalized_lang
|
|
566
683
|
)
|
|
@@ -586,53 +703,50 @@ def init(
|
|
|
586
703
|
msg = translate("cli.init.created_file", normalized_lang, file=".codexspec/memory/constitution.md")
|
|
587
704
|
console.print(f"[green]{msg}[/green]")
|
|
588
705
|
|
|
589
|
-
#
|
|
706
|
+
# Apply language settings surgically (DEC-001 / CON-005 / REQ-005 / REQ-010).
|
|
707
|
+
# Build the full {key: value} map to write: the output base (if resolved) plus
|
|
708
|
+
# the per-dimension overrides. config.yml is never fully regenerated -- not
|
|
709
|
+
# even under --force -- and keys the user did not specify are always preserved.
|
|
590
710
|
config_file = codexspec_dir / "config.yml"
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
711
|
+
keys_to_write: dict[str, str] = {}
|
|
712
|
+
if output_value is not None:
|
|
713
|
+
keys_to_write["output"] = output_value
|
|
714
|
+
keys_to_write.update(lang_overrides)
|
|
715
|
+
|
|
716
|
+
wrote_keys: list[str] = []
|
|
717
|
+
if keys_to_write:
|
|
718
|
+
if not config_exists:
|
|
719
|
+
# Fresh project: emit only the chosen keys (sparse config).
|
|
720
|
+
config_content = generate_config_content(
|
|
721
|
+
output=keys_to_write.get("output"),
|
|
722
|
+
interaction=keys_to_write.get("interaction"),
|
|
723
|
+
document=keys_to_write.get("document"),
|
|
724
|
+
commit=keys_to_write.get("commit"),
|
|
725
|
+
created=datetime.now().strftime("%Y-%m-%d"),
|
|
726
|
+
)
|
|
727
|
+
config_file.write_text(config_content, encoding="utf-8")
|
|
728
|
+
wrote_keys = list(keys_to_write.keys())
|
|
729
|
+
lang_name = get_language_name(normalized_lang)
|
|
609
730
|
msg = translate(
|
|
610
|
-
"cli.init.
|
|
731
|
+
"cli.init.created_file",
|
|
611
732
|
normalized_lang,
|
|
612
|
-
|
|
613
|
-
selected=normalized_lang,
|
|
733
|
+
file=f".codexspec/config.yml (language: {lang_name})",
|
|
614
734
|
)
|
|
615
|
-
|
|
616
|
-
update_config_language(config_file, normalized_lang)
|
|
617
|
-
console.print(
|
|
618
|
-
f"[green]{translate('cli.init.commit_lang_updated', normalized_lang, lang=normalized_lang)}[/green]"
|
|
619
|
-
)
|
|
620
|
-
else:
|
|
621
|
-
# Update only output language, keep commit language
|
|
622
|
-
update_output_language(config_file, normalized_lang)
|
|
623
|
-
console.print(
|
|
624
|
-
f"[dim]{translate('cli.init.commit_lang_kept', normalized_lang, lang=current_commit_lang)}[/dim]"
|
|
625
|
-
)
|
|
735
|
+
console.print(f"[green]{msg}[/green]")
|
|
626
736
|
else:
|
|
627
|
-
#
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
737
|
+
# Existing project: update each chosen key in place, preserving the rest.
|
|
738
|
+
for key, value in keys_to_write.items():
|
|
739
|
+
if update_language_field(config_file, key, value):
|
|
740
|
+
wrote_keys.append(key)
|
|
741
|
+
|
|
742
|
+
# Non-blocking notice of which language keys were set (REQ-010).
|
|
743
|
+
if wrote_keys:
|
|
744
|
+
details = ", ".join(f"{k}={keys_to_write[k]}" for k in wrote_keys)
|
|
745
|
+
console.print(f"[dim]{translate('cli.init.language_key_set', normalized_lang, details=details)}[/dim]")
|
|
746
|
+
|
|
747
|
+
# Discoverability hint after a first-time interactive base selection (REQ-009).
|
|
748
|
+
if prompted_base:
|
|
749
|
+
console.print(f"[dim]{translate('cli.init.language_dimensions_hint', normalized_lang)}[/dim]")
|
|
636
750
|
|
|
637
751
|
# Create CLAUDE.md
|
|
638
752
|
claude_md = target_dir / "CLAUDE.md"
|