synapse-cli-agent 0.1.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. synapse/__init__.py +13 -0
  2. synapse/__main__.py +6 -0
  3. synapse/app/__init__.py +1 -0
  4. synapse/app/agent.py +492 -0
  5. synapse/app/agent_md.py +107 -0
  6. synapse/cli.py +750 -0
  7. synapse/commands/__init__.py +1 -0
  8. synapse/commands/compression.py +573 -0
  9. synapse/commands/helpers.py +22 -0
  10. synapse/commands/mcp.py +406 -0
  11. synapse/commands/model.py +173 -0
  12. synapse/commands/result.py +34 -0
  13. synapse/commands/sessions.py +443 -0
  14. synapse/commands/slash_cmds.py +521 -0
  15. synapse/commands/slash_complete.py +816 -0
  16. synapse/commands/theme.py +99 -0
  17. synapse/config.py +27 -0
  18. synapse/content/__init__.py +1 -0
  19. synapse/content/input_history.py +122 -0
  20. synapse/content/multimodal.py +733 -0
  21. synapse/content/prompts.py +249 -0
  22. synapse/content/skills_catalog.py +128 -0
  23. synapse/integrations/__init__.py +1 -0
  24. synapse/integrations/checkpoint_seed.py +281 -0
  25. synapse/integrations/codex_history.py +375 -0
  26. synapse/integrations/codex_import.py +393 -0
  27. synapse/integrations/codex_sessions.py +629 -0
  28. synapse/integrations/describe_image.py +370 -0
  29. synapse/integrations/http_clients.py +199 -0
  30. synapse/integrations/llm_openai_compat.py +90 -0
  31. synapse/integrations/llm_openai_websocket.py +187 -0
  32. synapse/integrations/mcp_client.py +646 -0
  33. synapse/integrations/vision_middleware.py +62 -0
  34. synapse/models/__init__.py +5 -0
  35. synapse/models/config.py +240 -0
  36. synapse/models/helpers.py +206 -0
  37. synapse/models/profile.py +59 -0
  38. synapse/models/registry.py +722 -0
  39. synapse/models_registry.py +7 -0
  40. synapse/observability/__init__.py +1 -0
  41. synapse/observability/startup_trace.py +127 -0
  42. synapse/runtime/__init__.py +1 -0
  43. synapse/runtime/async_runtime.py +176 -0
  44. synapse/runtime/backends.py +458 -0
  45. synapse/runtime/context_compact.py +249 -0
  46. synapse/runtime/execute_capture.py +48 -0
  47. synapse/runtime/fs_permissions.py +79 -0
  48. synapse/runtime/harness.py +57 -0
  49. synapse/runtime/hitl.py +197 -0
  50. synapse/runtime/interaction_ledger.py +82 -0
  51. synapse/runtime/middleware.py +802 -0
  52. synapse/runtime/model_request_compression_middleware.py +745 -0
  53. synapse/runtime/pathing.py +146 -0
  54. synapse/runtime/safety.py +184 -0
  55. synapse/runtime/steer.py +240 -0
  56. synapse/runtime/subagents.py +207 -0
  57. synapse/runtime/tool_ignore.py +221 -0
  58. synapse/runtime/tool_output_eval.py +118 -0
  59. synapse/runtime/tool_output_middleware.py +585 -0
  60. synapse/runtime/tool_output_usage_middleware.py +60 -0
  61. synapse/sessions/__init__.py +31 -0
  62. synapse/sessions/cancel_repair.py +208 -0
  63. synapse/sessions/session_recap.py +174 -0
  64. synapse/sessions/store.py +695 -0
  65. synapse/sessions/transcript.py +754 -0
  66. synapse/settings/__init__.py +5 -0
  67. synapse/settings/config_paths.py +184 -0
  68. synapse/settings/schema.py +464 -0
  69. synapse/tool_output/__init__.py +59 -0
  70. synapse/tool_output/detection.py +170 -0
  71. synapse/tool_output/metrics.py +32 -0
  72. synapse/tool_output/models.py +173 -0
  73. synapse/tool_output/pipeline.py +330 -0
  74. synapse/tool_output/repository.py +721 -0
  75. synapse/tool_output/transformers.py +648 -0
  76. synapse/tools/__init__.py +5 -0
  77. synapse/tools/session_tools.py +204 -0
  78. synapse/ui/__init__.py +10 -0
  79. synapse/ui/bottombar/__init__.py +73 -0
  80. synapse/ui/bottombar/components/__init__.py +143 -0
  81. synapse/ui/bottombar/components/key_hints.py +30 -0
  82. synapse/ui/bottombar/components/mcp.py +64 -0
  83. synapse/ui/bottombar/components/mode.py +24 -0
  84. synapse/ui/bottombar/components/model.py +28 -0
  85. synapse/ui/bottombar/components/thread.py +29 -0
  86. synapse/ui/bottombar/context.py +36 -0
  87. synapse/ui/bottombar/core.py +74 -0
  88. synapse/ui/dialogs/__init__.py +25 -0
  89. synapse/ui/dialogs/base.py +362 -0
  90. synapse/ui/dialogs/codex_session_list.py +84 -0
  91. synapse/ui/dialogs/compression_diagnostics.py +210 -0
  92. synapse/ui/dialogs/git_explore.py +702 -0
  93. synapse/ui/dialogs/mcp_panel.py +407 -0
  94. synapse/ui/dialogs/model_picker.py +128 -0
  95. synapse/ui/dialogs/safety_panel.py +63 -0
  96. synapse/ui/dialogs/session_list.py +98 -0
  97. synapse/ui/dialogs/theme_designer.py +863 -0
  98. synapse/ui/dialogs/theme_picker.py +113 -0
  99. synapse/ui/git_explore/__init__.py +31 -0
  100. synapse/ui/git_explore/engine.py +82 -0
  101. synapse/ui/git_explore/provider.py +242 -0
  102. synapse/ui/git_explore/unified.py +85 -0
  103. synapse/ui/rendering.py +350 -0
  104. synapse/ui/sink.py +70 -0
  105. synapse/ui/steer_widget.py +367 -0
  106. synapse/ui/stream.py +1207 -0
  107. synapse/ui/stream_events.py +421 -0
  108. synapse/ui/stream_runtime.py +252 -0
  109. synapse/ui/theme.py +1154 -0
  110. synapse/ui/timeline.py +621 -0
  111. synapse/ui/topbar/__init__.py +97 -0
  112. synapse/ui/topbar/components/__init__.py +150 -0
  113. synapse/ui/topbar/components/branch.py +41 -0
  114. synapse/ui/topbar/components/title.py +24 -0
  115. synapse/ui/topbar/components/tool_output.py +24 -0
  116. synapse/ui/topbar/components/usage.py +24 -0
  117. synapse/ui/topbar/components/workspace.py +32 -0
  118. synapse/ui/topbar/context.py +32 -0
  119. synapse/ui/topbar/core.py +979 -0
  120. synapse/ui/topbar/git_changes_popover.py +178 -0
  121. synapse/ui/topbar/git_chrome.py +475 -0
  122. synapse/ui/topbar/tool_output_popover.py +84 -0
  123. synapse/ui/topbar/widget.py +474 -0
  124. synapse/ui/tui.py +5717 -0
  125. synapse/ui/turn_rail.py +71 -0
  126. synapse/ui/user_turn.py +83 -0
  127. synapse/ui/welcome.py +261 -0
  128. synapse_cli_agent-0.1.13.dist-info/METADATA +412 -0
  129. synapse_cli_agent-0.1.13.dist-info/RECORD +131 -0
  130. synapse_cli_agent-0.1.13.dist-info/WHEEL +4 -0
  131. synapse_cli_agent-0.1.13.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,99 @@
