abstractcode 0.3.0__py3-none-any.whl → 0.3.2__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.
- abstractcode/__init__.py +1 -1
- abstractcode/cli.py +682 -3
- abstractcode/file_mentions.py +276 -0
- abstractcode/fullscreen_ui.py +1592 -74
- abstractcode/gateway_cli.py +715 -0
- abstractcode/react_shell.py +2474 -116
- abstractcode/terminal_markdown.py +426 -37
- abstractcode/theme.py +244 -0
- abstractcode/workflow_agent.py +630 -112
- abstractcode/workflow_cli.py +229 -0
- abstractcode-0.3.2.dist-info/METADATA +158 -0
- abstractcode-0.3.2.dist-info/RECORD +21 -0
- {abstractcode-0.3.0.dist-info → abstractcode-0.3.2.dist-info}/WHEEL +1 -1
- abstractcode-0.3.0.dist-info/METADATA +0 -270
- abstractcode-0.3.0.dist-info/RECORD +0 -17
- {abstractcode-0.3.0.dist-info → abstractcode-0.3.2.dist-info}/entry_points.txt +0 -0
- {abstractcode-0.3.0.dist-info → abstractcode-0.3.2.dist-info}/licenses/LICENSE +0 -0
- {abstractcode-0.3.0.dist-info → abstractcode-0.3.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _gateway_api(*, gateway_url: Optional[str], gateway_token: Optional[str]):
|
|
8
|
+
from .gateway_cli import GatewayApi, default_gateway_token, default_gateway_url
|
|
9
|
+
|
|
10
|
+
url = str(gateway_url or "").strip() or default_gateway_url()
|
|
11
|
+
token_raw = str(gateway_token or "").strip()
|
|
12
|
+
token = token_raw if token_raw else default_gateway_token()
|
|
13
|
+
return GatewayApi(base_url=url, token=token)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def install_workflow_bundle_command(
|
|
17
|
+
*,
|
|
18
|
+
source: str,
|
|
19
|
+
gateway_url: Optional[str] = None,
|
|
20
|
+
gateway_token: Optional[str] = None,
|
|
21
|
+
overwrite: bool = False,
|
|
22
|
+
output_json: bool = False,
|
|
23
|
+
) -> dict[str, Any]:
|
|
24
|
+
api = _gateway_api(gateway_url=gateway_url, gateway_token=gateway_token)
|
|
25
|
+
try:
|
|
26
|
+
resp = api.upload_bundle(path=source, overwrite=bool(overwrite), reload=True)
|
|
27
|
+
except Exception as e:
|
|
28
|
+
return {"ok": False, "error": str(e)}
|
|
29
|
+
|
|
30
|
+
out = dict(resp or {})
|
|
31
|
+
out.setdefault("gateway_url", str(getattr(api, "base_url", "") or ""))
|
|
32
|
+
if output_json:
|
|
33
|
+
print(json.dumps(out, indent=2, ensure_ascii=False))
|
|
34
|
+
else:
|
|
35
|
+
bref = str(out.get("bundle_ref") or "").strip() or str(out.get("bundle_id") or "").strip() or "?"
|
|
36
|
+
print(f"Installed on gateway: {bref}")
|
|
37
|
+
return out
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def list_workflow_bundles_command(
|
|
41
|
+
*,
|
|
42
|
+
gateway_url: Optional[str] = None,
|
|
43
|
+
gateway_token: Optional[str] = None,
|
|
44
|
+
interface: Optional[str] = None,
|
|
45
|
+
all_versions: bool = False,
|
|
46
|
+
include_deprecated: bool = False,
|
|
47
|
+
output_json: bool = False,
|
|
48
|
+
) -> dict[str, Any]:
|
|
49
|
+
api = _gateway_api(gateway_url=gateway_url, gateway_token=gateway_token)
|
|
50
|
+
try:
|
|
51
|
+
resp = api.list_bundles(all_versions=bool(all_versions), include_deprecated=bool(include_deprecated))
|
|
52
|
+
except Exception as e:
|
|
53
|
+
return {"ok": False, "error": str(e)}
|
|
54
|
+
|
|
55
|
+
bundles_raw = resp.get("items") if isinstance(resp, dict) and isinstance(resp.get("items"), list) else []
|
|
56
|
+
items: list[dict[str, Any]] = []
|
|
57
|
+
for b in bundles_raw:
|
|
58
|
+
if not isinstance(b, dict):
|
|
59
|
+
continue
|
|
60
|
+
bid = str(b.get("bundle_id") or "").strip()
|
|
61
|
+
bver = str(b.get("bundle_version") or "").strip()
|
|
62
|
+
bref = str(b.get("bundle_ref") or "").strip() or (f"{bid}@{bver}" if bid and bver else bid)
|
|
63
|
+
eps_raw = b.get("entrypoints") if isinstance(b.get("entrypoints"), list) else []
|
|
64
|
+
default_fid = str(b.get("default_entrypoint") or "").strip()
|
|
65
|
+
implied_default = len(eps_raw) == 1
|
|
66
|
+
for ep in eps_raw:
|
|
67
|
+
if not isinstance(ep, dict):
|
|
68
|
+
continue
|
|
69
|
+
fid = str(ep.get("flow_id") or "").strip()
|
|
70
|
+
if not fid:
|
|
71
|
+
continue
|
|
72
|
+
deprecated = bool(ep.get("deprecated") is True)
|
|
73
|
+
interfaces = [str(x).strip() for x in list(ep.get("interfaces") or []) if isinstance(x, str) and x.strip()]
|
|
74
|
+
if interface and interface not in interfaces:
|
|
75
|
+
continue
|
|
76
|
+
items.append(
|
|
77
|
+
{
|
|
78
|
+
"bundle_id": bid,
|
|
79
|
+
"bundle_version": bver,
|
|
80
|
+
"bundle_ref": bref,
|
|
81
|
+
"workflow_id": f"{bref}:{fid}" if bref and fid else None,
|
|
82
|
+
"flow_id": fid,
|
|
83
|
+
"name": str(ep.get("name") or "") or fid,
|
|
84
|
+
"description": str(ep.get("description") or "") or "",
|
|
85
|
+
"interfaces": interfaces,
|
|
86
|
+
"default": bool(implied_default or (default_fid and fid == default_fid)),
|
|
87
|
+
"deprecated": bool(deprecated),
|
|
88
|
+
"deprecated_reason": str(ep.get("deprecated_reason") or "") or "",
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
items.sort(key=lambda x: (str(x.get("bundle_id") or ""), str(x.get("bundle_version") or ""), str(x.get("flow_id") or "")))
|
|
93
|
+
out = {"ok": True, "gateway_url": str(getattr(api, "base_url", "") or ""), "count": len(items), "entrypoints": items}
|
|
94
|
+
if output_json:
|
|
95
|
+
print(json.dumps(out, indent=2, ensure_ascii=False))
|
|
96
|
+
else:
|
|
97
|
+
if not items:
|
|
98
|
+
print("No workflows found on the gateway.")
|
|
99
|
+
for it in items:
|
|
100
|
+
name = it.get("name") or it.get("workflow_id") or ""
|
|
101
|
+
iface = ""
|
|
102
|
+
interfaces = it.get("interfaces") or []
|
|
103
|
+
if isinstance(interfaces, list) and interfaces:
|
|
104
|
+
iface = f" [{', '.join(interfaces)}]"
|
|
105
|
+
default = " *" if it.get("default") else ""
|
|
106
|
+
dep = " (deprecated)" if it.get("deprecated") else ""
|
|
107
|
+
wid = str(it.get("workflow_id") or it.get("bundle_ref") or "")
|
|
108
|
+
print(f"{wid}{default}{dep} {name}{iface}")
|
|
109
|
+
return out
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def workflow_bundle_info_command(
|
|
113
|
+
*,
|
|
114
|
+
bundle_ref: str,
|
|
115
|
+
gateway_url: Optional[str] = None,
|
|
116
|
+
gateway_token: Optional[str] = None,
|
|
117
|
+
output_json: bool = False,
|
|
118
|
+
) -> dict[str, Any]:
|
|
119
|
+
api = _gateway_api(gateway_url=gateway_url, gateway_token=gateway_token)
|
|
120
|
+
ref = str(bundle_ref or "").strip()
|
|
121
|
+
if not ref:
|
|
122
|
+
return {"ok": False, "error": "bundle_ref is required"}
|
|
123
|
+
bid, ver = (ref.split("@", 1) + [""])[:2] if "@" in ref else (ref, "")
|
|
124
|
+
ver2 = ver.strip() or None
|
|
125
|
+
try:
|
|
126
|
+
resp = api.get_bundle(bundle_id=str(bid).strip(), bundle_version=ver2)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
return {"ok": False, "error": str(e)}
|
|
129
|
+
|
|
130
|
+
out = dict(resp or {})
|
|
131
|
+
out.setdefault("ok", True)
|
|
132
|
+
out.setdefault("gateway_url", str(getattr(api, "base_url", "") or ""))
|
|
133
|
+
if output_json:
|
|
134
|
+
print(json.dumps(out, indent=2, ensure_ascii=False))
|
|
135
|
+
else:
|
|
136
|
+
bref = str(out.get("bundle_ref") or out.get("bundle_id") or "").strip()
|
|
137
|
+
print(bref or ref)
|
|
138
|
+
de = out.get("default_entrypoint")
|
|
139
|
+
if isinstance(de, str) and de.strip():
|
|
140
|
+
print(f"default: {de.strip()}")
|
|
141
|
+
eps = out.get("entrypoints") if isinstance(out.get("entrypoints"), list) else []
|
|
142
|
+
for ep in eps:
|
|
143
|
+
if not isinstance(ep, dict):
|
|
144
|
+
continue
|
|
145
|
+
fid = str(ep.get("flow_id") or "").strip()
|
|
146
|
+
name = str(ep.get("name") or "").strip() or fid
|
|
147
|
+
if fid:
|
|
148
|
+
print(f"- {fid} {name}")
|
|
149
|
+
return out
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def remove_workflow_bundle_command(
|
|
153
|
+
*,
|
|
154
|
+
bundle_ref: str,
|
|
155
|
+
gateway_url: Optional[str] = None,
|
|
156
|
+
gateway_token: Optional[str] = None,
|
|
157
|
+
output_json: bool = False,
|
|
158
|
+
) -> dict[str, Any]:
|
|
159
|
+
api = _gateway_api(gateway_url=gateway_url, gateway_token=gateway_token)
|
|
160
|
+
try:
|
|
161
|
+
resp = api.remove_bundle(bundle_ref=str(bundle_ref or "").strip(), reload=True)
|
|
162
|
+
except Exception as e:
|
|
163
|
+
return {"ok": False, "error": str(e)}
|
|
164
|
+
|
|
165
|
+
out = dict(resp or {})
|
|
166
|
+
out.setdefault("gateway_url", str(getattr(api, "base_url", "") or ""))
|
|
167
|
+
if output_json:
|
|
168
|
+
print(json.dumps(out, indent=2, ensure_ascii=False))
|
|
169
|
+
else:
|
|
170
|
+
removed = out.get("removed")
|
|
171
|
+
bref = str(out.get("bundle_ref") or bundle_ref or "").strip() or "?"
|
|
172
|
+
print(f"Removed {removed} bundle(s) from gateway for {bref}")
|
|
173
|
+
return out
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def deprecate_workflow_bundle_command(
|
|
177
|
+
*,
|
|
178
|
+
bundle_id: str,
|
|
179
|
+
flow_id: Optional[str] = None,
|
|
180
|
+
reason: Optional[str] = None,
|
|
181
|
+
gateway_url: Optional[str] = None,
|
|
182
|
+
gateway_token: Optional[str] = None,
|
|
183
|
+
output_json: bool = False,
|
|
184
|
+
) -> dict[str, Any]:
|
|
185
|
+
api = _gateway_api(gateway_url=gateway_url, gateway_token=gateway_token)
|
|
186
|
+
bid = str(bundle_id or "").strip()
|
|
187
|
+
if not bid:
|
|
188
|
+
return {"ok": False, "error": "bundle_id is required"}
|
|
189
|
+
try:
|
|
190
|
+
resp = api.deprecate_bundle(bundle_id=bid, flow_id=flow_id, reason=reason)
|
|
191
|
+
except Exception as e:
|
|
192
|
+
return {"ok": False, "error": str(e)}
|
|
193
|
+
|
|
194
|
+
out = dict(resp or {})
|
|
195
|
+
out.setdefault("gateway_url", str(getattr(api, "base_url", "") or ""))
|
|
196
|
+
if output_json:
|
|
197
|
+
print(json.dumps(out, indent=2, ensure_ascii=False))
|
|
198
|
+
else:
|
|
199
|
+
fid = str(out.get("flow_id") or "").strip() or "*"
|
|
200
|
+
print(f"Deprecated on gateway: {bid}:{fid}")
|
|
201
|
+
return out
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def undeprecate_workflow_bundle_command(
|
|
205
|
+
*,
|
|
206
|
+
bundle_id: str,
|
|
207
|
+
flow_id: Optional[str] = None,
|
|
208
|
+
gateway_url: Optional[str] = None,
|
|
209
|
+
gateway_token: Optional[str] = None,
|
|
210
|
+
output_json: bool = False,
|
|
211
|
+
) -> dict[str, Any]:
|
|
212
|
+
api = _gateway_api(gateway_url=gateway_url, gateway_token=gateway_token)
|
|
213
|
+
bid = str(bundle_id or "").strip()
|
|
214
|
+
if not bid:
|
|
215
|
+
return {"ok": False, "error": "bundle_id is required"}
|
|
216
|
+
try:
|
|
217
|
+
resp = api.undeprecate_bundle(bundle_id=bid, flow_id=flow_id)
|
|
218
|
+
except Exception as e:
|
|
219
|
+
return {"ok": False, "error": str(e)}
|
|
220
|
+
|
|
221
|
+
out = dict(resp or {})
|
|
222
|
+
out.setdefault("gateway_url", str(getattr(api, "base_url", "") or ""))
|
|
223
|
+
if output_json:
|
|
224
|
+
print(json.dumps(out, indent=2, ensure_ascii=False))
|
|
225
|
+
else:
|
|
226
|
+
fid = str(out.get("flow_id") or "").strip() or "*"
|
|
227
|
+
removed = bool(out.get("removed") is True)
|
|
228
|
+
print(f"Undeprecated on gateway: {bid}:{fid} ({'changed' if removed else 'no-op'})")
|
|
229
|
+
return out
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: abstractcode
|
|
3
|
+
Version: 0.3.2
|
|
4
|
+
Summary: A clean terminal CLI for multi-agent agentic coding
|
|
5
|
+
Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
|
|
6
|
+
Maintainer-email: Laurent-Philippe Albou <contact@abstractcore.ai>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://abstractcore.ai
|
|
9
|
+
Project-URL: Documentation, https://abstractcore.ai
|
|
10
|
+
Project-URL: Repository, https://github.com/lpalbou/abstractcode
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/lpalbou/abstractcode/issues
|
|
12
|
+
Keywords: ai,llm,cli,coding,agent,multi-agent,agentic,terminal,abstractcore,abstractruntime,abstractagent
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: abstractagent>=0.2.0
|
|
27
|
+
Requires-Dist: abstractruntime>=0.2.0
|
|
28
|
+
Requires-Dist: abstractcore[tools]>=2.6.8
|
|
29
|
+
Requires-Dist: ddgs<10.0.0,>=9.10.0
|
|
30
|
+
Requires-Dist: prompt_toolkit>=3.0.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
36
|
+
Provides-Extra: flow
|
|
37
|
+
Requires-Dist: abstractflow>=0.1.0; extra == "flow"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# AbstractCode
|
|
41
|
+
|
|
42
|
+
Durable terminal TUI for agentic coding on the Abstract* stack (**AbstractAgent + AbstractRuntime + AbstractCore**).
|
|
43
|
+
|
|
44
|
+
Status: **pre-alpha** (APIs and UX may change).
|
|
45
|
+
|
|
46
|
+
Next: [`docs/getting-started.md`](docs/getting-started.md).
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- Interactive TUI (`abstractcode`) with **durable runs** (resume/pause/cancel), snapshots, and logs
|
|
51
|
+
- **Approval-gated tools** by default (with an allowlist you can configure)
|
|
52
|
+
- Built-in agents: `react`, `memact`, `codeact`
|
|
53
|
+
- VisualFlow workflows:
|
|
54
|
+
- run locally: `abstractcode flow ...` (optional extra)
|
|
55
|
+
- run as an agent: `abstractcode --agent <flow_ref>`
|
|
56
|
+
- Remote tool execution via **MCP** (`/mcp`, `/executor`)
|
|
57
|
+
- Optional gateway-first Web UI in `web/`
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
Python: **3.10+**
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install abstractcode
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Optional (run VisualFlow locally via `abstractcode flow ...`):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install "abstractcode[flow]"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
From source (development):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install -e ".[dev]"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quickstart (TUI)
|
|
80
|
+
|
|
81
|
+
Ollama (default provider):
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
abstractcode --provider ollama --model qwen3:1.7b-q4_K_M
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
OpenAI-compatible server (e.g. LM Studio):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
abstractcode --provider openai --base-url http://127.0.0.1:1234/v1 --model qwen/qwen3-next-80b
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Inside the app:
|
|
94
|
+
- `/help` shows the authoritative command list
|
|
95
|
+
- type a task (or use `/task ...`)
|
|
96
|
+
- tool approvals: `/auto-accept` (or start with `--auto-approve`)
|
|
97
|
+
- attach files with `@path/to/file` in your prompt
|
|
98
|
+
|
|
99
|
+
## Persistence (durable runs)
|
|
100
|
+
|
|
101
|
+
Default paths:
|
|
102
|
+
- state file: `~/.abstractcode/state.json`
|
|
103
|
+
- durable stores: `~/.abstractcode/state.d/`
|
|
104
|
+
- saved settings: `~/.abstractcode/state.config.json`
|
|
105
|
+
|
|
106
|
+
Disable persistence:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
abstractcode --no-state
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Workflows
|
|
113
|
+
|
|
114
|
+
- Local runs: `abstractcode flow run <flow_id_or_path> ...` (requires `abstractcode[flow]`)
|
|
115
|
+
- Workflow agent: `abstractcode --agent /path/to/workflow.json ...`
|
|
116
|
+
- Remote control-plane: `abstractcode gateway --help`
|
|
117
|
+
- Bundle management on a gateway: `abstractcode workflow --help`
|
|
118
|
+
|
|
119
|
+
Details: [`docs/workflows.md`](docs/workflows.md).
|
|
120
|
+
|
|
121
|
+
## Web UI
|
|
122
|
+
|
|
123
|
+
The web host lives in `web/` and connects to an `abstractgateway` at `/api/gateway/*`.
|
|
124
|
+
|
|
125
|
+
Start here:
|
|
126
|
+
- [`docs/web.md`](docs/web.md)
|
|
127
|
+
- [`docs/deployment-web.md`](docs/deployment-web.md)
|
|
128
|
+
|
|
129
|
+
## Documentation
|
|
130
|
+
|
|
131
|
+
- Start here: [`docs/getting-started.md`](docs/getting-started.md)
|
|
132
|
+
- FAQ: [`docs/faq.md`](docs/faq.md)
|
|
133
|
+
- Docs index: [`docs/README.md`](docs/README.md)
|
|
134
|
+
- [`docs/architecture.md`](docs/architecture.md)
|
|
135
|
+
- [`docs/cli.md`](docs/cli.md)
|
|
136
|
+
- [`docs/api.md`](docs/api.md)
|
|
137
|
+
- [`docs/workflows.md`](docs/workflows.md)
|
|
138
|
+
- [`docs/ui_events.md`](docs/ui_events.md)
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pip install -e ".[dev]"
|
|
144
|
+
pytest -q
|
|
145
|
+
ruff check .
|
|
146
|
+
black .
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Project
|
|
150
|
+
|
|
151
|
+
- Changelog: [`CHANGELOG.md`](CHANGELOG.md)
|
|
152
|
+
- Contributing: [`CONTRIBUTING.md`](CONTRIBUTING.md)
|
|
153
|
+
- Security: [`SECURITY.md`](SECURITY.md)
|
|
154
|
+
- Acknowledgments: [`ACKNOWLEDMENTS.md`](ACKNOWLEDMENTS.md)
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT. See [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
abstractcode/__init__.py,sha256=O-DbZctdBATghwjZUCeHBYwe84a5wuRK6cd4z3kkyzg,658
|
|
2
|
+
abstractcode/cli.py,sha256=sDpKTREeRGWcXyFwnVEZLBuLQcpD4WuOnGU9nTx6SMU,44440
|
|
3
|
+
abstractcode/file_mentions.py,sha256=KFtFX_Ofjk_9r9Nirt1IXKLWr1VsSrXimHnRYxxvRYs,8664
|
|
4
|
+
abstractcode/flow_cli.py,sha256=CQq1g4rFylSW7y8ZZeQYmfroAXG1dDtmuOF8tc1BF2Y,49235
|
|
5
|
+
abstractcode/fullscreen_ui.py,sha256=rcYETwA5nMFNE1cNgIDTF3zUFLlqtJoveQ2W6feR7Ao,117536
|
|
6
|
+
abstractcode/gateway_cli.py,sha256=ajAYI0IUK3FBAVx5HdDh_zThyU7-tABre8meAUuO_cU,25251
|
|
7
|
+
abstractcode/input_handler.py,sha256=W2ImW4Yr3HMMD5SNPZOZJendVL-CRdYR5idXTWroxAY,2240
|
|
8
|
+
abstractcode/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
9
|
+
abstractcode/react_shell.py,sha256=lgydDok2ZgNwVDForvAYT9MxVYk7SiVmuUFMnYEOpvg,387224
|
|
10
|
+
abstractcode/recall.py,sha256=xu-xz8-kvkmAW6pKoAIcwox5jHWMBfBKExsA8fgwg_A,13040
|
|
11
|
+
abstractcode/remember.py,sha256=cmJlw-nR0OvK7tkN5B0sZSMaYlECl_tAyO34c-IL0Uo,6158
|
|
12
|
+
abstractcode/terminal_markdown.py,sha256=mL3YdBZt25DoJec23EXgve2zH4OMDJLxBnQfWvsSSh0,21465
|
|
13
|
+
abstractcode/theme.py,sha256=WWlMsbqIj4Z-jylOTnmV_zTirf87tdbrlrRsIaTBfQE,6689
|
|
14
|
+
abstractcode/workflow_agent.py,sha256=J8pw7r79lJZ58aqMXsRTabbgxEJzIlJHb8__oUPg_08,56744
|
|
15
|
+
abstractcode/workflow_cli.py,sha256=gabA7RzPe9-qkI3MSaRCrxErWeCl1faRa-oTGULxp_w,8936
|
|
16
|
+
abstractcode-0.3.2.dist-info/licenses/LICENSE,sha256=f8z-PbFM_xH0eqLsha8_nEg1jbm1eVtC2NNjtXhtuCo,1080
|
|
17
|
+
abstractcode-0.3.2.dist-info/METADATA,sha256=_gu5zFXrd6aZScr8oUGK_9mz0YNwPDsLQABJB-mK5dE,4657
|
|
18
|
+
abstractcode-0.3.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
19
|
+
abstractcode-0.3.2.dist-info/entry_points.txt,sha256=M4AOKb4h2vKWy5OMg-czeJE2aXunVKlljUPN8BUtwoE,51
|
|
20
|
+
abstractcode-0.3.2.dist-info/top_level.txt,sha256=n9Hfv6cnmL55HoxXsA3V1YLbQxYAr58-UMLbdr-FCNw,13
|
|
21
|
+
abstractcode-0.3.2.dist-info/RECORD,,
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: abstractcode
|
|
3
|
-
Version: 0.3.0
|
|
4
|
-
Summary: A clean terminal CLI for multi-agent agentic coding
|
|
5
|
-
Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
|
|
6
|
-
Maintainer-email: Laurent-Philippe Albou <contact@abstractcore.ai>
|
|
7
|
-
License: MIT
|
|
8
|
-
Project-URL: Homepage, https://abstractcore.ai
|
|
9
|
-
Project-URL: Documentation, https://abstractcore.ai
|
|
10
|
-
Project-URL: Repository, https://github.com/lpalbou/abstractcode
|
|
11
|
-
Project-URL: Bug Tracker, https://github.com/lpalbou/abstractcode/issues
|
|
12
|
-
Keywords: ai,llm,cli,coding,agent,multi-agent,agentic,terminal,abstractcore,abstractruntime,abstractagent
|
|
13
|
-
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
-
Classifier: Operating System :: OS Independent
|
|
17
|
-
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
-
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
-
Requires-Python: >=3.10
|
|
24
|
-
Description-Content-Type: text/markdown
|
|
25
|
-
License-File: LICENSE
|
|
26
|
-
Requires-Dist: abstractagent>=0.2.0
|
|
27
|
-
Requires-Dist: abstractruntime>=0.2.0
|
|
28
|
-
Requires-Dist: abstractcore[tools]>=2.6.8
|
|
29
|
-
Requires-Dist: ddgs<10.0.0,>=9.10.0
|
|
30
|
-
Requires-Dist: prompt_toolkit>=3.0.0
|
|
31
|
-
Provides-Extra: dev
|
|
32
|
-
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
33
|
-
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
34
|
-
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
35
|
-
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
36
|
-
Provides-Extra: flow
|
|
37
|
-
Requires-Dist: abstractflow>=0.1.0; extra == "flow"
|
|
38
|
-
Dynamic: license-file
|
|
39
|
-
|
|
40
|
-
# AbstractCode
|
|
41
|
-
|
|
42
|
-
Terminal TUI for the AbstractFramework:
|
|
43
|
-
- run **agents** (ReAct / CodeAct)
|
|
44
|
-
- run **workflows** authored in AbstractFlow (VisualFlow JSON)
|
|
45
|
-
- keep everything **durable** via AbstractRuntime (runs, ledger, artifacts)
|
|
46
|
-
|
|
47
|
-
## Install
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
pip install abstractcode
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
To run AbstractFlow workflows from AbstractCode:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
pip install "abstractcode[flow]"
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Start the TUI (Agents)
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
abstractcode --provider lmstudio --model qwen/qwen3-next-80b
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Interaction model:
|
|
66
|
-
- commands are slash-prefixed (`/help`)
|
|
67
|
-
- any non-command line starts a task (same as `/task ...`)
|
|
68
|
-
|
|
69
|
-
### Useful commands
|
|
70
|
-
- `/status` (run status)
|
|
71
|
-
- `/auto-accept on|off` (tool approvals)
|
|
72
|
-
- `/max-tokens N` (or `-1` auto-detect)
|
|
73
|
-
- `/compact [light|standard|heavy] [--preserve N] [focus...]` (durable compaction)
|
|
74
|
-
- `/spans`, `/expand <span> [--show] [--into-context]` (provenance recall)
|
|
75
|
-
- `/recall [--since ISO] [--until ISO] [--tag k=v] [--q text] [--into-context]`
|
|
76
|
-
- `/snapshot save|load|list`
|
|
77
|
-
|
|
78
|
-
### Persistence
|
|
79
|
-
By default, AbstractCode uses `~/.abstractcode/state.json` and stores the durable data in `~/.abstractcode/state.d/`.
|
|
80
|
-
- disable persistence: `abstractcode --no-state`
|
|
81
|
-
- override path: `ABSTRACTCODE_STATE_FILE=... abstractcode`
|
|
82
|
-
|
|
83
|
-
## Workflow Agents (VisualFlow as `--agent`)
|
|
84
|
-
|
|
85
|
-
AbstractCode can run an AbstractFlow VisualFlow workflow *as an agent* (instead of using the built-in `react|codeact|memact` agents).
|
|
86
|
-
|
|
87
|
-
### Requirements (`abstractcode.agent.v1`)
|
|
88
|
-
- The workflow JSON must declare: `interfaces: ["abstractcode.agent.v1"]`
|
|
89
|
-
- The workflow must expose these pins:
|
|
90
|
-
- `On Flow Start`: output pins:
|
|
91
|
-
- `request` (type `string`)
|
|
92
|
-
- `provider` (type `provider`)
|
|
93
|
-
- `model` (type `model`)
|
|
94
|
-
- `tools` (type `tools`)
|
|
95
|
-
- `On Flow End`: input pin `response` (type `string`)
|
|
96
|
-
|
|
97
|
-
Recommended (optional) pins:
|
|
98
|
-
- `On Flow Start`: `context` (object), `max_iterations` (number)
|
|
99
|
-
- `On Flow End`: `meta` (object), `scratchpad` (object), `raw_result` (object)
|
|
100
|
-
|
|
101
|
-
### Authoring in the AbstractFlow visual editor
|
|
102
|
-
- **Mark the interface**: click `📂 Load` → select the workflow → in the right preview panel find **Interfaces** → click the ✏️ icon → enable **AbstractCode Agent (v1)** → **Save Interfaces**
|
|
103
|
-
- **Pins are scaffolded automatically**: when the interface is enabled, AbstractFlow will ensure `On Flow Start` and `On Flow End` have the required pins. You can still add/remove optional pins as needed.
|
|
104
|
-
|
|
105
|
-
Tip: an example workflow is shipped at `abstractflow/web/flows/acagent01.json` (implements the interface).
|
|
106
|
-
|
|
107
|
-
### What is `meta` and how do I use it?
|
|
108
|
-
`On Flow End.meta` is an **optional JSON object** for host-facing metadata (usage, trace ids, warnings, raw provider info, etc.).
|
|
109
|
-
|
|
110
|
-
It is intentionally **not strictly validated** today (the host treats it as opaque JSON). To make workflows portable and predictable across hosts, we recommend using a small “envelope” shape:
|
|
111
|
-
|
|
112
|
-
```json
|
|
113
|
-
{
|
|
114
|
-
"schema": "abstractcode.agent.v1.meta",
|
|
115
|
-
"version": 1,
|
|
116
|
-
"provider": "lmstudio",
|
|
117
|
-
"model": "qwen/qwen3-next-80b",
|
|
118
|
-
"usage": { "input_tokens": 123, "output_tokens": 456 },
|
|
119
|
-
"trace": { "trace_id": "..." },
|
|
120
|
-
"warnings": ["..."],
|
|
121
|
-
"debug": {}
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Hosts should treat unknown fields as allowed and ignore what they don’t understand (forward-compatible).
|
|
126
|
-
|
|
127
|
-
Typical ways to produce it inside a workflow:
|
|
128
|
-
- Wire `LLM Call.result` (object) → `On Flow End.meta`
|
|
129
|
-
- Wire `Agent.result` (object) → `On Flow End.meta`
|
|
130
|
-
- Or build your own object and wire it into `meta`
|
|
131
|
-
|
|
132
|
-
When present, AbstractCode attaches it to the assistant message metadata as `workflow_meta`.
|
|
133
|
-
|
|
134
|
-
### Workflow-driven status updates (footer / live UX)
|
|
135
|
-
Inside a workflow, you can update AbstractCode’s footer status text by emitting the reserved event:
|
|
136
|
-
- Add an **Emit Event** node
|
|
137
|
-
- Set **name** to `abstractcode.status`
|
|
138
|
-
- Set **payload** to:
|
|
139
|
-
- a string (e.g. `"Enrich Query..."`), or
|
|
140
|
-
- an object like `{ "text": "Enrich Query...", "duration": -1 }`
|
|
141
|
-
|
|
142
|
-
`duration` is seconds:
|
|
143
|
-
- default: `-1` (sticky)
|
|
144
|
-
- if `> 0`: auto-clears after the timeout unless superseded by a newer status
|
|
145
|
-
|
|
146
|
-
Example workflow: `abstractflow/web/flows/acagent_status_demo.json` (3 status updates, each separated by a 2s Delay).
|
|
147
|
-
|
|
148
|
-
### Workflow-driven UI events (messages + tool UX)
|
|
149
|
-
Workflows can also emit additional reserved events for host UX:
|
|
150
|
-
- `abstractcode.message`: show a message/notification (payload is a string or `{text, level?, title?}`)
|
|
151
|
-
- `abstractcode.tool_execution`: render a tool-call block (payload is a tool call object or a list)
|
|
152
|
-
- recommended shape: `{name, arguments, call_id?}`
|
|
153
|
-
- `abstractcode.tool_result`: render a tool-result block (payload is a tool result object or a list)
|
|
154
|
-
- recommended shape: `{name, call_id?, success?, output?, error?}`
|
|
155
|
-
|
|
156
|
-
Full contract (recommended for integrators): `docs/ui_events.md`
|
|
157
|
-
|
|
158
|
-
Example workflows:
|
|
159
|
-
- `abstractflow/web/flows/acagent_message_demo.json`
|
|
160
|
-
- `abstractflow/web/flows/acagent_tool_events_demo.json`
|
|
161
|
-
|
|
162
|
-
### Durable ask+wait (prompt the user and resume)
|
|
163
|
-
For workflows that need human input, you can:
|
|
164
|
-
- use the **Ask User** node (WAIT_USER), or
|
|
165
|
-
- use **Wait Event** with a `prompt` field (WAIT_EVENT), which is also durable and network-friendly.
|
|
166
|
-
|
|
167
|
-
Example workflow: `abstractflow/web/flows/acagent_ask_demo.json`
|
|
168
|
-
|
|
169
|
-
### Run
|
|
170
|
-
Use `--agent` with a workflow id/name (from the flows directory) or a direct JSON path:
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
abstractcode --agent acagent01
|
|
174
|
-
abstractcode --agent abstractflow/web/flows/acagent01.json
|
|
175
|
-
|
|
176
|
-
# If your flows are stored elsewhere:
|
|
177
|
-
ABSTRACTFLOW_FLOWS_DIR=/path/to/flows abstractcode --agent my_flow_id
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## Run Workflows (AbstractFlow VisualFlow)
|
|
181
|
-
|
|
182
|
-
### From the CLI
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
abstractcode flow run <flow_id_or_path> [inputs...]
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
Inputs can be passed as flags (no JSON typing required):
|
|
189
|
-
|
|
190
|
-
```bash
|
|
191
|
-
abstractcode flow run abstractflow/web/flows/4e2f2329.json --query "who are you?"
|
|
192
|
-
|
|
193
|
-
abstractcode flow run abstractflow/web/flows/b3a9d7c1.json \
|
|
194
|
-
--query "who are you?" \
|
|
195
|
-
--max_web_search 15 \
|
|
196
|
-
--max_fetch_url 50 \
|
|
197
|
-
--follow_up_questions true
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Other input options:
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
abstractcode flow run deep-research-pro --input-json-file params.json
|
|
204
|
-
abstractcode flow run deep-research-pro --param max_web_search=15 --param follow_up_questions=true
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
Tool approvals:
|
|
208
|
-
- approval-gated by default (type `a` once to approve all remaining calls for that run)
|
|
209
|
-
- auto-approve (unsafe): `--accept-tools` (alias `--auto-approve`)
|
|
210
|
-
|
|
211
|
-
Controls:
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
abstractcode flow resume
|
|
215
|
-
abstractcode flow pause
|
|
216
|
-
abstractcode flow resume-run
|
|
217
|
-
abstractcode flow cancel
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
Run discovery and event injection:
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
abstractcode flow runs
|
|
224
|
-
abstractcode flow attach <run_id>
|
|
225
|
-
abstractcode flow emit --name my_event --scope session --payload-json '{"k":"v"}'
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
Flow state:
|
|
229
|
-
- default: `~/.abstractcode/flow_state.json` (stores in `~/.abstractcode/flow_state.d/`)
|
|
230
|
-
- override with `ABSTRACTCODE_FLOW_STATE_FILE=...`
|
|
231
|
-
- flow discovery default dir: `ABSTRACTFLOW_FLOWS_DIR=...`
|
|
232
|
-
|
|
233
|
-
### From inside the TUI (keeps outputs in context)
|
|
234
|
-
|
|
235
|
-
```text
|
|
236
|
-
/flow run deep-research-pro --query "..." --max_web_search 10 --follow_up_questions true
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
`ANSWER_USER` outputs from the workflow are appended into the current conversation’s active context so you can continue the dialogue naturally.
|
|
240
|
-
|
|
241
|
-
## Development (Monorepo)
|
|
242
|
-
|
|
243
|
-
```bash
|
|
244
|
-
pip install -e ./abstractcore -e ./abstractruntime -e ./abstractagent -e ./abstractflow -e ./abstractcode
|
|
245
|
-
abstractcode --help
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
## Environment variables
|
|
249
|
-
- `ABSTRACTCODE_STATE_FILE`
|
|
250
|
-
- `ABSTRACTCODE_FLOW_STATE_FILE`
|
|
251
|
-
- `ABSTRACTFLOW_FLOWS_DIR`
|
|
252
|
-
|
|
253
|
-
## Default Tools
|
|
254
|
-
|
|
255
|
-
AbstractCode provides a curated set of 10 tools for coding tasks (ReAct agent):
|
|
256
|
-
|
|
257
|
-
| Tool | Description |
|
|
258
|
-
|------|-------------|
|
|
259
|
-
| `list_files` | Find and list files using glob patterns (case-insensitive) |
|
|
260
|
-
| `search_files` | Search for text patterns inside files using regex |
|
|
261
|
-
| `analyze_code` | Outline a Python/JS file (imports/classes/functions + line ranges) |
|
|
262
|
-
| `read_file` | Read file contents with optional line range |
|
|
263
|
-
| `write_file` | Write content to files, creating directories as needed |
|
|
264
|
-
| `edit_file` | Edit files by replacing text patterns (supports regex, line ranges, preview mode) |
|
|
265
|
-
| `execute_command` | Execute shell commands with security controls |
|
|
266
|
-
| `web_search` | Search the web via DuckDuckGo (no API key required) |
|
|
267
|
-
| `fetch_url` | Fetch a URL and return text/metadata (best-effort parsing) |
|
|
268
|
-
| `self_improve` | Log improvement suggestions for later review |
|
|
269
|
-
|
|
270
|
-
When running `--agent codeact`, AbstractCode exposes `execute_python` instead of the ReAct toolset.
|