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.toml with a [defaults] section, e.g.:
[defaults]
language = "japanese"
(Resolved relative to ASKCC_HOME, defaulting to ~/.askcc, so it matches the existing convention used by templates/ and logs/.)
- 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-635 defines the SupportedLanguage StrEnum (english, japanese).
askcc/cli.py:122-128 exposes the -l/--language argparse flag with hardcoded default=SupportedLanguage.ENGLISH.
askcc/cli.py:280-281 appends Output all comments in <lang>. to the prompt when the resolved language is not English.
askcc/settings.py:39-66 defines VALID_EFFORT_LEVELS and _resolve_effort_level() — the warn-and-fall-through pattern this work mirrors. The note at askcc/settings.py:35-37 documents that the enum lives in settings.py (not definitions.py) to avoid a circular import, since definitions.py:4 already does from askcc.settings import DECISION_ISSUE_LABEL.
askcc/settings.py:86 defines ASKCC_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), so tomllib is available in the stdlib — no new third-party dependency required.
Step-by-step tasks
-
Move SupportedLanguage enum from askcc/definitions.py:633-635 to askcc/settings.py (insert near VALID_EFFORT_LEVELS around askcc/settings.py:39).
- Rationale:
settings.py needs to type the new DEFAULT_LANGUAGE constant as SupportedLanguage, but cannot import from definitions.py (circular import — same constraint that already pinned VALID_EFFORT_LEVELS here).
- Update the import at
askcc/cli.py:11 from from .definitions import AgentAction, AgentConfig, SupportedLanguage so that SupportedLanguage comes from .settings (keep AgentAction, AgentConfig from .definitions).
- Update the matching import in
tests/test_askcc.py:13-20.
-
Add a TOML config-loader helper in askcc/settings.py (after the ASKCC_HOME block at line 86):
import tomllib at 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
{} if target is not a file (silent no-op — covers the missing-file case).
- Open with
target.open("rb") and tomllib.load. On tomllib.TOMLDecodeError or OSError, 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
path argument is only there for tests — it lets us point the loader at a tmp_path without reloading the module.
-
Add _resolve_default_language() in askcc/settings.py (mirror _resolve_effort_level at lines 50-63):
- Signature:
_resolve_default_language(user_config: dict | None = None) -> SupportedLanguage (the optional user_config is for tests; defaults to _USER_CONFIG).
- Step 1 — env var:
os.getenv("ASKCC_LANGUAGE"). If non-empty, try SupportedLanguage(raw). On ValueError log 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, try SupportedLanguage(value). On ValueError log a warning that names USER_CONFIG_PATH and fall through.
- Step 3 — built-in default:
return SupportedLanguage.ENGLISH.
- Module-level export:
DEFAULT_LANGUAGE: SupportedLanguage = _resolve_default_language().
-
Wire DEFAULT_LANGUAGE into askcc/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 because SupportedLanguage is a StrEnum and equality is preserved.
-
Update README.md:
- Add an
ASKCC_LANGUAGE row 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.toml example and lists the four-level precedence for --language.
-
Add unit tests in tests/test_askcc.py (new TestDefaultLanguageResolution class, modelled on TestThinkingSettings at lines 1604+):
test_default_language_from_env_var — monkeypatch.setenv("ASKCC_LANGUAGE", "japanese"); _resolve_default_language() returns SupportedLanguage.JAPANESE.
test_default_language_from_config_file — write a tmp_path/config.toml with [defaults]\nlanguage = "japanese"; _resolve_default_language(_load_user_config(tmp_path/"config.toml")) returns JAPANESE.
test_env_var_overrides_config_file — env=japanese, config=english; resolves to JAPANESE.
test_config_file_overrides_builtin_default — env unset, config=japanese; resolves to JAPANESE.
test_default_language_unset_returns_english — env unset, no config; resolves to ENGLISH.
test_invalid_env_value_warns_and_falls_back — env=klingon, config unset; returns ENGLISH and caplog captures Invalid ASKCC_LANGUAGE warning.
test_invalid_config_value_warns_and_falls_back — config has language = "klingon"; returns ENGLISH and 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 to tmp_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 — add tomllib import, SupportedLanguage enum (moved here), USER_CONFIG_PATH, _load_user_config, _USER_CONFIG, _resolve_default_language, DEFAULT_LANGUAGE.
askcc/definitions.py — remove SupportedLanguage (now lives in settings.py).
askcc/cli.py — split SupportedLanguage import to .settings; switch argparse default to settings.DEFAULT_LANGUAGE; update help text.
tests/test_askcc.py — update SupportedLanguage import path; add TestDefaultLanguageResolution.
README.md — document ASKCC_LANGUAGE and ~/.askcc/config.toml.
Risks / open questions
- Moving
SupportedLanguage to settings.py is 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 marked Private :: 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_CONFIG is computed once at module import. Tests inject a config dict via the optional user_config argument 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
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.
Problem
The output language for agent comments is currently configurable only via the
-l/--languageCLI flag (askcc/cli.py:122-128), which defaults toenglish. Users who consistently want a non-default language (e.g. Japanese) must pass--language japaneseon 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:
-l/--language <lang>(highest precedence; existing behaviour).ASKCC_LANGUAGE(e.g.export ASKCC_LANGUAGE=japanese).~/.askcc/config.tomlwith a[defaults]section, e.g.:ASKCC_HOME, defaulting to~/.askcc, so it matches the existing convention used bytemplates/andlogs/.)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()inaskcc/settings.py:50-63.Actual behaviour
askcc/cli.py:126hardcodesdefault=SupportedLanguage.ENGLISH. There is no env var lookup and no user-config-file lookup.settings.pydefinesASKCC_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.>=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).settings.pyneeds to type the newDEFAULT_LANGUAGEconstant asSupportedLanguage, but cannot import fromdefinitions.py(circular import — same constraint that already pinnedVALID_EFFORT_LEVELShere).askcc/cli.py:11fromfrom .definitions import AgentAction, AgentConfig, SupportedLanguageso thatSupportedLanguagecomes from.settings(keepAgentAction, AgentConfigfrom.definitions).tests/test_askcc.py:13-20.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.{}iftargetis not a file (silent no-op — covers the missing-file case).target.open("rb")andtomllib.load. Ontomllib.TOMLDecodeErrororOSError, log a warning naming the path and return{}(warned no-op)._USER_CONFIG: dict = _load_user_config()(evaluated at import time, satisfies the "cache the result at module load" requirement).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):_resolve_default_language(user_config: dict | None = None) -> SupportedLanguage(the optionaluser_configis for tests; defaults to_USER_CONFIG).os.getenv("ASKCC_LANGUAGE"). If non-empty, trySupportedLanguage(raw). OnValueErrorlog a warning (Invalid ASKCC_LANGUAGE=%r (valid: %s). Ignoring.) and fall through.(user_config or _USER_CONFIG).get("defaults", {}).get("language"). If set, trySupportedLanguage(value). OnValueErrorlog a warning that namesUSER_CONFIG_PATHand fall through.return SupportedLanguage.ENGLISH.DEFAULT_LANGUAGE: SupportedLanguage = _resolve_default_language().Wire
DEFAULT_LANGUAGEintoaskcc/cli.py:122-128:default=SupportedLanguage.ENGLISH→default=settings.DEFAULT_LANGUAGE.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).askcc/cli.py:280(if args.language != SupportedLanguage.ENGLISH:) keeps working unchanged becauseSupportedLanguageis aStrEnumand equality is preserved.Update
README.md:ASKCC_LANGUAGErow to the Environment Variables table (README.md:96-101).~/.askcc/config.tomlexample and lists the four-level precedence for--language.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).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
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._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.Acceptance Criteria
ASKCC_LANGUAGEenvironment variable sets the default language~/.askcc/config.toml[defaults] languagesets the default languageASKCC_HOMEis honored when locating the user config fileuv run pytest,uv run ruff check, anduv run pyrightall passDependencies
None identified. Python 3.14 is already pinned (
pyproject.toml:8), sotomllibis available in the stdlib — no new third-party dependencies are introduced. TheSupportedLanguageenum relocation is internal-only.Out of scope
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.