cortexshift 0.1.0__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.
- cortexshift/__init__.py +10 -0
- cortexshift/__main__.py +6 -0
- cortexshift/adapters/__init__.py +22 -0
- cortexshift/adapters/command_runner.py +116 -0
- cortexshift/adapters/discovery.py +55 -0
- cortexshift/adapters/git/__init__.py +10 -0
- cortexshift/adapters/git/inspector.py +321 -0
- cortexshift/adapters/git/parser.py +140 -0
- cortexshift/adapters/headless_runner.py +92 -0
- cortexshift/adapters/process_runner.py +56 -0
- cortexshift/adapters/providers/__init__.py +4 -0
- cortexshift/adapters/providers/antigravity.py +530 -0
- cortexshift/adapters/providers/claude.py +375 -0
- cortexshift/adapters/providers/codex.py +434 -0
- cortexshift/adapters/sqlite/__init__.py +10 -0
- cortexshift/adapters/sqlite/migrations.py +268 -0
- cortexshift/adapters/sqlite/store.py +914 -0
- cortexshift/adapters/workspace_lease.py +123 -0
- cortexshift/application/__init__.py +42 -0
- cortexshift/application/checkpoint_builder.py +218 -0
- cortexshift/application/checkpoint_service.py +273 -0
- cortexshift/application/doctor.py +80 -0
- cortexshift/application/handoff_builder.py +281 -0
- cortexshift/application/handoff_renderer.py +430 -0
- cortexshift/application/handoff_service.py +66 -0
- cortexshift/application/init_service.py +86 -0
- cortexshift/application/locator.py +48 -0
- cortexshift/application/native_session.py +65 -0
- cortexshift/application/recovery_service.py +235 -0
- cortexshift/application/repository_service.py +146 -0
- cortexshift/application/resume_service.py +124 -0
- cortexshift/application/run_service.py +270 -0
- cortexshift/application/session_launcher.py +183 -0
- cortexshift/application/session_service.py +63 -0
- cortexshift/application/source_session.py +62 -0
- cortexshift/application/status_service.py +73 -0
- cortexshift/application/switch_service.py +671 -0
- cortexshift/application/task_service.py +201 -0
- cortexshift/application/task_workspace.py +152 -0
- cortexshift/cli/__init__.py +5 -0
- cortexshift/cli/app.py +2477 -0
- cortexshift/domain/__init__.py +153 -0
- cortexshift/domain/checkpoint.py +174 -0
- cortexshift/domain/doctor.py +68 -0
- cortexshift/domain/errors.py +277 -0
- cortexshift/domain/git.py +102 -0
- cortexshift/domain/handoff.py +241 -0
- cortexshift/domain/identifiers.py +27 -0
- cortexshift/domain/launch.py +58 -0
- cortexshift/domain/mcp_binding.py +81 -0
- cortexshift/domain/native_session.py +19 -0
- cortexshift/domain/project.py +37 -0
- cortexshift/domain/provider.py +67 -0
- cortexshift/domain/session.py +92 -0
- cortexshift/domain/status.py +40 -0
- cortexshift/domain/task.py +191 -0
- cortexshift/mcp/__init__.py +38 -0
- cortexshift/mcp/context.py +165 -0
- cortexshift/mcp/facade.py +513 -0
- cortexshift/mcp/models.py +178 -0
- cortexshift/mcp/resources.py +45 -0
- cortexshift/mcp/server.py +52 -0
- cortexshift/mcp/tools.py +176 -0
- cortexshift/ports/__init__.py +39 -0
- cortexshift/ports/checkpoint_store.py +45 -0
- cortexshift/ports/command_runner.py +56 -0
- cortexshift/ports/discovery.py +41 -0
- cortexshift/ports/handoff_delivery.py +91 -0
- cortexshift/ports/handoff_store.py +43 -0
- cortexshift/ports/headless_runner.py +58 -0
- cortexshift/ports/native_session.py +20 -0
- cortexshift/ports/process_runner.py +31 -0
- cortexshift/ports/provider.py +152 -0
- cortexshift/ports/repository.py +44 -0
- cortexshift/ports/session_store.py +27 -0
- cortexshift/ports/state_store.py +55 -0
- cortexshift/ports/workspace_lease.py +39 -0
- cortexshift/tui/__init__.py +24 -0
- cortexshift/tui/actions.py +58 -0
- cortexshift/tui/app.py +1051 -0
- cortexshift/tui/coordinator.py +173 -0
- cortexshift/tui/cortexshift.tcss +258 -0
- cortexshift/tui/facade.py +614 -0
- cortexshift/tui/modals.py +594 -0
- cortexshift/tui/models.py +503 -0
- cortexshift/tui/screens/__init__.py +81 -0
- cortexshift/tui/screens/checkpoints.py +188 -0
- cortexshift/tui/screens/handoffs.py +180 -0
- cortexshift/tui/screens/help.py +117 -0
- cortexshift/tui/screens/overview.py +200 -0
- cortexshift/tui/screens/providers.py +169 -0
- cortexshift/tui/screens/repository.py +143 -0
- cortexshift/tui/screens/sessions.py +146 -0
- cortexshift/tui/screens/task.py +174 -0
- cortexshift/tui/widgets.py +209 -0
- cortexshift-0.1.0.dist-info/METADATA +202 -0
- cortexshift-0.1.0.dist-info/RECORD +100 -0
- cortexshift-0.1.0.dist-info/WHEEL +4 -0
- cortexshift-0.1.0.dist-info/entry_points.txt +2 -0
- cortexshift-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""Reusable presentation widgets for the CortexShift control center.
|
|
2
|
+
|
|
3
|
+
Meaning is never carried by colour alone: every status marker pairs a glyph with its
|
|
4
|
+
styling so the dashboard stays readable on limited-colour terminals.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from collections.abc import Sequence
|
|
8
|
+
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
from textual.containers import Vertical
|
|
12
|
+
from textual.widgets import DataTable, Static
|
|
13
|
+
|
|
14
|
+
from cortexshift.tui.models import (
|
|
15
|
+
AUTHORITY_LABELS,
|
|
16
|
+
DataAuthority,
|
|
17
|
+
TuiTaskProgress,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
MARK_OK = "✓"
|
|
21
|
+
MARK_WARN = "!"
|
|
22
|
+
MARK_FAIL = "×"
|
|
23
|
+
MARK_IDLE = "·"
|
|
24
|
+
MARK_UNKNOWN = "?"
|
|
25
|
+
|
|
26
|
+
PROGRESS_FILLED = "█"
|
|
27
|
+
PROGRESS_EMPTY = "░"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def status_marker(state: str) -> tuple[str, str]:
|
|
31
|
+
"""Return a (glyph, css class) pair for a status keyword.
|
|
32
|
+
|
|
33
|
+
The glyph carries the meaning; the class only adds emphasis where colour exists.
|
|
34
|
+
"""
|
|
35
|
+
normalized = state.strip().lower()
|
|
36
|
+
if normalized in ("ok", "ready", "available", "completed", "delivered", "clean", "free"):
|
|
37
|
+
return MARK_OK, "state-ok"
|
|
38
|
+
if normalized in ("warn", "warning", "dirty", "interrupted", "unknown", "pending", "busy"):
|
|
39
|
+
return MARK_WARN, "state-warn"
|
|
40
|
+
if normalized in ("fail", "failed", "error", "unavailable", "cancelled"):
|
|
41
|
+
return MARK_FAIL, "state-fail"
|
|
42
|
+
if normalized in ("running", "initializing", "in_progress", "prepared"):
|
|
43
|
+
return MARK_IDLE, "state-active"
|
|
44
|
+
return MARK_UNKNOWN, "state-muted"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def marked(state: str, label: str) -> Text:
|
|
48
|
+
"""Render a glyph-prefixed status label."""
|
|
49
|
+
glyph, css = status_marker(state)
|
|
50
|
+
text = Text()
|
|
51
|
+
text.append(f"{glyph} ", style=_STYLE_FOR_CLASS.get(css, ""))
|
|
52
|
+
text.append(label)
|
|
53
|
+
return text
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
_STYLE_FOR_CLASS = {
|
|
57
|
+
"state-ok": "bold green",
|
|
58
|
+
"state-warn": "bold yellow",
|
|
59
|
+
"state-fail": "bold red",
|
|
60
|
+
"state-active": "bold cyan",
|
|
61
|
+
"state-muted": "dim",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def authority_label(authority: DataAuthority) -> str:
|
|
66
|
+
"""Human wording for a data authority level."""
|
|
67
|
+
return AUTHORITY_LABELS[authority]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def render_progress_bar(progress: TuiTaskProgress, width: int = 20) -> Text:
|
|
71
|
+
"""Render structured task progress.
|
|
72
|
+
|
|
73
|
+
When no structured items exist, CortexShift states that plainly rather than
|
|
74
|
+
fabricating a completion percentage out of Git state or session exit codes.
|
|
75
|
+
"""
|
|
76
|
+
fraction = progress.fraction
|
|
77
|
+
if fraction is None:
|
|
78
|
+
return Text("No structured progress yet", style="dim italic")
|
|
79
|
+
|
|
80
|
+
filled = int(round(fraction * width))
|
|
81
|
+
bar = Text()
|
|
82
|
+
bar.append(PROGRESS_FILLED * filled, style="bold green")
|
|
83
|
+
bar.append(PROGRESS_EMPTY * (width - filled), style="dim")
|
|
84
|
+
bar.append(f" {progress.completed_count}/{progress.total} · {progress.percent}%")
|
|
85
|
+
return bar
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class Panel(Vertical):
|
|
89
|
+
"""A titled content block used to group related dashboard facts.
|
|
90
|
+
|
|
91
|
+
The heading rides the container's top border, so it always precedes the panel's
|
|
92
|
+
content regardless of how children are composed. The authority label sits on the
|
|
93
|
+
right of the same border, keeping "what this is" and "how much you can trust it"
|
|
94
|
+
adjacent.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
DEFAULT_CLASSES = "panel"
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
title: str,
|
|
102
|
+
*,
|
|
103
|
+
authority: DataAuthority | None = None,
|
|
104
|
+
id: str | None = None,
|
|
105
|
+
classes: str | None = None,
|
|
106
|
+
) -> None:
|
|
107
|
+
super().__init__(id=id, classes=classes)
|
|
108
|
+
self.border_title = title.upper()
|
|
109
|
+
if authority is not None:
|
|
110
|
+
self.border_subtitle = authority_label(authority)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class FieldList(Static):
|
|
114
|
+
"""A copy-friendly label/value list.
|
|
115
|
+
|
|
116
|
+
Values render as plain selectable terminal text so identifiers stay inspectable and
|
|
117
|
+
copyable, and wrap inside their own aligned column rather than running back under
|
|
118
|
+
the labels.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def __init__(self, id: str | None = None, classes: str | None = None) -> None:
|
|
122
|
+
super().__init__("", id=id, classes=f"field-list {classes}" if classes else "field-list")
|
|
123
|
+
|
|
124
|
+
def set_fields(self, fields: Sequence[tuple[str, str | Text]]) -> None:
|
|
125
|
+
"""Replace the rendered fields."""
|
|
126
|
+
if not fields:
|
|
127
|
+
self.update(Text(""))
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
grid = Table.grid(padding=(0, 2))
|
|
131
|
+
grid.add_column(style="bold cyan", justify="left", no_wrap=True)
|
|
132
|
+
grid.add_column(justify="left", overflow="fold")
|
|
133
|
+
for label, value in fields:
|
|
134
|
+
grid.add_row(label, value if isinstance(value, Text) else Text(str(value)))
|
|
135
|
+
self.update(grid)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class BulletList(Static):
|
|
139
|
+
"""A bounded bullet list with an honest overflow notice."""
|
|
140
|
+
|
|
141
|
+
def __init__(self, id: str | None = None, classes: str | None = None) -> None:
|
|
142
|
+
super().__init__("", id=id, classes=f"bullets {classes}" if classes else "bullets")
|
|
143
|
+
|
|
144
|
+
def set_items(
|
|
145
|
+
self,
|
|
146
|
+
items: Sequence[str],
|
|
147
|
+
*,
|
|
148
|
+
limit: int = 12,
|
|
149
|
+
empty: str = "None recorded.",
|
|
150
|
+
marker: str = "•",
|
|
151
|
+
) -> None:
|
|
152
|
+
"""Render up to `limit` items, stating how many were not shown."""
|
|
153
|
+
if not items:
|
|
154
|
+
self.update(Text(empty, style="dim italic"))
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
body = Text()
|
|
158
|
+
for index, item in enumerate(items[:limit]):
|
|
159
|
+
if index:
|
|
160
|
+
body.append("\n")
|
|
161
|
+
body.append(f" {marker} ", style="dim")
|
|
162
|
+
body.append(" ".join(item.split()))
|
|
163
|
+
|
|
164
|
+
omitted = len(items) - limit
|
|
165
|
+
if omitted > 0:
|
|
166
|
+
body.append(
|
|
167
|
+
f"\n … {omitted} more item(s) not shown here; canonical state is complete.",
|
|
168
|
+
style="dim italic",
|
|
169
|
+
)
|
|
170
|
+
self.update(body)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class EmptyState(Static):
|
|
174
|
+
"""A useful empty state that tells the operator what to do next."""
|
|
175
|
+
|
|
176
|
+
DEFAULT_CLASSES = "empty-state"
|
|
177
|
+
|
|
178
|
+
def __init__(self, message: str, hint: str | None = None, id: str | None = None) -> None:
|
|
179
|
+
body = Text(message, style="bold")
|
|
180
|
+
if hint:
|
|
181
|
+
body.append("\n\n")
|
|
182
|
+
body.append(hint, style="dim")
|
|
183
|
+
super().__init__(body, id=id)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def restore_cursor(table: DataTable[Text], key: str | None) -> None:
|
|
187
|
+
"""Move a table's cursor back to a row key when that row still exists.
|
|
188
|
+
|
|
189
|
+
Refreshing data must not throw the operator's selection around, so selection is
|
|
190
|
+
preserved whenever the underlying row survives the refresh.
|
|
191
|
+
"""
|
|
192
|
+
if key is None:
|
|
193
|
+
return
|
|
194
|
+
try:
|
|
195
|
+
row_index = table.get_row_index(key)
|
|
196
|
+
except Exception:
|
|
197
|
+
return
|
|
198
|
+
table.move_cursor(row=row_index, animate=False)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def current_row_key(table: DataTable[Text]) -> str | None:
|
|
202
|
+
"""Return the row key under the cursor, or None when the table is empty."""
|
|
203
|
+
if table.row_count == 0:
|
|
204
|
+
return None
|
|
205
|
+
try:
|
|
206
|
+
row_key, _ = table.coordinate_to_cell_key(table.cursor_coordinate)
|
|
207
|
+
except Exception:
|
|
208
|
+
return None
|
|
209
|
+
return None if row_key.value is None else str(row_key.value)
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cortexshift
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Provider-neutral coding-agent handoffs with persistent project context.
|
|
5
|
+
Project-URL: Homepage, https://github.com/batuhanasmakaya/CortexShift
|
|
6
|
+
Project-URL: Repository, https://github.com/batuhanasmakaya/CortexShift
|
|
7
|
+
Project-URL: Issues, https://github.com/batuhanasmakaya/CortexShift/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/batuhanasmakaya/CortexShift/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Security, https://github.com/batuhanasmakaya/CortexShift/security
|
|
10
|
+
Author: CortexShift contributors
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: ai,antigravity,claude-code,codex,coding-agents,context-handoff,developer-tools
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
23
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Requires-Dist: mcp<3,>=2
|
|
26
|
+
Requires-Dist: pydantic>=2.6.0
|
|
27
|
+
Requires-Dist: rich>=13.7.0
|
|
28
|
+
Requires-Dist: textual<9,>=8
|
|
29
|
+
Requires-Dist: typer>=0.12.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: mypy>=1.10.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# CortexShift
|
|
39
|
+
|
|
40
|
+
**Switch agents. Keep the context.**
|
|
41
|
+
|
|
42
|
+
CortexShift keeps development tasks intact as you move between Claude Code,
|
|
43
|
+
Codex, and Antigravity in the same local repository. A Task belongs to CortexShift;
|
|
44
|
+
providers are workers over that Task. Switching does not require the outgoing
|
|
45
|
+
agent to answer, summarize, or even remain installed.
|
|
46
|
+
|
|
47
|
+
**0.1.0 is an initial public alpha release candidate. PyPI publication is pending.**
|
|
48
|
+
The canonical repository is <https://github.com/batuhanasmakaya/CortexShift>.
|
|
49
|
+
Python 3.12–3.14 is targeted, with required Linux/macOS/Windows CI on protected
|
|
50
|
+
`main`. Real end-to-end validation is complete for Claude Code and Codex;
|
|
51
|
+
Antigravity has automated coverage only, and interactive platform smokes remain
|
|
52
|
+
on a separate maintainer checklist.
|
|
53
|
+
|
|
54
|
+
## How it works
|
|
55
|
+
|
|
56
|
+
CortexShift stores objectives, requirements, progress, checkpoints, and session
|
|
57
|
+
metadata in project-local SQLite. It combines that structured state with live
|
|
58
|
+
Git inspection to prepare a canonical handoff. A conversation transcript is not
|
|
59
|
+
canonical project state: receiving agents must verify the files and run tests.
|
|
60
|
+
|
|
61
|
+
Authority flows from live repository files → live Git → verified evidence →
|
|
62
|
+
canonical Task state → historical observations. Recorded completion is a report,
|
|
63
|
+
not proof; checkpoints never establish that tests passed independently.
|
|
64
|
+
|
|
65
|
+
## Capabilities
|
|
66
|
+
|
|
67
|
+
- Persistent tasks, provider session history, and manual agent switching.
|
|
68
|
+
- Exact native resume when an ID is known; fresh canonical context on return.
|
|
69
|
+
- Cooperative checkpoints and deterministic crash recovery.
|
|
70
|
+
- MCP shared state over local stdio: agents can read context and report progress.
|
|
71
|
+
- A keyboard-driven terminal control center for tasks, repository state, and history.
|
|
72
|
+
- Read-only Git inspection and an OS workspace lock for exclusive provider runs.
|
|
73
|
+
|
|
74
|
+
## Quick start
|
|
75
|
+
|
|
76
|
+
After PyPI publication:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pipx install cortexshift
|
|
80
|
+
cd my-project
|
|
81
|
+
cortexshift doctor
|
|
82
|
+
cortexshift init
|
|
83
|
+
cortexshift task start --title "Implement authentication" \
|
|
84
|
+
--objective "Add authentication without breaking existing APIs."
|
|
85
|
+
cortexshift run claude
|
|
86
|
+
# After the provider exits:
|
|
87
|
+
cortexshift switch codex
|
|
88
|
+
cortexshift tui
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
CortexShift does not bundle Git or any provider CLI. Install and authenticate
|
|
92
|
+
native providers yourself. Git is recommended for repository-aware handoffs,
|
|
93
|
+
but project initialization also works without Git.
|
|
94
|
+
|
|
95
|
+
## Installation and updates
|
|
96
|
+
|
|
97
|
+
[pipx](https://pipx.pypa.io/stable/) gives a global command with isolated Python
|
|
98
|
+
dependencies. Use Python 3.12 or newer. Once published:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pipx upgrade cortexshift
|
|
102
|
+
pipx uninstall cortexshift
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Before publication, clone the repository and use `uv sync --locked`, then
|
|
106
|
+
`uv run cortexshift --help`:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
git clone https://github.com/batuhanasmakaya/CortexShift.git
|
|
110
|
+
cd CortexShift
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
To install a candidate globally without an editable checkout: `uv build`, then
|
|
114
|
+
`pipx install dist/cortexshift-0.1.0-py3-none-any.whl`.
|
|
115
|
+
A virtualenv/pip alternative is in [Getting Started][getting-started].
|
|
116
|
+
Homebrew is pending publication of a custom tap.
|
|
117
|
+
|
|
118
|
+
## Your first handoff
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
cortexshift checkpoint create -d "Preserve existing API compatibility" \
|
|
122
|
+
-t "Targeted tests passed (operator report)"
|
|
123
|
+
cortexshift handoff preview codex
|
|
124
|
+
cortexshift switch codex --dry-run
|
|
125
|
+
cortexshift switch codex
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Preview and dry-run launch no model and persist nothing. Actual Codex and
|
|
129
|
+
Antigravity handoffs each use one read-only bootstrap model turn before native
|
|
130
|
+
resume, which may consume provider usage. Claude receives context directly.
|
|
131
|
+
|
|
132
|
+
## Terminal control center
|
|
133
|
+
|
|
134
|
+
Run `cortexshift tui` in an initialized project and a real terminal. Use `1`–`7`
|
|
135
|
+
to navigate, `r` to refresh, `c` to checkpoint, `x` for provider actions, and `?`
|
|
136
|
+
for help. The dashboard releases terminal ownership before launching a native
|
|
137
|
+
provider. After that provider exits, run `cortexshift tui` again.
|
|
138
|
+
An open dashboard holds no workspace lease and may observe a running agent.
|
|
139
|
+
|
|
140
|
+
## MCP and providers
|
|
141
|
+
|
|
142
|
+
Claude and Codex receive MCP configuration automatically. Antigravity requires
|
|
143
|
+
explicit workspace setup: `cortexshift mcp setup antigravity`.
|
|
144
|
+
Inspect integration with `cortexshift mcp status`. Managed sessions expose
|
|
145
|
+
10 context-bound tools; unmanaged/read-only sessions expose four read tools.
|
|
146
|
+
See [Provider Support][provider-support] for identity, resume, and
|
|
147
|
+
validation limits.
|
|
148
|
+
|
|
149
|
+
## Privacy and trust
|
|
150
|
+
|
|
151
|
+
CortexShift has no cloud account, telemetry, or paid model API key requirement.
|
|
152
|
+
It does not copy provider credentials or persist prompts, provider responses,
|
|
153
|
+
transcripts, or full Git patches. Native provider permissions remain authoritative.
|
|
154
|
+
Providers may send repository/context data to their own services under their
|
|
155
|
+
configuration and terms; local orchestration does not change that behavior.
|
|
156
|
+
|
|
157
|
+
Task text, paths, and project history are local development data. Generally ignore
|
|
158
|
+
`.cortexshift/` in Git; CortexShift does not rewrite your `.gitignore`.
|
|
159
|
+
See [Security][security-policy] for boundaries and safe reporting.
|
|
160
|
+
|
|
161
|
+
## Limitations
|
|
162
|
+
|
|
163
|
+
External provider CLIs can change. Legacy or plain Codex/Antigravity runs may
|
|
164
|
+
have no native ID; those sessions cannot be exact-resumed. Deleted native
|
|
165
|
+
conversations are not silently replaced. There is no automatic quota switching,
|
|
166
|
+
embedded provider TUI, GUI, cloud agent, secret manager, or terminal multiplexer.
|
|
167
|
+
Provider accounts, subscriptions, and usage costs are governed by each provider.
|
|
168
|
+
Internal Python modules are not a stable library API.
|
|
169
|
+
|
|
170
|
+
## Documentation and development
|
|
171
|
+
|
|
172
|
+
- [Getting Started][getting-started] and [Troubleshooting][troubleshooting]
|
|
173
|
+
- [Provider Support][provider-support]
|
|
174
|
+
- [Architecture][architecture] and [Handoff Protocol][handoff-protocol]
|
|
175
|
+
- [Contributing][contributing], [Releasing][releasing], and [Roadmap][roadmap]
|
|
176
|
+
- [Changelog][changelog] and [Issues][issues]
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
uv sync --locked
|
|
180
|
+
uv run ruff check .
|
|
181
|
+
uv run ruff format --check .
|
|
182
|
+
uv run mypy
|
|
183
|
+
uv run pytest
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
[MIT][license]. CortexShift is an independent open-source project and is not
|
|
189
|
+
affiliated with or endorsed by Anthropic, OpenAI, or Google.
|
|
190
|
+
|
|
191
|
+
[getting-started]: https://github.com/batuhanasmakaya/CortexShift/blob/main/docs/getting-started.md
|
|
192
|
+
[provider-support]: https://github.com/batuhanasmakaya/CortexShift/blob/main/docs/provider-support.md
|
|
193
|
+
[architecture]: https://github.com/batuhanasmakaya/CortexShift/blob/main/docs/architecture.md
|
|
194
|
+
[handoff-protocol]: https://github.com/batuhanasmakaya/CortexShift/blob/main/docs/handoff-protocol.md
|
|
195
|
+
[troubleshooting]: https://github.com/batuhanasmakaya/CortexShift/blob/main/docs/troubleshooting.md
|
|
196
|
+
[contributing]: https://github.com/batuhanasmakaya/CortexShift/blob/main/CONTRIBUTING.md
|
|
197
|
+
[releasing]: https://github.com/batuhanasmakaya/CortexShift/blob/main/docs/releasing.md
|
|
198
|
+
[roadmap]: https://github.com/batuhanasmakaya/CortexShift/blob/main/docs/roadmap.md
|
|
199
|
+
[changelog]: https://github.com/batuhanasmakaya/CortexShift/blob/main/CHANGELOG.md
|
|
200
|
+
[security-policy]: https://github.com/batuhanasmakaya/CortexShift/blob/main/SECURITY.md
|
|
201
|
+
[license]: https://github.com/batuhanasmakaya/CortexShift/blob/main/LICENSE
|
|
202
|
+
[issues]: https://github.com/batuhanasmakaya/CortexShift/issues
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
cortexshift/__init__.py,sha256=S6WctUzK9vhfVwwgsPTyHQjLZ4e-2ZdDrFUFpuEp5ng,269
|
|
2
|
+
cortexshift/__main__.py,sha256=CKwGwVD-Z9WbS_ekNCYyhZEj7ajtYuoD45jcn2bi20Q,121
|
|
3
|
+
cortexshift/adapters/__init__.py,sha256=FuMQ86Uzqw87XEj5f-1Z6Av-uqYnT0ystrgvaPDuDi4,977
|
|
4
|
+
cortexshift/adapters/command_runner.py,sha256=V3H1l2EPZ_HFRn_wTiY2QGwKc6ax69QKo36sckN5dkU,3884
|
|
5
|
+
cortexshift/adapters/discovery.py,sha256=e_GaCyJ9Uxbmg7A7wI0Eg98fRApma4Zs3Fo_HR08gGM,2249
|
|
6
|
+
cortexshift/adapters/headless_runner.py,sha256=B3EK3TbQb9T2La78RgkaK2wm6e66eb7Nr0FxlEXP_nw,3018
|
|
7
|
+
cortexshift/adapters/process_runner.py,sha256=dbaeWsT9ZT2dwP9Cu42cc7oFLhSQIr3Qr00ge1s_KEU,1746
|
|
8
|
+
cortexshift/adapters/workspace_lease.py,sha256=TNDeuYKJkkw1aYBJd7__XuCKxpgL-AoFQ6HRi5ebIrk,3787
|
|
9
|
+
cortexshift/adapters/git/__init__.py,sha256=ahVrqQ_gO3R_GMZRBUitSnha6VlVWGPfGZXuVpoMqYM,306
|
|
10
|
+
cortexshift/adapters/git/inspector.py,sha256=6EG5nfYhidma5fawgrG92ie9AlRuAfI2gJ38TFG3mr4,12169
|
|
11
|
+
cortexshift/adapters/git/parser.py,sha256=ea0Mc86H6Dv6gSujPr_NyyM7jW4AZPHxD-5sh1ZkCb4,4538
|
|
12
|
+
cortexshift/adapters/providers/__init__.py,sha256=TYmvQ_Ofa4uztlLouq6I_nGFqJChVA0GCYNQ06Bu8QQ,156
|
|
13
|
+
cortexshift/adapters/providers/antigravity.py,sha256=QTiULR3E9DK0tneyWRUqwGQjStecOkaiD9OlywpORvY,19055
|
|
14
|
+
cortexshift/adapters/providers/claude.py,sha256=gsHeY4Iclhf6BgiMaQ0vPjTRTLgH5ZEeSK4yPBmY9jU,13343
|
|
15
|
+
cortexshift/adapters/providers/codex.py,sha256=ROLYa_FkpdrySDNR0Kr2qNuOKSOJXIrbMTsksgK7ZoY,16173
|
|
16
|
+
cortexshift/adapters/sqlite/__init__.py,sha256=aOddAauCgcmUoLgL7PACKYRBppqzHvbITI3EZf0vRs4,287
|
|
17
|
+
cortexshift/adapters/sqlite/migrations.py,sha256=93HO1uKDOUh2j-dS4txzHLi4FiD17WIkFQj4zqdB6ik,9830
|
|
18
|
+
cortexshift/adapters/sqlite/store.py,sha256=MQ4jvb1Ux_MW_ZvVgfMSLRWElWtp3xYTAAJbkZ05lSU,40876
|
|
19
|
+
cortexshift/application/__init__.py,sha256=g-NTGeT8KSIwnjyK_QmcQcm6HJGg3cQm5Rwwvt2aUN0,1401
|
|
20
|
+
cortexshift/application/checkpoint_builder.py,sha256=yPE9SChXsy2R3goeJhpnKVnlJJE4b6nSwiSQcEmZeM0,8453
|
|
21
|
+
cortexshift/application/checkpoint_service.py,sha256=4yq-DVxglYS86GjcimA_tBJj18Wu5PtzJ7QXv9G1EPA,10422
|
|
22
|
+
cortexshift/application/doctor.py,sha256=MZPvRwxCLRHECkiLLUqdcOheeshws_9svPqTK_ITork,2957
|
|
23
|
+
cortexshift/application/handoff_builder.py,sha256=zlcgwcaNAeLYDL02FUjuQUAeHTPt3OxNfheeX8FbwCM,11933
|
|
24
|
+
cortexshift/application/handoff_renderer.py,sha256=wBpSpwO0GtQo5OPX5D67_MLZZdHpTjhRM1I-RPdLZZY,17137
|
|
25
|
+
cortexshift/application/handoff_service.py,sha256=Lv_sf2goZdePoN2t0jqmbz_p2S4S3vnikiGh875fE0A,2426
|
|
26
|
+
cortexshift/application/init_service.py,sha256=Hnuz749JinPC7CTFVWz9VRf0jZ-rMQ9JishgmyKfY-E,3186
|
|
27
|
+
cortexshift/application/locator.py,sha256=LIH4uXPPK9WGRbX70HoM__OHWt38CHCrvh5qzIDve7E,1826
|
|
28
|
+
cortexshift/application/native_session.py,sha256=s6Wwx3sn7uMEi_Vtz0DozfthEju4DHGuUL35dcM2vKA,2621
|
|
29
|
+
cortexshift/application/recovery_service.py,sha256=JW1rw18gN3J2fYuExhoEwEnrlMrqcVL2DJUrq0JTXNA,9509
|
|
30
|
+
cortexshift/application/repository_service.py,sha256=t9WnUSxtY9CkgFsYCpMIcttoYVPxhcWPNOMlrHUuyYc,5953
|
|
31
|
+
cortexshift/application/resume_service.py,sha256=IyjX2f3fMn3s7VvlZyzhUXUtLhLlETRDuQF_Dze56I0,5155
|
|
32
|
+
cortexshift/application/run_service.py,sha256=OAO4nYwUpuqtjgW0P1Y6_0zRT7PPFqLc45lf87V3NEw,10319
|
|
33
|
+
cortexshift/application/session_launcher.py,sha256=Ze51bFQ2Kar7ke5ljKQdr7DClJf-D_Rt8OMZ663HQzo,7257
|
|
34
|
+
cortexshift/application/session_service.py,sha256=cBDbYyQhQG8gkKEU_urLpGrz7vp1EJ7gOl0OyNFcWeM,2292
|
|
35
|
+
cortexshift/application/source_session.py,sha256=aTMPGjD-7KIExoPyyCIyxJNvXAPT_VHeelVxCXEH96g,2254
|
|
36
|
+
cortexshift/application/status_service.py,sha256=alOaZ1hy85cTKztiP0sk8bXIhG2uWZxFwOg6yW2i_Ts,3026
|
|
37
|
+
cortexshift/application/switch_service.py,sha256=HRq78cvFu2ZcqR4j_ubem64pAz6NGLXF2avEjVrvp2A,27679
|
|
38
|
+
cortexshift/application/task_service.py,sha256=iqGo5oiNJE8PoBZ8ju3ZP6McVdh0l-BsaXOcYgIyiYg,7350
|
|
39
|
+
cortexshift/application/task_workspace.py,sha256=i0v0v6LZUUZ9HD1PdZ2Trl4QtNGBvRDMhLRqTpuv7ZQ,6184
|
|
40
|
+
cortexshift/cli/__init__.py,sha256=uwV-wjJKMsqESiE6bPMgEC1-j22k-_sWTRppDbpUPtw,91
|
|
41
|
+
cortexshift/cli/app.py,sha256=IbsYhMQFYqnlj2-CuIc45YANVrAw1psZvVNvvrSGxo4,83977
|
|
42
|
+
cortexshift/domain/__init__.py,sha256=ksO2MWMQFVlKHIjBEOO2C_zDTkrA2Fq7WPUs06akkl8,4150
|
|
43
|
+
cortexshift/domain/checkpoint.py,sha256=EFTu_mbiNxjjevW1GDyISk2k6xyYRxTqqA8ThMWAb6Q,5979
|
|
44
|
+
cortexshift/domain/doctor.py,sha256=usyYg8zFiE-QAMxCtlBaodlL6KgmiDg5tIl784-K6ac,1863
|
|
45
|
+
cortexshift/domain/errors.py,sha256=Lqb3V_2FJSAsmf6XEJEMa5WavA8P803_QojSZxlO8ZM,9831
|
|
46
|
+
cortexshift/domain/git.py,sha256=DqF5iO9_73parQz7FL2y6LJ5Zndt4nODjRzVMxeQQHA,3541
|
|
47
|
+
cortexshift/domain/handoff.py,sha256=vuX5gA0hTlrczhKs1VQlxmMyM59E_RCUa0fSqlQeYPg,8470
|
|
48
|
+
cortexshift/domain/identifiers.py,sha256=24JhklY3uhTloh49y_WNYkwB4cghBipRJ7w6ziglUu8,756
|
|
49
|
+
cortexshift/domain/launch.py,sha256=7fcNomIMi8J11W7LOdFj4aFSfvrLYD4K0OKWhj0a4EQ,1969
|
|
50
|
+
cortexshift/domain/mcp_binding.py,sha256=KQ2NOBk32sPjb3gnsiLlQx1ztcUETAqP-UeokesM4gA,2746
|
|
51
|
+
cortexshift/domain/native_session.py,sha256=R-qcm9NbpmOBTAM02TUZPimUIj_5mUBtWUnxlEqA244,716
|
|
52
|
+
cortexshift/domain/project.py,sha256=nTV9o0Y3pmVlCg6Lptg-z3SrX5S6-vzIPO195Yvk5eg,1101
|
|
53
|
+
cortexshift/domain/provider.py,sha256=DG2OWZt0FvHz4ZU9_FvwQ2l0bfEMZH4acvfPjGkCEnQ,2199
|
|
54
|
+
cortexshift/domain/session.py,sha256=nhAusoguTGer6tMdzgQiFCj99fk2FYly916n_5tVzuw,3355
|
|
55
|
+
cortexshift/domain/status.py,sha256=ZGZXN1Ij48fVunOYABiptGzM-0VUULUD4TCr4FoF_ks,887
|
|
56
|
+
cortexshift/domain/task.py,sha256=MnLheyrrNLyBuR92uRQkqVlO5E74dxfyQjdgWZ_lH74,6899
|
|
57
|
+
cortexshift/mcp/__init__.py,sha256=HtTz2hURL_O5pWLr6OYGDE6pzd9looL5yiNESbg_M1U,938
|
|
58
|
+
cortexshift/mcp/context.py,sha256=E5LlgQkCmcBRCDpbKyaS_AjbiMHSPJU5cFTDFrrs4rQ,5707
|
|
59
|
+
cortexshift/mcp/facade.py,sha256=pEhN84WZYwUtzeRz388pmmk6ME0-pTbIOQj72EXQQXk,19538
|
|
60
|
+
cortexshift/mcp/models.py,sha256=kVu59PmkUYeWylPLjLhb0IkfyoeUXJRqYJstmwVtTJw,4437
|
|
61
|
+
cortexshift/mcp/resources.py,sha256=OZVEAnt2QhMBE-UFqRTCSxK6xz9sVCV0D9nLAE1ZHVk,2101
|
|
62
|
+
cortexshift/mcp/server.py,sha256=VSZSxnae0JiajTxXNT2xNhD_ODvqTKXm0gQ3whzd8y8,1747
|
|
63
|
+
cortexshift/mcp/tools.py,sha256=-SO9V9iPc6LFjvYVx6qUlbnuvwYqUXIOhLHqVt0hayU,6572
|
|
64
|
+
cortexshift/ports/__init__.py,sha256=lAWsaoTn6xYGjq87eXdeh9u8aHWEya49fx4r94fOn2E,1454
|
|
65
|
+
cortexshift/ports/checkpoint_store.py,sha256=H36uj_M7eoetnfhr4b7B3pJVavQXVuOpiaI4FtytjiQ,1470
|
|
66
|
+
cortexshift/ports/command_runner.py,sha256=IrSUKmUwqdcJukDI7UhZ1D2zppZ7qq26mA-BcFfU-RE,1717
|
|
67
|
+
cortexshift/ports/discovery.py,sha256=CDUKkLy-jO77tQlz75yP_l-1-7O5WUtwqWgp0VFAaHM,1255
|
|
68
|
+
cortexshift/ports/handoff_delivery.py,sha256=WFJUr6PuDQjIRNbB0Y5y67WnBD3RjRhEh-znmK4teRc,3160
|
|
69
|
+
cortexshift/ports/handoff_store.py,sha256=anyU3WXdOxkdAt6Upty1RhSG8gzX-bJpxESzdNOeePI,1446
|
|
70
|
+
cortexshift/ports/headless_runner.py,sha256=tgSu7H5HB8eoX6hzPSKF9EkiXthpRslpzU2nXUCqTqM,2006
|
|
71
|
+
cortexshift/ports/native_session.py,sha256=H4GMp4UVrL54-6vL5Ht-JCiu1I3eFLSgfZpuBP_wBA0,732
|
|
72
|
+
cortexshift/ports/process_runner.py,sha256=EX86n2fmXlE9fpkmeuEkelmrKZlEFX1uDjGmiYRjDX8,946
|
|
73
|
+
cortexshift/ports/provider.py,sha256=Ix4w51HU92H8vLUCrFD7fZYUmxw3vda2pL9wsLlghwc,4748
|
|
74
|
+
cortexshift/ports/repository.py,sha256=oUW7K9LdkioOFSdq-aDT95co4Q-ZACdYRlausDtIBs0,1482
|
|
75
|
+
cortexshift/ports/session_store.py,sha256=xzVbL9CW4deQha2cX9cgF8lBOlSyZYOkTXRXw6LyqFE,795
|
|
76
|
+
cortexshift/ports/state_store.py,sha256=NlGjviCcZt8htHyHxleQIGxKeK_cWaVEXkEstUgItC8,1760
|
|
77
|
+
cortexshift/ports/workspace_lease.py,sha256=PBWNTJxBdVqEkjAMh53nKvV_0odxUclXhllYAmR5fSY,1132
|
|
78
|
+
cortexshift/tui/__init__.py,sha256=c6jBo-ni7vqI44UtRsvrqqdAF369lI0U1hGo1_VoC8E,796
|
|
79
|
+
cortexshift/tui/actions.py,sha256=_e0GAhPaMQLl3VrVFVNXW4obUg2YxAgbGnw6rzookOc,2261
|
|
80
|
+
cortexshift/tui/app.py,sha256=oKgJ44pqQHjl5fYT30nOYgZgKE_njxLKEEyj04Ou_Dk,42210
|
|
81
|
+
cortexshift/tui/coordinator.py,sha256=oM4dhB0a7BrLEDBsZl8pUNoK0YF9mHo9FUomgbW51SA,6710
|
|
82
|
+
cortexshift/tui/cortexshift.tcss,sha256=Q24SzsMYggtUyHHHvrj2jMLEV90FB9HEaOvLh6PKMnI,3858
|
|
83
|
+
cortexshift/tui/facade.py,sha256=4pOyYpbQZq05OohvgQlGpmHno7PSJICT7632H_d6Q9o,26198
|
|
84
|
+
cortexshift/tui/modals.py,sha256=-sxJAZC7M1ESp4R1XwxrL2nSOeNH2A50vjCvqisP4vg,22183
|
|
85
|
+
cortexshift/tui/models.py,sha256=9GSEnncSczQCX-Cr9nroXRw8fUlvJkd1DftVWxX0bGI,15426
|
|
86
|
+
cortexshift/tui/widgets.py,sha256=cN01544fx05pGh8Rp68ZpzokxMl8QxXgjVJB3W4gjtw,6846
|
|
87
|
+
cortexshift/tui/screens/__init__.py,sha256=WOFd_u1TncHziZX_cB3nbjbc3jzHP4P_TzxZRmhHXlE,2234
|
|
88
|
+
cortexshift/tui/screens/checkpoints.py,sha256=KIyrycD-iM8PqXBa9xsE1sZFzUOocXX6dzH85PLSlzk,7184
|
|
89
|
+
cortexshift/tui/screens/handoffs.py,sha256=jvqT3AgeJ0ZN4CT-ftbpG6BXJhYRChw3ArHsU0ubJmw,7071
|
|
90
|
+
cortexshift/tui/screens/help.py,sha256=2sRrxDXsorC-h0CaXkh9OUgMb5HzZlgoFa0S7sJ-dqo,3937
|
|
91
|
+
cortexshift/tui/screens/overview.py,sha256=JUrZu6vcn5T2AkSu_-ctlr4ONEmu7ZeXLV7IUdUD9AE,7612
|
|
92
|
+
cortexshift/tui/screens/providers.py,sha256=Avg6i4X4UXubeT1RJ4Y48cViUFe2briO8xMqnGhemyc,6732
|
|
93
|
+
cortexshift/tui/screens/repository.py,sha256=azIwKTuwq3nQQfJJqdYmbNw9Fi7wCBSVcjVl3MMekP8,6120
|
|
94
|
+
cortexshift/tui/screens/sessions.py,sha256=yn10-pGKKkJVeuwBtub-_wRRiHxHFW1Hm60EPfE9Vm4,5431
|
|
95
|
+
cortexshift/tui/screens/task.py,sha256=-5PglyXkKDZhbEmyCmJo8R8T0Uc0vRAbTGtCJbwOf00,6910
|
|
96
|
+
cortexshift-0.1.0.dist-info/METADATA,sha256=S0ZLSPpGUn7T1FelTapmRqSHtk1Gsx99Cz_RiV6imq4,8794
|
|
97
|
+
cortexshift-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
98
|
+
cortexshift-0.1.0.dist-info/entry_points.txt,sha256=s59tQ6PO0BnOc6QpbXTFy47QyyQcc6dSgCKQ3Ll26pg,56
|
|
99
|
+
cortexshift-0.1.0.dist-info/licenses/LICENSE,sha256=XGltKOLpx2_uhSAH5QpyFQwRNhMwksitvPGwJt-7i9A,1081
|
|
100
|
+
cortexshift-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CortexShift contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|