context-forge-cli 0.4.0__tar.gz → 0.4.2__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.
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/PKG-INFO +1 -1
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/adapters/codex.py +24 -4
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/tokens_panel.py +8 -5
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/utils/tokens.py +10 -1
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/pyproject.toml +1 -1
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/.contextforge.toml +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/.github/workflows/publish.yml +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/.gitignore +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/.python-version +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/AGENTS.md +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/PLAN.md +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/README.md +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/adapters/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/adapters/altimate_code.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/adapters/base.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/adapters/claude_code.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/adapters/claude_desktop.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/adapters/gemini.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/adapters/registry.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/cli.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/core/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/core/analytics.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/core/compactor.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/core/db.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/core/injector.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/core/scanner.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/core/summarizer.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/core/token_analyzer.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/mcp_server.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/models/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/models/config.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/models/session.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/app.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/styles.tcss +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/session_detail.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/session_table.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/stats_panel.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/status_bar.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/transfer_panel.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/utils/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/utils/display.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/adapters/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/adapters/test_claude_code.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/conftest.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/core/__init__.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/core/test_compactor.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/core/test_db.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/core/test_injector.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/core/test_summarizer.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/core/test_token_analyzer.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/fixtures/claude_session_sample.jsonl +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/test_mcp_server.py +0 -0
- {context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: context-forge-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
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
|
|
@@ -50,19 +50,39 @@ def _compute_message_tokens(msg: Message) -> int:
|
|
|
50
50
|
return total
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def _extract_function_output(raw_output
|
|
53
|
+
def _extract_function_output(raw_output) -> str:
|
|
54
54
|
"""Extract the human-readable output from a Codex function_call_output payload.
|
|
55
55
|
|
|
56
|
-
The `output` field
|
|
57
|
-
|
|
56
|
+
The `output` field may be:
|
|
57
|
+
- A JSON string: {"output": "...", "metadata": {...}}
|
|
58
|
+
- An already-parsed list of content blocks (e.g. input_text/input_image)
|
|
59
|
+
- An already-parsed dict
|
|
60
|
+
Returns a plain string suitable for token counting.
|
|
58
61
|
"""
|
|
62
|
+
if isinstance(raw_output, list):
|
|
63
|
+
# Content-block array — extract text parts, skip images
|
|
64
|
+
parts = [
|
|
65
|
+
block.get("text", "")
|
|
66
|
+
for block in raw_output
|
|
67
|
+
if isinstance(block, dict) and block.get("type") in ("text", "input_text")
|
|
68
|
+
]
|
|
69
|
+
return "\n".join(p for p in parts if p)
|
|
70
|
+
if isinstance(raw_output, dict):
|
|
71
|
+
return str(raw_output.get("output", ""))
|
|
59
72
|
try:
|
|
60
73
|
parsed = json.loads(raw_output)
|
|
61
74
|
if isinstance(parsed, dict):
|
|
62
75
|
return str(parsed.get("output", raw_output))
|
|
76
|
+
if isinstance(parsed, list):
|
|
77
|
+
parts = [
|
|
78
|
+
block.get("text", "")
|
|
79
|
+
for block in parsed
|
|
80
|
+
if isinstance(block, dict) and block.get("type") in ("text", "input_text")
|
|
81
|
+
]
|
|
82
|
+
return "\n".join(p for p in parts if p)
|
|
63
83
|
except (json.JSONDecodeError, TypeError):
|
|
64
84
|
pass
|
|
65
|
-
return raw_output
|
|
85
|
+
return str(raw_output) if raw_output is not None else ""
|
|
66
86
|
|
|
67
87
|
|
|
68
88
|
class CodexAdapter(ToolAdapter):
|
{context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/tokens_panel.py
RENAMED
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
import json
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
from rich.markup import escape
|
|
7
8
|
from textual.app import ComposeResult
|
|
8
9
|
from textual.binding import Binding
|
|
9
10
|
from textual.containers import ScrollableContainer, Vertical
|
|
@@ -98,7 +99,7 @@ class TurnDetailPanel(ModalScreen):
|
|
|
98
99
|
# ── Text content ────────────────────────────────────────────────────
|
|
99
100
|
if msg.content:
|
|
100
101
|
parts.append("[bold dim]── Content ──[/bold dim]")
|
|
101
|
-
parts.append(msg.content)
|
|
102
|
+
parts.append(escape(msg.content))
|
|
102
103
|
|
|
103
104
|
# ── Tool calls ──────────────────────────────────────────────────────
|
|
104
105
|
if msg.tool_calls:
|
|
@@ -109,17 +110,19 @@ class TurnDetailPanel(ModalScreen):
|
|
|
109
110
|
try:
|
|
110
111
|
formatted = json.dumps(json.loads(raw_input), indent=2)
|
|
111
112
|
except (json.JSONDecodeError, TypeError):
|
|
112
|
-
formatted = raw_input
|
|
113
|
-
parts.append(f"[bold yellow]{i}. {name}[/bold yellow]")
|
|
114
|
-
parts.append(f"[dim]{formatted}[/dim]")
|
|
113
|
+
formatted = raw_input if isinstance(raw_input, str) else str(raw_input)
|
|
114
|
+
parts.append(f"[bold yellow]{i}. {escape(name)}[/bold yellow]")
|
|
115
|
+
parts.append(f"[dim]{escape(formatted)}[/dim]")
|
|
115
116
|
|
|
116
117
|
# ── Tool results ────────────────────────────────────────────────────
|
|
117
118
|
if msg.tool_results:
|
|
118
119
|
parts.append("\n[bold dim]── Tool Results ──[/bold dim]")
|
|
119
120
|
for i, tr in enumerate(msg.tool_results, 1):
|
|
120
121
|
output = tr.get("output", "")
|
|
122
|
+
if not isinstance(output, str):
|
|
123
|
+
output = str(output)
|
|
121
124
|
parts.append(f"[bold green]Result {i}[/bold green]")
|
|
122
|
-
parts.append(f"[dim]{output}[/dim]")
|
|
125
|
+
parts.append(f"[dim]{escape(output)}[/dim]")
|
|
123
126
|
|
|
124
127
|
self.query_one("#turn-body", Static).update("\n".join(parts))
|
|
125
128
|
|
|
@@ -13,7 +13,16 @@ def _get_encoding() -> tiktoken.Encoding:
|
|
|
13
13
|
return _ENCODING
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def count_tokens(text
|
|
16
|
+
def count_tokens(text) -> int:
|
|
17
|
+
if not isinstance(text, str):
|
|
18
|
+
if isinstance(text, list):
|
|
19
|
+
text = " ".join(
|
|
20
|
+
block.get("text", "")
|
|
21
|
+
for block in text
|
|
22
|
+
if isinstance(block, dict) and block.get("type") in ("text", "input_text")
|
|
23
|
+
)
|
|
24
|
+
else:
|
|
25
|
+
text = str(text) if text is not None else ""
|
|
17
26
|
return len(_get_encoding().encode(text))
|
|
18
27
|
|
|
19
28
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/session_detail.py
RENAMED
|
File without changes
|
{context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/session_table.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/contextforge/tui/widgets/transfer_panel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{context_forge_cli-0.4.0 → context_forge_cli-0.4.2}/tests/fixtures/claude_session_sample.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|