Coverage for little_loops / init / core.py: 18%

33 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-06-16 13:11 -0500

1"""Config building for headless ll-init.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from little_loops.init.detect import TemplateMatch 

8 

9SCHEMA_URL = ( 

10 "https://raw.githubusercontent.com/BrennonTWilliams/little-loops/main/config-schema.json" 

11) 

12 

13_DEFAULT_ANALYTICS_CAPTURE: dict[str, Any] = { 

14 "skills": ["*"], 

15 "cli_commands": ["*"], 

16 "corrections": True, 

17 "file_events": True, 

18} 

19 

20 

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. 

26 

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``. 

29 

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 

40 Returns: 

41 Complete config dict (``$schema`` key first, then sections). 

42 """ 

43 choices = choices or {} 

44 data = template.data 

45 

46 config: dict[str, Any] = {"$schema": SCHEMA_URL} 

47 

48 # --- project --- 

49 project: dict[str, Any] = dict(data.get("project", {})) 

50 if choices.get("project_name"): 

51 project["name"] = choices["project_name"] 

52 if choices.get("src_dir"): 

53 project["src_dir"] = choices["src_dir"] 

54 config["project"] = project 

55 

56 # --- issues --- 

57 config["issues"] = dict(data.get("issues", {})) 

58 

59 # --- scan --- 

60 config["scan"] = dict(data.get("scan", {})) 

61 

62 # --- learning_tests (always written) --- 

63 learning_tests_enabled = bool(choices.get("learning_tests_enabled", True)) 

64 config["learning_tests"] = {"enabled": learning_tests_enabled} 

65 

66 # --- analytics (always written) --- 

67 analytics_enabled = bool(choices.get("analytics_enabled", True)) 

68 if analytics_enabled: 

69 config["analytics"] = { 

70 "enabled": True, 

71 "capture": dict(_DEFAULT_ANALYTICS_CAPTURE), 

72 } 

73 else: 

74 config["analytics"] = {"enabled": False} 

75 

76 # --- context_monitor (omit if disabled) --- 

77 context_monitor_enabled = bool(choices.get("context_monitor_enabled", True)) 

78 if context_monitor_enabled: 

79 config["context_monitor"] = {"enabled": True} 

80 

81 # --- product (omit if disabled; default True for --yes mode) --- 

82 product_enabled = bool(choices.get("product_enabled", True)) 

83 if product_enabled: 

84 config["product"] = {"enabled": True} 

85 

86 # --- history.session_digest (always written) --- 

87 session_digest_enabled = bool(choices.get("session_digest_enabled", True)) 

88 config["history"] = { 

89 "session_digest": { 

90 "enabled": session_digest_enabled, 

91 "days": 7, 

92 "char_cap": 1200, 

93 } 

94 } 

95 

96 # --- loops.run_defaults (always written; exposes the feature at init time) --- 

97 config["loops"] = { 

98 "run_defaults": { 

99 "clear": False, 

100 "show_diagrams": None, 

101 "mode": None, 

102 } 

103 } 

104 

105 return config