Coverage for little_loops / init / core.py: 14%
43 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-26 17:38 -0500
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-26 17:38 -0500
1"""Config building for headless ll-init."""
3from __future__ import annotations
5from typing import Any
7from little_loops.init.detect import TemplateMatch
9SCHEMA_URL = (
10 "https://raw.githubusercontent.com/BrennonTWilliams/little-loops/main/config-schema.json"
11)
13_DEFAULT_ANALYTICS_CAPTURE: dict[str, Any] = {
14 "skills": ["*"],
15 "cli_commands": ["*"],
16 "corrections": True,
17 "file_events": True,
18}
21def build_config(
22 template: TemplateMatch,
23 choices: dict[str, Any] | None = None,
24) -> dict[str, Any]:
25 """Build the output ll-config.json dict from a template and optional choices.
27 Ports the skill's Step 4 and Step 8 config-building logic. The resulting
28 dict is ready for serialisation with ``atomic_write_json``.
30 Args:
31 template: Matched template from detect_project_type().
32 choices: Optional overrides. Recognised keys:
33 - ``project_name`` (str): value written to project.name.
34 - ``src_dir`` (str): override project.src_dir.
35 - ``product_enabled`` (bool, default True): include product section.
36 - ``analytics_enabled`` (bool, default True): include analytics section.
37 - ``context_monitor_enabled`` (bool, default True): include context_monitor.
38 - ``learning_tests_enabled`` (bool, default True): include learning_tests.
39 - ``decisions_enabled`` (bool, default False): include decisions section.
40 - ``scratch_pad_enabled`` (bool, default False): include scratch_pad section.
41 - ``session_capture_enabled`` (bool, default False): include session_capture.
42 - ``prompt_optimization_enabled`` (bool, default True): when False, write
43 prompt_optimization.enabled=false (opt-out of a default-on feature).
44 - ``loop_clear_default`` (bool, default True): write loops.run_defaults.clear.
45 - ``loop_show_diagrams_default`` (str | None, default "clean"): write loops.run_defaults.show_diagrams.
47 Returns:
48 Complete config dict (``$schema`` key first, then sections).
49 """
50 choices = choices or {}
51 data = template.data
53 config: dict[str, Any] = {"$schema": SCHEMA_URL}
55 # --- project ---
56 project: dict[str, Any] = dict(data.get("project", {}))
57 if choices.get("project_name"):
58 project["name"] = choices["project_name"]
59 if choices.get("src_dir"):
60 project["src_dir"] = choices["src_dir"]
61 config["project"] = project
63 # --- issues ---
64 config["issues"] = dict(data.get("issues", {}))
66 # --- scan ---
67 config["scan"] = dict(data.get("scan", {}))
69 # --- learning_tests (always written) ---
70 learning_tests_enabled = bool(choices.get("learning_tests_enabled", True))
71 config["learning_tests"] = {"enabled": learning_tests_enabled}
73 # --- analytics (always written) ---
74 analytics_enabled = bool(choices.get("analytics_enabled", True))
75 if analytics_enabled:
76 config["analytics"] = {
77 "enabled": True,
78 "capture": dict(_DEFAULT_ANALYTICS_CAPTURE),
79 }
80 else:
81 config["analytics"] = {"enabled": False}
83 # --- context_monitor (omit if disabled) ---
84 context_monitor_enabled = bool(choices.get("context_monitor_enabled", True))
85 if context_monitor_enabled:
86 config["context_monitor"] = {"enabled": True}
88 # --- product (omit if disabled; default True for --yes mode) ---
89 product_enabled = bool(choices.get("product_enabled", True))
90 if product_enabled:
91 config["product"] = {"enabled": True}
93 # --- decisions (opt-in; omit if disabled) ---
94 if choices.get("decisions_enabled"):
95 config["decisions"] = {"enabled": True}
97 # --- scratch_pad (opt-in; omit if disabled) ---
98 if choices.get("scratch_pad_enabled"):
99 config["scratch_pad"] = {"enabled": True}
101 # --- session_capture (opt-in; omit if disabled) ---
102 if choices.get("session_capture_enabled"):
103 config["session_capture"] = {"enabled": True}
105 # --- prompt_optimization (default-on; only write when opting out) ---
106 if choices.get("prompt_optimization_enabled", True) is False:
107 config["prompt_optimization"] = {"enabled": False}
109 # --- history.session_digest (always written) ---
110 session_digest_enabled = bool(choices.get("session_digest_enabled", True))
111 config["history"] = {
112 "session_digest": {
113 "enabled": session_digest_enabled,
114 "days": 7,
115 }
116 }
118 # --- loops.run_defaults (always written; exposes the feature at init time) ---
119 loop_clear = bool(choices.get("loop_clear_default", True))
120 loop_show_diagrams = choices.get("loop_show_diagrams_default", "clean")
121 config["loops"] = {
122 "run_defaults": {
123 "clear": loop_clear,
124 "show_diagrams": loop_show_diagrams,
125 "mode": None,
126 }
127 }
129 return config