1
+ """Theme selection slash-command handler."""
2
+ from __future__ import annotations
3
+
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from synapse.commands.helpers import markdown_escape
8
+ from synapse.commands.result import SlashResult
9
+
10
+
11
+ def handle_theme(
12
+ args: list[str],
13
+ *,
14
+ settings: Any,
15
+ project_root: Path,
16
+ ) -> SlashResult:
17
+ """List or switch UI themes; persist selection to user settings.json."""
18
+ from synapse.ui.theme import (
19
+ format_theme_list_lines,
20
+ get_theme,
21
+ list_theme_names,
22
+ reload_theme_catalog,
23
+ set_theme,
24
+ )
25
+
26
+ reload_theme_catalog(project_root)
27
+ if not args or args[0].casefold() in {"list", "ls", "show"}:
28
+ active = getattr(settings, "theme", None) or get_theme().name
29
+ plain = format_theme_list_lines(active=active)
30
+ from synapse.ui.theme import list_themes, theme_kind
31
+
32
+ md = (
33
+ "## Themes\n\n"
34
+ f"**current**: `{active}`\n\n"
35
+ "| | Name | Label | Kind |\n|---|---|---|---|\n"
36
+ )
37
+ for t in list_themes():
38
+ mark = "*" if t.name == active else " "
39
+ tone = theme_kind(t)
40
+ from synapse.ui.theme import BUILTIN_THEMES, _custom
41
+
42
+ if t.name in _custom and t.name not in BUILTIN_THEMES:
43
+ kind = f"custom/{tone}"
44
+ elif t.name in _custom:
45
+ kind = f"override/{tone}"
46
+ else:
47
+ kind = f"built-in/{tone}"
48
+ md += (
49
+ f"| {mark} | `{markdown_escape(t.name)}` "
50
+ f"| {markdown_escape(t.label)} | {markdown_escape(kind)} |\n"
51
+ )
52
+ md += "\nusage: `/theme <name>`\n\nconfig: `settings.json` theme + optional `themes.json`"
53
+ return SlashResult(handled=True, lines=plain, markdown=md)
54
+
55
+ name = args[0].strip()
56
+ # Optional: /theme set <name> | /theme use <name>
57
+ if name.casefold() in {"set", "use", "switch"} and len(args) >= 2:
58
+ name = args[1].strip()
59
+ try:
60
+ theme = set_theme(
61
+ name,
62
+ workspace=project_root,
63
+ persist=True,
64
+ scope="user",
65
+ reload=False,
66
+ )
67
+ except KeyError as exc:
68
+ names = ", ".join(list_theme_names())
69
+ return SlashResult(
70
+ handled=True,
71
+ lines=[str(exc), f"available: {names}"],
72
+ error=True,
73
+ )
74
+ except Exception as exc: # noqa: BLE001
75
+ return SlashResult(
76
+ handled=True,
77
+ lines=[f"theme switch failed: {exc}"],
78
+ error=True,
79
+ )
80
+
81
+ try:
82
+ settings.theme = theme.name
83
+ except Exception: # noqa: BLE001
84
+ pass
85
+ return SlashResult(
86
+ handled=True,
87
+ lines=[
88
+ f"theme switched to {theme.name} ({theme.label})",
89
+ "saved to ~/.coding-agent/settings.json",
90
+ ],
91
+ markdown=(
92
+ "## Theme\n\n"
93
+ f"- **name**: `{markdown_escape(theme.name)}`\n"
94
+ f"- **label**: {markdown_escape(theme.label)}\n\n"
95
+ "*saved to `~/.coding-agent/settings.json`*"
96
+ ),
97
+ settings_changed=True,
98
+ theme_name=theme.name,
99
+ )
synapse/config.py ADDED
@@ -0,0 +1,27 @@
1
+ """Compatibility exports for the settings domain.
2
+
3
+ New code should import from :mod:`synapse.settings`. This module remains so
4
+ existing integrations and extension code do not need an atomic migration.
5
+ """
6
+
7
+ from synapse.settings.schema import (
8
+ Settings,
9
+ bootstrap_project_env,
10
+ config_search_roots,
11
+ executable_config_dirs,
12
+ find_dotenv,
13
+ load_settings,
14
+ project_config_dir,
15
+ user_config_dir,
16
+ )
17
+
18
+ __all__ = [
19
+ "Settings",
20
+ "bootstrap_project_env",
21
+ "config_search_roots",
22
+ "executable_config_dirs",
23
+ "find_dotenv",
24
+ "load_settings",
25
+ "project_config_dir",
26
+ "user_config_dir",
27
+ ]
@@ -0,0 +1 @@
1
+ """Prompt, skill, input, and multimodal content services."""
@@ -0,0 +1,122 @@
1
+ """Project-scoped input history for chat CLI / TUI (up/down navigation)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+
8
+ def _read_text_lossy(path: Path) -> str:
9
+ """Read history file, tolerating legacy Windows encodings (GBK etc.)."""
10
+ data = path.read_bytes()
11
+ if not data:
12
+ return ""
13
+ # UTF-8 BOM
14
+ if data.startswith(b"\xef\xbb\xbf"):
15
+ return data.decode("utf-8-sig")
16
+ # UTF-16 LE/BE BOM
17
+ if data.startswith(b"\xff\xfe"):
18
+ return data.decode("utf-16-le")
19
+ if data.startswith(b"\xfe\xff"):
20
+ return data.decode("utf-16-be")
21
+ for enc in ("utf-8", "gbk", "cp936", "gb18030", "cp1252", "latin-1"):
22
+ try:
23
+ return data.decode(enc)
24
+ except UnicodeDecodeError:
25
+ continue
26
+ return data.decode("utf-8", errors="replace")
27
+
28
+
29
+ class InputHistory:
30
+ """Line-based history stored under the project ``.synapse/`` dir.
31
+
32
+ Shares the same default file as the readline chat CLI
33
+ (``.synapse/history``) so entries accumulate across UIs.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ path: Path,
39
+ *,
40
+ max_entries: int = 1000,
41
+ ) -> None:
42
+ self.path = Path(path)
43
+ self.max_entries = max(1, int(max_entries))
44
+ self.entries: list[str] = []
45
+ self._index: int | None = None
46
+ self._draft: str = ""
47
+ self.load()
48
+
49
+ @classmethod
50
+ def for_project(cls, project_root: Path | None = None, **kwargs) -> InputHistory:
51
+ from synapse.settings.config_paths import SYNAPSE_DIRNAME
52
+
53
+ root = Path(project_root or Path.cwd()).expanduser().resolve()
54
+ return cls(root / SYNAPSE_DIRNAME / "history", **kwargs)
55
+
56
+ def load(self) -> None:
57
+ self.entries = []
58
+ try:
59
+ raw = _read_text_lossy(self.path)
60
+ except (FileNotFoundError, OSError):
61
+ return
62
+ for line in raw.splitlines():
63
+ text = line.rstrip("\n\r")
64
+ if not text or text.startswith("_HiStOrY_"):
65
+ continue
66
+ # readline may escape leading spaces; keep content as-is after strip of
67
+ # trailing junk only. Preserve intentional leading spaces rarely used.
68
+ self.entries.append(text)
69
+ if len(self.entries) > self.max_entries:
70
+ self.entries = self.entries[-self.max_entries :]
71
+
72
+ def save(self) -> None:
73
+ try:
74
+ self.path.parent.mkdir(parents=True, exist_ok=True)
75
+ body = "\n".join(self.entries[-self.max_entries :])
76
+ if body:
77
+ body += "\n"
78
+ self.path.write_text(body, encoding="utf-8")
79
+ except OSError:
80
+ pass
81
+
82
+ def add(self, text: str) -> None:
83
+ line = (text or "").strip()
84
+ if not line:
85
+ self.reset_cursor()
86
+ return
87
+ if self.entries and self.entries[-1] == line:
88
+ self.reset_cursor()
89
+ return
90
+ self.entries.append(line)
91
+ if len(self.entries) > self.max_entries:
92
+ self.entries = self.entries[-self.max_entries :]
93
+ self.save()
94
+ self.reset_cursor()
95
+
96
+ def reset_cursor(self) -> None:
97
+ self._index = None
98
+ self._draft = ""
99
+
100
+ def up(self, current: str) -> str | None:
101
+ """Move to older entry. Returns text to put in the prompt, or None."""
102
+ if not self.entries:
103
+ return None
104
+ if self._index is None:
105
+ self._draft = current or ""
106
+ self._index = len(self.entries) - 1
107
+ elif self._index > 0:
108
+ self._index -= 1
109
+ return self.entries[self._index]
110
+
111
+ def down(self, current: str) -> str | None: # noqa: ARG002
112
+ """Move to newer entry, or restore the draft when leaving history."""
113
+ if self._index is None:
114
+ return None
115
+ if self._index < len(self.entries) - 1:
116
+ self._index += 1
117
+ return self.entries[self._index]
118
+ # Past the newest → restore live draft
119
+ self._index = None
120
+ draft = self._draft
121
+ self._draft = ""
122
+ return draft