context-forge-cli 0.4.8__tar.gz → 0.4.9__tar.gz

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 (58) hide show
  1. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/PKG-INFO +1 -1
  2. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/widgets/session_detail.py +16 -5
  3. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/widgets/tokens_panel.py +13 -18
  4. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/pyproject.toml +1 -1
  5. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/.contextforge.toml +0 -0
  6. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/.github/workflows/publish.yml +0 -0
  7. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/.gitignore +0 -0
  8. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/.python-version +0 -0
  9. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/AGENTS.md +0 -0
  10. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/PLAN.md +0 -0
  11. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/README.md +0 -0
  12. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/__init__.py +0 -0
  13. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/adapters/__init__.py +0 -0
  14. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/adapters/altimate_code.py +0 -0
  15. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/adapters/base.py +0 -0
  16. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/adapters/claude_code.py +0 -0
  17. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/adapters/claude_desktop.py +0 -0
  18. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/adapters/codex.py +0 -0
  19. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/adapters/gemini.py +0 -0
  20. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/adapters/registry.py +0 -0
  21. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/cli.py +0 -0
  22. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/core/__init__.py +0 -0
  23. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/core/analytics.py +0 -0
  24. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/core/compactor.py +0 -0
  25. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/core/db.py +0 -0
  26. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/core/injector.py +0 -0
  27. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/core/scanner.py +0 -0
  28. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/core/summarizer.py +0 -0
  29. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/core/token_analyzer.py +0 -0
  30. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/mcp_server.py +0 -0
  31. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/models/__init__.py +0 -0
  32. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/models/config.py +0 -0
  33. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/models/session.py +0 -0
  34. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/__init__.py +0 -0
  35. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/app.py +0 -0
  36. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/clipboard.py +0 -0
  37. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/styles.tcss +0 -0
  38. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/widgets/__init__.py +0 -0
  39. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/widgets/session_table.py +0 -0
  40. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/widgets/stats_panel.py +0 -0
  41. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/widgets/status_bar.py +0 -0
  42. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/tui/widgets/transfer_panel.py +0 -0
  43. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/utils/__init__.py +0 -0
  44. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/utils/display.py +0 -0
  45. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/contextforge/utils/tokens.py +0 -0
  46. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/__init__.py +0 -0
  47. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/adapters/__init__.py +0 -0
  48. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/adapters/test_claude_code.py +0 -0
  49. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/conftest.py +0 -0
  50. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/core/__init__.py +0 -0
  51. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/core/test_compactor.py +0 -0
  52. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/core/test_db.py +0 -0
  53. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/core/test_injector.py +0 -0
  54. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/core/test_summarizer.py +0 -0
  55. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/core/test_token_analyzer.py +0 -0
  56. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/fixtures/claude_session_sample.jsonl +0 -0
  57. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/tests/test_mcp_server.py +0 -0
  58. {context_forge_cli-0.4.8 → context_forge_cli-0.4.9}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: context-forge-cli
3
- Version: 0.4.8
3
+ Version: 0.4.9
4
4
  Summary: Session manager and context bridge for agentic CLI tools
5
5
  Project-URL: Homepage, https://github.com/emmver/contextforge
6
6
  Project-URL: Repository, https://github.com/emmver/contextforge
@@ -8,7 +8,7 @@ from pathlib import Path
8
8
  from textual.app import ComposeResult
9
9
  from textual.containers import Vertical
10
10
  from textual.widget import Widget
11
- from textual.widgets import Markdown, Static
11
+ from textual.widgets import Markdown, Static, TextArea
12
12
 
13
13
  TOOL_COLORS = {
14
14
  "claude_code": "cyan",
@@ -101,6 +101,17 @@ class SessionDetail(Widget):
101
101
  background: transparent;
102
102
  color: $text;
103
103
  }
