feat: add content targets and loop expansion for target templates
All checks were successful
Continuous Integration / Build Package (push) Successful in 25s
Continuous Integration / Lint, Check & Test (push) Successful in 44s

Content targets write literal text to files via 'content:' field,
without requiring an AI provider or API keys. They are not archived
when overwritten.

Loop expansion allows defining 'loops:' at the top level with named
lists of values. Targets with [var] in their name are expanded via
cartesian product. Variables are substituted in all string fields.
Explicit targets override expanded ones. Escaping: \[var] -> [var].
Expansion happens at config load time so the rest of the system
(builder, graph, state) sees only expanded targets.
This commit is contained in:
Konstantin Fickel 2026-02-21 18:39:13 +01:00
parent bb03975ece
commit 7503672942
Signed by: kfickel
GPG key ID: A793722F9933C1A5
7 changed files with 581 additions and 2 deletions

View file

@ -84,6 +84,41 @@ class TestLoadConfig:
with pytest.raises(Exception):
_ = load_config(config_path)
def test_config_with_loops(self, project_dir: Path) -> None:
config_path = project_dir / "test.hokusai.yaml"
_ = config_path.write_text(
yaml.dump(
{
"loops": {"a": [1, 2]},
"targets": {"file-[a].txt": {"content": "Value [a]"}},
}
)
)
config = load_config(config_path)
assert "file-1.txt" in config.targets
assert "file-2.txt" in config.targets
assert "file-[a].txt" not in config.targets
t1 = config.targets["file-1.txt"]
assert isinstance(t1, ContentTargetConfig)
assert t1.content == "Value 1"
def test_config_loops_normalize_values_to_strings(self, project_dir: Path) -> None:
config_path = project_dir / "test.hokusai.yaml"
_ = config_path.write_text(
yaml.dump(
{
"loops": {"x": [True, 3.14]},
"targets": {"out-[x].txt": {"content": "[x]"}},
}
)
)
config = load_config(config_path)
assert "out-True.txt" in config.targets
assert "out-3.14.txt" in config.targets
def test_content_target(self, project_dir: Path) -> None:
config_path = project_dir / "test.hokusai.yaml"
_ = config_path.write_text(