context-forge-cli 0.3.0__tar.gz → 0.4.0__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.3.0 → context_forge_cli-0.4.0}/PKG-INFO +1 -1
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/core/db.py +12 -2
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/mcp_server.py +120 -32
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/pyproject.toml +1 -1
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/.contextforge.toml +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/.github/workflows/publish.yml +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/.gitignore +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/.python-version +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/AGENTS.md +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/PLAN.md +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/README.md +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/adapters/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/adapters/altimate_code.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/adapters/base.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/adapters/claude_code.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/adapters/claude_desktop.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/adapters/codex.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/adapters/gemini.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/adapters/registry.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/cli.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/core/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/core/analytics.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/core/compactor.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/core/injector.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/core/scanner.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/core/summarizer.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/core/token_analyzer.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/models/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/models/config.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/models/session.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/app.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/styles.tcss +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/session_detail.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/session_table.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/stats_panel.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/status_bar.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/tokens_panel.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/transfer_panel.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/utils/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/utils/display.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/utils/tokens.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/adapters/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/adapters/test_claude_code.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/conftest.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/core/__init__.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/core/test_compactor.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/core/test_db.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/core/test_injector.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/core/test_summarizer.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/core/test_token_analyzer.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/fixtures/claude_session_sample.jsonl +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/test_mcp_server.py +0 -0
- {context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: context-forge-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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
|
|
@@ -115,14 +115,24 @@ def get_sessions(
|
|
|
115
115
|
db: sqlite_utils.Database,
|
|
116
116
|
tool: str | None = None,
|
|
117
117
|
limit: int = 200,
|
|
118
|
+
offset: int = 0,
|
|
119
|
+
since_ms: int | None = None,
|
|
118
120
|
) -> list[dict]:
|
|
119
|
-
|
|
120
|
-
params = [
|
|
121
|
+
clauses: list[str] = []
|
|
122
|
+
params: list = []
|
|
123
|
+
if tool:
|
|
124
|
+
clauses.append("tool = ?")
|
|
125
|
+
params.append(tool)
|
|
126
|
+
if since_ms is not None:
|
|
127
|
+
clauses.append("updated_at >= ?")
|
|
128
|
+
params.append(since_ms)
|
|
129
|
+
where = " AND ".join(clauses) if clauses else None
|
|
121
130
|
rows = db["sessions"].rows_where(
|
|
122
131
|
where,
|
|
123
132
|
params,
|
|
124
133
|
order_by="updated_at desc",
|
|
125
134
|
limit=limit,
|
|
135
|
+
offset=offset,
|
|
126
136
|
)
|
|
127
137
|
return list(rows)
|
|
128
138
|
|
|
@@ -14,38 +14,83 @@ mcp = FastMCP(
|
|
|
14
14
|
"contextforge",
|
|
15
15
|
instructions=(
|
|
16
16
|
"ContextForge provides read-only access to indexed agentic-tool sessions "
|
|
17
|
-
"and their token usage. Start with list_sessions to discover what's available
|
|
18
|
-
"
|
|
19
|
-
"for
|
|
17
|
+
"and their token usage. Start with list_sessions to discover what's available "
|
|
18
|
+
"(supports pagination via offset/limit and date filtering via window). "
|
|
19
|
+
"Use get_session_tokens for token stats — pass include_turns=True with "
|
|
20
|
+
"turns_limit/turns_offset to page through per-turn data without blowing up "
|
|
21
|
+
"the context window. Use get_token_analytics for aggregated stats across all tools."
|
|
20
22
|
),
|
|
21
23
|
)
|
|
22
24
|
|
|
25
|
+
_VALID_WINDOWS = {"7d", "30d", "6m", "1y"}
|
|
26
|
+
|
|
23
27
|
|
|
24
28
|
def _db():
|
|
25
29
|
cfg = ForgeConfig()
|
|
26
30
|
return db_module.get_db(cfg.db_path)
|
|
27
31
|
|
|
28
32
|
|
|
33
|
+
def _short_cwd(cwd: str) -> str:
|
|
34
|
+
"""Return a short project label from a full cwd path.
|
|
35
|
+
|
|
36
|
+
Returns the last path component, or '~' for the user home directory,
|
|
37
|
+
or '' for empty paths.
|
|
38
|
+
"""
|
|
39
|
+
if not cwd:
|
|
40
|
+
return ""
|
|
41
|
+
parts = [p for p in cwd.replace("\\", "/").split("/") if p]
|
|
42
|
+
if not parts:
|
|
43
|
+
return ""
|
|
44
|
+
# Detect home directory: last part is a username-like segment with no parent project
|
|
45
|
+
# If the path ends at /Users/<name> or /home/<name>, show '~'
|
|
46
|
+
if len(parts) >= 2 and parts[-2] in ("Users", "home"):
|
|
47
|
+
return "~"
|
|
48
|
+
return parts[-1]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _validate_window(window: str) -> str:
|
|
52
|
+
if window not in _VALID_WINDOWS:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"Invalid window {window!r}. Must be one of: {', '.join(sorted(_VALID_WINDOWS))}"
|
|
55
|
+
)
|
|
56
|
+
return window
|
|
57
|
+
|
|
58
|
+
|
|
29
59
|
@mcp.tool()
|
|
30
|
-
def list_sessions(
|
|
60
|
+
def list_sessions(
|
|
61
|
+
tool: str | None = None,
|
|
62
|
+
limit: int = 50,
|
|
63
|
+
offset: int = 0,
|
|
64
|
+
window: str | None = None,
|
|
65
|
+
) -> list[dict[str, Any]]:
|
|
31
66
|
"""List indexed sessions with basic token summaries.
|
|
32
67
|
|
|
68
|
+
Supports pagination via offset/limit and optional date filtering via window.
|
|
69
|
+
|
|
33
70
|
Args:
|
|
34
71
|
tool: Filter by tool name (claude_code, codex, gemini, etc.). Omit for all tools.
|
|
35
|
-
limit:
|
|
72
|
+
limit: Page size (default 50, max 200).
|
|
73
|
+
offset: Number of sessions to skip for pagination (default 0).
|
|
74
|
+
window: Optional time window to filter by recency — one of "7d", "30d", "6m", "1y".
|
|
36
75
|
"""
|
|
76
|
+
since_ms: int | None = None
|
|
77
|
+
if window is not None:
|
|
78
|
+
_validate_window(window)
|
|
79
|
+
since_ms = analytics_module._window_start_ms(window)
|
|
80
|
+
|
|
37
81
|
db = _db()
|
|
38
|
-
rows = db_module.get_sessions(
|
|
82
|
+
rows = db_module.get_sessions(
|
|
83
|
+
db, tool=tool, limit=min(limit, 200), offset=offset, since_ms=since_ms
|
|
84
|
+
)
|
|
39
85
|
return [
|
|
40
86
|
{
|
|
41
87
|
"id": r["id"],
|
|
42
88
|
"tool": r["tool"],
|
|
43
|
-
"title": r.get("title") or "",
|
|
44
|
-
"cwd": r.get("cwd") or "",
|
|
89
|
+
"title": (r.get("title") or "")[:100],
|
|
90
|
+
"cwd": _short_cwd(r.get("cwd") or ""),
|
|
45
91
|
"token_count": r.get("token_count") or 0,
|
|
46
92
|
"updated_at": r.get("updated_at"),
|
|
47
93
|
"status": r.get("status") or "",
|
|
48
|
-
"summary": r.get("summary") or "",
|
|
49
94
|
}
|
|
50
95
|
for r in rows
|
|
51
96
|
]
|
|
@@ -53,41 +98,60 @@ def list_sessions(tool: str | None = None, limit: int = 50) -> list[dict[str, An
|
|
|
53
98
|
|
|
54
99
|
@mcp.tool()
|
|
55
100
|
def get_session(session_id: str) -> dict[str, Any] | None:
|
|
56
|
-
"""Get
|
|
101
|
+
"""Get details for a single session by ID.
|
|
57
102
|
|
|
58
103
|
Args:
|
|
59
104
|
session_id: The exact session ID from list_sessions.
|
|
60
105
|
"""
|
|
61
106
|
db = _db()
|
|
62
107
|
row = db_module.get_session(db, session_id)
|
|
63
|
-
|
|
108
|
+
if row is None:
|
|
109
|
+
return None
|
|
110
|
+
return {
|
|
111
|
+
"id": row["id"],
|
|
112
|
+
"tool": row["tool"],
|
|
113
|
+
"title": (row.get("title") or "")[:100],
|
|
114
|
+
"cwd": row.get("cwd") or "",
|
|
115
|
+
"project": _short_cwd(row.get("cwd") or ""),
|
|
116
|
+
"token_count": row.get("token_count") or 0,
|
|
117
|
+
"status": row.get("status") or "",
|
|
118
|
+
"summary": (row.get("summary") or "")[:300],
|
|
119
|
+
"created_at": row.get("created_at"),
|
|
120
|
+
"updated_at": row.get("updated_at"),
|
|
121
|
+
"tags": row.get("tags") or "[]",
|
|
122
|
+
}
|
|
64
123
|
|
|
65
124
|
|
|
66
125
|
@mcp.tool()
|
|
67
|
-
def get_session_tokens(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
126
|
+
def get_session_tokens(
|
|
127
|
+
session_id: str,
|
|
128
|
+
include_turns: bool = False,
|
|
129
|
+
turns_limit: int = 100,
|
|
130
|
+
turns_offset: int = 0,
|
|
131
|
+
top: int = 0,
|
|
132
|
+
) -> dict[str, Any] | None:
|
|
133
|
+
"""Get token breakdown for a session.
|
|
134
|
+
|
|
135
|
+
By default returns only summary statistics (cheap). Set include_turns=True
|
|
136
|
+
to also page through per-turn data using turns_limit and turns_offset.
|
|
72
137
|
|
|
73
138
|
Args:
|
|
74
139
|
session_id: The session ID.
|
|
75
|
-
|
|
140
|
+
include_turns: If True, include per-turn token counts. Default False.
|
|
141
|
+
turns_limit: Max turns to return per page when include_turns=True (default 100).
|
|
142
|
+
turns_offset: Turn index to start from for pagination (default 0).
|
|
143
|
+
top: If > 0, return the N heaviest turns instead of sequential pagination.
|
|
144
|
+
Overrides turns_offset when set.
|
|
76
145
|
"""
|
|
77
146
|
db = _db()
|
|
78
147
|
report = analyze_tokens(db, session_id)
|
|
79
148
|
if report is None:
|
|
80
149
|
return None
|
|
81
150
|
|
|
82
|
-
|
|
83
|
-
if top > 0:
|
|
84
|
-
turns = sorted(turns, key=lambda t: t.tokens, reverse=True)[:top]
|
|
85
|
-
turns = sorted(turns, key=lambda t: t.turn)
|
|
86
|
-
|
|
87
|
-
return {
|
|
151
|
+
result: dict[str, Any] = {
|
|
88
152
|
"session_id": report.session_id,
|
|
89
153
|
"tool": report.tool,
|
|
90
|
-
"title": report.title,
|
|
154
|
+
"title": report.title[:100],
|
|
91
155
|
"total_tokens": report.total,
|
|
92
156
|
"user_tokens": report.user_total,
|
|
93
157
|
"assistant_tokens": report.assistant_total,
|
|
@@ -102,12 +166,20 @@ def get_session_tokens(session_id: str, top: int = 0) -> dict[str, Any] | None:
|
|
|
102
166
|
"turn": report.max_turn.turn,
|
|
103
167
|
"role": report.max_turn.role,
|
|
104
168
|
"tokens": report.max_turn.tokens,
|
|
105
|
-
"preview": report.max_turn.content_preview,
|
|
106
169
|
}
|
|
107
170
|
if report.max_turn
|
|
108
171
|
else None
|
|
109
172
|
),
|
|
110
|
-
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if include_turns:
|
|
176
|
+
turns = report.turns
|
|
177
|
+
if top > 0:
|
|
178
|
+
turns = sorted(turns, key=lambda t: t.tokens, reverse=True)[:top]
|
|
179
|
+
turns = sorted(turns, key=lambda t: t.turn)
|
|
180
|
+
else:
|
|
181
|
+
turns = turns[turns_offset: turns_offset + turns_limit]
|
|
182
|
+
result["turns"] = [
|
|
111
183
|
{
|
|
112
184
|
"turn": t.turn,
|
|
113
185
|
"role": t.role,
|
|
@@ -115,12 +187,25 @@ def get_session_tokens(session_id: str, top: int = 0) -> dict[str, Any] | None:
|
|
|
115
187
|
"text_tokens": t.text_tokens,
|
|
116
188
|
"tool_call_tokens": t.tool_call_tokens,
|
|
117
189
|
"tool_result_tokens": t.tool_result_tokens,
|
|
118
|
-
"cumulative": t.cumulative,
|
|
119
|
-
"preview": t.content_preview,
|
|
120
190
|
}
|
|
121
191
|
for t in turns
|
|
122
|
-
]
|
|
123
|
-
|
|
192
|
+
]
|
|
193
|
+
if top > 0:
|
|
194
|
+
result["turns_pagination"] = {
|
|
195
|
+
"mode": "top",
|
|
196
|
+
"n": top,
|
|
197
|
+
"total": report.turn_count,
|
|
198
|
+
}
|
|
199
|
+
else:
|
|
200
|
+
result["turns_pagination"] = {
|
|
201
|
+
"mode": "sequential",
|
|
202
|
+
"offset": turns_offset,
|
|
203
|
+
"limit": turns_limit,
|
|
204
|
+
"total": report.turn_count,
|
|
205
|
+
"has_more": (turns_offset + turns_limit) < report.turn_count,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return result
|
|
124
209
|
|
|
125
210
|
|
|
126
211
|
@mcp.tool()
|
|
@@ -130,6 +215,7 @@ def get_token_analytics(window: str = "30d") -> dict[str, Any]:
|
|
|
130
215
|
Args:
|
|
131
216
|
window: Time window — one of "7d", "30d", "6m", "1y" (default "30d").
|
|
132
217
|
"""
|
|
218
|
+
_validate_window(window)
|
|
133
219
|
db = _db()
|
|
134
220
|
overview = analytics_module.get_overview(db, window=window)
|
|
135
221
|
return {
|
|
@@ -155,6 +241,7 @@ def get_activity_timeline(window: str = "30d") -> list[dict[str, Any]]:
|
|
|
155
241
|
Args:
|
|
156
242
|
window: Time window — one of "7d", "30d", "6m", "1y" (default "30d").
|
|
157
243
|
"""
|
|
244
|
+
_validate_window(window)
|
|
158
245
|
db = _db()
|
|
159
246
|
buckets = analytics_module.get_activity_over_time(db, window=window)
|
|
160
247
|
return [
|
|
@@ -171,6 +258,7 @@ def get_top_projects(window: str = "30d", top_n: int = 5) -> list[dict[str, Any]
|
|
|
171
258
|
window: Time window — one of "7d", "30d", "6m", "1y" (default "30d").
|
|
172
259
|
top_n: Number of top projects to return (default 5).
|
|
173
260
|
"""
|
|
261
|
+
_validate_window(window)
|
|
174
262
|
db = _db()
|
|
175
263
|
projects = analytics_module.get_top_projects(db, window=window, top_n=top_n)
|
|
176
264
|
return [{"project": p.project, "session_count": p.count} for p in projects]
|
|
@@ -183,7 +271,7 @@ def compare_sessions(session_ids: list[str]) -> list[dict[str, Any]]:
|
|
|
183
271
|
Returns one entry per session with key metrics, sorted by total tokens descending.
|
|
184
272
|
|
|
185
273
|
Args:
|
|
186
|
-
session_ids: List of session IDs to compare
|
|
274
|
+
session_ids: List of session IDs to compare.
|
|
187
275
|
"""
|
|
188
276
|
db = _db()
|
|
189
277
|
results: list[dict[str, Any]] = []
|
|
@@ -195,7 +283,7 @@ def compare_sessions(session_ids: list[str]) -> list[dict[str, Any]]:
|
|
|
195
283
|
results.append({
|
|
196
284
|
"session_id": sid,
|
|
197
285
|
"tool": report.tool,
|
|
198
|
-
"title": report.title,
|
|
286
|
+
"title": report.title[:100],
|
|
199
287
|
"total_tokens": report.total,
|
|
200
288
|
"turn_count": report.turn_count,
|
|
201
289
|
"user_tokens": report.user_total,
|
|
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.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/session_detail.py
RENAMED
|
File without changes
|
{context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/session_table.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/contextforge/tui/widgets/tokens_panel.py
RENAMED
|
File without changes
|
{context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/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
|
|
File without changes
|
{context_forge_cli-0.3.0 → context_forge_cli-0.4.0}/tests/fixtures/claude_session_sample.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|