Repository navigation
feat: configurable default language via env var and ~/.askcc/config.toml #92
Description
Problem
The output language for agent comments is currently configurable only via the -l/--language CLI flag (askcc/cli.py:122-128), which defaults to english. Users who consistently want a non-default language (e.g. Japanese) must pass --language japanese on every invocation.
There is no environment variable and no persistent user-level config for this.
Expected behaviour
The default language should be resolvable from any of the following sources, with the first match winning:
- CLI flag —
-l/--language <lang>(highest precedence; existing behaviour). - Environment variable —
ASKCC_LANGUAGE(e.g.export ASKCC_LANGUAGE=japanese). - User config file —
~/.askcc/config.tomlwith a[defaults]section, e.g.:(Resolved relative to[defaults] language = "japanese"
ASKCC_HOME, defaulting to~/.askcc, so it matches the existing convention used bytemplates/andlogs/.) - Built-in default —
english(lowest precedence; preserves current behaviour for unconfigured users).
Invalid values (not in SupportedLanguage) should log a warning and fall back to the next precedence level — same pattern as _resolve_effort_level() in askcc/settings.py:50-63.
Actual behaviour
askcc/cli.py:126 hardcodes default=SupportedLanguage.ENGLISH. There is no env var lookup and no user-config-file lookup. settings.py defines ASKCC_HOME (askcc/settings.py:86) but does not load any config file from it.
Implementation Plan
Summary of current state
askcc/definitions.py:633-635defines theSupportedLanguageStrEnum (english,japanese).askcc/cli.py:122-128exposes the-l/--languageargparse flag with hardcodeddefault=SupportedLanguage.ENGLISH.askcc/cli.py:280-281appendsOutput all comments in <lang>.to the prompt when the resolved language is not English.askcc/settings.py:39-66definesVALID_EFFORT_LEVELSand_resolve_effort_level()— the warn-and-fall-through pattern this work mirrors. The note ataskcc/settings.py:35-37documents that the enum lives insettings.py(notdefinitions.py) to avoid a circular import, sincedefinitions.py:4already doesfrom askcc.settings import DECISION_ISSUE_LABEL.askcc/settings.py:86definesASKCC_HOME(env-overridable, defaults to~/.askcc). No config file is loaded from it today.- Python is pinned to
>=3.14,<3.15(pyproject.toml:8), sotomllibis available in the stdlib — no new third-party dependency required.
Step-by-step tasks
-
Move
SupportedLanguageenum fromaskcc/definitions.py:633-635toaskcc/settings.py(insert nearVALID_EFFORT_LEVELSaroundaskcc/settings.py:39).- Rationale:
settings.pyneeds to type the newDEFAULT_LANGUAGEconstant asSupportedLanguage, but cannot import fromdefinitions.py(circular import — same constraint that already pinnedVALID_EFFORT_LEVELShere). - Update the import at
askcc/cli.py:11fromfrom .definitions import AgentAction, AgentConfig, SupportedLanguageso thatSupportedLanguagecomes from.settings(keepAgentAction, AgentConfigfrom.definitions). - Update the matching import in
tests/test_askcc.py:13-20.
- Rationale:
-
Add a TOML config-loader helper in
askcc/settings.py(after theASKCC_HOMEblock at line 86):import tomllibat the top.USER_CONFIG_PATH: Path = ASKCC_HOME / "config.toml"._load_user_config(path: Path | None = None) -> dict:target = path if path is not None else USER_CONFIG_PATH.- Return
{}iftargetis not a file (silent no-op — covers the missing-file case). - Open with
target.open("rb")andtomllib.load. Ontomllib.TOMLDecodeErrororOSError, log a warning naming the path and return{}(warned no-op).
- Module-level cache:
_USER_CONFIG: dict = _load_user_config()(evaluated at import time, satisfies the "cache the result at module load" requirement). - The optional
pathargument is only there for tests — it lets us point the loader at atmp_pathwithout reloading the module.
-
Add
_resolve_default_language()inaskcc/settings.py(mirror_resolve_effort_levelat lines 50-63):- Signature:
_resolve_default_language(user_config: dict | None = None) -> SupportedLanguage(the optionaluser_configis for tests; defaults to_USER_CONFIG). - Step 1 — env var:
os.getenv("ASKCC_LANGUAGE"). If non-empty, trySupportedLanguage(raw). OnValueErrorlog a warning (Invalid ASKCC_LANGUAGE=%r (valid: %s). Ignoring.) and fall through. - Step 2 — user config:
(user_config or _USER_CONFIG).get("defaults", {}).get("language"). If set, trySupportedLanguage(value). OnValueErrorlog a warning that namesUSER_CONFIG_PATHand fall through. - Step 3 — built-in default:
return SupportedLanguage.ENGLISH. - Module-level export:
DEFAULT_LANGUAGE: SupportedLanguage = _resolve_default_language().
- Signature:
-
Wire
DEFAULT_LANGUAGEintoaskcc/cli.py:122-128:- Change
default=SupportedLanguage.ENGLISH→default=settings.DEFAULT_LANGUAGE. - Update the
help=string to document the precedence in the same style as--effort(askcc/cli.py:140-142):CLI > env (ASKCC_LANGUAGE) > user config (~/.askcc/config.toml [defaults].language) > built-in default (english). - The runtime guard at
askcc/cli.py:280(if args.language != SupportedLanguage.ENGLISH:) keeps working unchanged becauseSupportedLanguageis aStrEnumand equality is preserved.
- Change
-
Update
README.md:- Add an
ASKCC_LANGUAGErow to the Environment Variables table (README.md:96-101). - Add a "User Configuration File" subsection (under "Customizing Prompts" or as its own "Configuration" section) that shows the
~/.askcc/config.tomlexample and lists the four-level precedence for--language.
- Add an
-
Add unit tests in
tests/test_askcc.py(newTestDefaultLanguageResolutionclass, modelled onTestThinkingSettingsat lines 1604+):test_default_language_from_env_var—monkeypatch.setenv("ASKCC_LANGUAGE", "japanese");_resolve_default_language()returnsSupportedLanguage.JAPANESE.test_default_language_from_config_file— write atmp_path/config.tomlwith[defaults]\nlanguage = "japanese";_resolve_default_language(_load_user_config(tmp_path/"config.toml"))returnsJAPANESE.test_env_var_overrides_config_file— env=japanese, config=english; resolves toJAPANESE.test_config_file_overrides_builtin_default— env unset, config=japanese; resolves toJAPANESE.test_default_language_unset_returns_english— env unset, no config; resolves toENGLISH.test_invalid_env_value_warns_and_falls_back— env=klingon, config unset; returnsENGLISHandcaplogcapturesInvalid ASKCC_LANGUAGEwarning.test_invalid_config_value_warns_and_falls_back— config haslanguage = "klingon"; returnsENGLISHand warning is logged.test_missing_config_file_silent—_load_user_config(tmp_path/"missing.toml")returns{}with no warning emitted.test_malformed_toml_warns— write invalid TOML totmp_path/config.toml;_load_user_config(...)returns{}and logs a warning (does not raise).- CLI-flag-overrides-env precedence is enforced by argparse and is implicitly covered by existing CLI tests; one assertion in the new class can confirm explicitly.
-
Run the verification gate before opening the PR (per
DEVELOP_AGENT_PROMPT):uv run pytest,uv run ruff check,uv run pyright. All three must pass.
Files touched
askcc/settings.py— addtomllibimport,SupportedLanguageenum (moved here),USER_CONFIG_PATH,_load_user_config,_USER_CONFIG,_resolve_default_language,DEFAULT_LANGUAGE.askcc/definitions.py— removeSupportedLanguage(now lives insettings.py).askcc/cli.py— splitSupportedLanguageimport to.settings; switch argparse default tosettings.DEFAULT_LANGUAGE; update help text.tests/test_askcc.py— updateSupportedLanguageimport path; addTestDefaultLanguageResolution.README.md— documentASKCC_LANGUAGEand~/.askcc/config.toml.
Risks / open questions
- Moving
SupportedLanguagetosettings.pyis an internal-import-path change. The two known call sites (askcc/cli.py:11,tests/test_askcc.py:19) are both updated in this PR. The package is markedPrivate :: Do Not Upload(pyproject.toml:11) so there are no external consumers — public API impact is nil. Flagged so reviewers don't need to look it up. - Cache lifetime —
_USER_CONFIGis computed once at module import. Tests inject a config dict via the optionaluser_configargument rather than reloading the module; production callers never need to invalidate the cache mid-run. - TOML parse-error logging — on malformed TOML the warning includes the path but not the exception detail (avoids accidentally echoing config contents into logs). If the maintainer prefers exception detail surfaced, that is a one-line change. Minor judgment call; default chosen for safety.
Acceptance Criteria
-
ASKCC_LANGUAGEenvironment variable sets the default language -
~/.askcc/config.toml[defaults] languagesets the default language - Resolution order is CLI flag > env var > user config > built-in default
-
ASKCC_HOMEis honored when locating the user config file - Invalid language values log a warning and fall back gracefully
- Missing/malformed config files do not crash the CLI
- Unit tests cover all four precedence levels and error cases
- README documents the new configuration mechanism
-
uv run pytest,uv run ruff check, anduv run pyrightall pass
Dependencies
None identified. Python 3.14 is already pinned (pyproject.toml:8), so tomllib is available in the stdlib — no new third-party dependencies are introduced. The SupportedLanguage enum relocation is internal-only.
Out of scope
- Generalizing other settings (e.g.
effort_level,runner) to read from the same config file. That is a natural follow-up but should be its own issue once the config-loader pattern is in place.
Activity
monkut commentedon Apr 27, 2026
Plan summary
Updated the issue description with a finalized Implementation Plan, Acceptance Criteria (preserved from the original spec), and a Dependencies section. Assigned to
@me.Sections added/updated in the issue body
## Acceptance Criteria.tomllibis stdlib).Approach in one paragraph
Mirror the existing
_resolve_effort_level()pattern (askcc/settings.py:50-63): add_load_user_config()(caches a parsed~/.askcc/config.tomlat module load; missing → silent{}, malformed → warned{}) and_resolve_default_language()(env > config > built-in default, warn-and-fall-through on invalid values). ExportDEFAULT_LANGUAGE: SupportedLanguageand wire it into theargparsedefault=ataskcc/cli.py:126.Risks / open questions flagged for review
SupportedLanguageenum is moved fromaskcc/definitions.pytoaskcc/settings.pyto avoid a circular import (definitions.pyalready imports fromsettings.py). This matches the existingVALID_EFFORT_LEVELSprecedent. Two internal import sites (askcc/cli.py:11,tests/test_askcc.py:19) are updated in this PR; the package is private (Private :: Do Not Upload) so there is no external API impact.No
needs:decisionblock — the spec is complete and the judgment calls above are minor.:tokens-used: model: N/A, input: 23, output: 21950
monkut commentedon Apr 27, 2026
Implementation opened in #93 on branch `feature/92-configurable-default-language`.
Summary of changes
Verification
Acceptance criteria
:tokens-used: model: N/A, input: 74, output: 23821