104
+ SessionDetail TextArea {
105
+ height: 1;
106
+ border: none;
107
+ background: transparent;
108
+ color: $text;
109
+ padding: 0;
110
+ margin: 0;
111
+ }
112
+ SessionDetail TextArea:focus {
113
+ border: none;
114
+ }
104
115
  """
105
116
 
106
117
  _current_id: str | None = None
@@ -110,13 +121,13 @@ class SessionDetail(Widget):
110
121
  yield Static("", id="detail-title")
111
122
  with Vertical(id="detail-meta-block"):
112
123
  yield Static("", id="detail-tool")
113
- yield Static("", id="detail-cwd")
124
+ yield TextArea("", id="detail-cwd", read_only=True)
114
125
  yield Static("", id="detail-tokens")
115
126
  yield Static("", id="detail-created")
116
127
  yield Static("", id="detail-updated")
117
128
  yield Static("", id="detail-status")
118
129
  yield Static("", id="detail-tags")
119
- yield Static("", id="detail-id")
130
+ yield TextArea("", id="detail-id", read_only=True)
120
131
  yield Static("✎ Summary", id="detail-summary-label")
121
132
  yield Markdown("", id="detail-summary-md")
122
133
 
@@ -176,13 +187,13 @@ class SessionDetail(Widget):
176
187
 
177
188
  self.query_one("#detail-title", Static).update(f"[bold]{title}[/bold]")
178
189
  self.query_one("#detail-tool", Static).update(_meta_line("Tool", tool_markup))
179
- self.query_one("#detail-cwd", Static).update(_meta_line("Project", f"[dim]{cwd}[/dim]"))
190
+ self.query_one("#detail-cwd", TextArea).load_text("Project " + cwd)
180
191
  self.query_one("#detail-tokens", Static).update(_meta_line("Tokens", tok_markup))
181
192
  self.query_one("#detail-created", Static).update(_meta_line("Created", f"[dim]{created_str}[/dim]"))
182
193
  self.query_one("#detail-updated", Static).update(_meta_line("Updated", f"[dim]{updated_str}[/dim]"))
183
194
  self.query_one("#detail-status", Static).update(_meta_line("Status", f"[dim]{status}[/dim]"))
184
195
  self.query_one("#detail-tags", Static).update(_meta_line("Tags", f"[dim]{tags}[/dim]"))
185
- self.query_one("#detail-id", Static).update(_meta_line("ID", f"[dim]{session_id}[/dim]"))
196
+ self.query_one("#detail-id", TextArea).load_text("ID " + session_id)
186
197
 
187
198
  md = self.query_one("#detail-summary-md", Markdown)
188
199
  self.app.call_later(md.update, summary)
@@ -4,12 +4,11 @@ from __future__ import annotations
4
4
  import json
5
5
  from pathlib import Path
6
6
 
7
- from rich.text import Text
8
7
  from textual.app import ComposeResult
9
8
  from textual.binding import Binding
10
- from textual.containers import ScrollableContainer, Vertical
9
+ from textual.containers import Vertical
11
10
  from textual.screen import ModalScreen
12
- from textual.widgets import DataTable, Static
11
+ from textual.widgets import DataTable, Static, TextArea
13
12
 
14
13
  from contextforge.models.session import Message
15
14
  from contextforge.tui.clipboard import copy as clipboard_copy
@@ -76,10 +75,16 @@ class TurnDetailPanel(ModalScreen):
76
75
  border-bottom: solid $primary-background-lighten-1;
77
76
  margin-bottom: 1;
78
77
  }
79
- TurnDetailPanel ScrollableContainer {
78
+ TurnDetailPanel TextArea {
80
79
  height: 1fr;
80
+ border: none;
81
+ background: transparent;
82
+ color: $text;
81
83
  padding: 0 1;
82
84
  }
85
+ TurnDetailPanel TextArea:focus {
86
+ border: none;
87
+ }
83
88
  TurnDetailPanel #turn-footer {
84
89
  height: 1;
85
90
  color: $text-disabled;
@@ -96,9 +101,8 @@ class TurnDetailPanel(ModalScreen):
96
101
  def compose(self) -> ComposeResult:
97
102
  with Vertical():
98
103
  yield Static("", id="turn-header")
99
- with ScrollableContainer():
100
- yield Static("", id="turn-body")
101
- yield Static("[dim]ESC / q — close Y — copy[/dim]", id="turn-footer")
104
+ yield TextArea("", id="turn-body", read_only=True)
105
+ yield Static("[dim]ESC / q — close select & Ctrl+C[/dim]", id="turn-footer")
102
106
 
103
107
  def on_mount(self) -> None:
104
108
  msg = self._msg
@@ -120,17 +124,13 @@ class TurnDetailPanel(ModalScreen):
120
124
  if msg.tool_results else "")
121
125
  )
122
126
 
123
- body = Text()
124
127
  plain_parts = []
125
128
 
126
129
  if msg.content:
127
- body.append("── Content ──\n", style="bold dim")
128
- body.append(msg.content + "\n")
129
130
  plain_parts.append("── Content ──\n")
130
131
  plain_parts.append(msg.content + "\n")
131
132
 
132
133
  if msg.tool_calls:
133
- body.append("\n── Tool Calls ──\n", style="bold dim")
134
134
  plain_parts.append("\n── Tool Calls ──\n")
135
135
  for i, tc in enumerate(msg.tool_calls, 1):
136
136
  name = tc.get("name", "?")
@@ -139,28 +139,23 @@ class TurnDetailPanel(ModalScreen):
139
139
  formatted = json.dumps(json.loads(raw_input), indent=2)
140
140
  except (json.JSONDecodeError, TypeError):
141
141
  formatted = raw_input if isinstance(raw_input, str) else str(raw_input)
142
- body.append(f"⚙ {i}. {name}\n", style="bold yellow")
143
- body.append(formatted + "\n", style="dim")
144
142
  plain_parts.append(f"⚙ {i}. {name}\n")
145
143
  plain_parts.append(formatted + "\n")
146
144
 
147
145
  if msg.tool_results:
148
- body.append("\n── Tool Results ──\n", style="bold dim")
149
146
  plain_parts.append("\n── Tool Results ──\n")
150
147
  for i, tr in enumerate(msg.tool_results, 1):
151
148
  output = tr.get("output", "")
152
149
  if not isinstance(output, str):
153
150
  output = str(output)
154
- body.append(f"⇥ Result {i}\n", style="bold green")
155
- body.append(output + "\n", style="dim")
156
151
  plain_parts.append(f"⇥ Result {i}\n")
157
152
  plain_parts.append(output + "\n")
158
153
 
159
154
  self._plain_text = "".join(plain_parts)
160
- self.query_one("#turn-body", Static).update(body)
155
+ self.query_one("#turn-body", TextArea).load_text(self._plain_text)
161
156
 
162
157
  def action_copy_content(self) -> None:
163
- """Copy the turn content to the clipboard."""
158
+ """Copy the full turn content to the clipboard."""
164
159
  if clipboard_copy(self._plain_text):
165
160
  self.notify(f"Copied turn #{self._turn_num} content", title="Copy")
166
161
  else:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "context-forge-cli"
3
- version = "0.4.8"
3
+ version = "0.4.9"
4
4
  description = "Session manager and context bridge for agentic CLI tools"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }