evo-cli 0.14.4__tar.gz → 0.15.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.
- {evo_cli-0.14.4 → evo_cli-0.15.0}/PKG-INFO +1 -1
- evo_cli-0.15.0/evo_cli/VERSION +1 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/_model.py +59 -1
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/_mutate.py +19 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/_server.py +37 -1
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/serve.py +3 -3
- evo_cli-0.15.0/evo_cli/commands/harness/web/assets/index-Bo-8pFen.js +257 -0
- evo_cli-0.14.4/evo_cli/commands/harness/web/assets/index-CiUsumT3.css → evo_cli-0.15.0/evo_cli/commands/harness/web/assets/index-ByHqvOcG.css +1 -1
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/index.html +2 -2
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli.egg-info/PKG-INFO +1 -1
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli.egg-info/SOURCES.txt +2 -2
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_harness.py +35 -0
- evo_cli-0.14.4/evo_cli/VERSION +0 -1
- evo_cli-0.14.4/evo_cli/commands/harness/web/assets/index-BpeLRwRd.js +0 -242
- {evo_cli-0.14.4 → evo_cli-0.15.0}/Containerfile +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/HISTORY.md +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/LICENSE +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/MANIFEST.in +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/README.md +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/__init__.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/__main__.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/base.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/cli.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/__init__.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/claude_code.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/cloudflare.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/cred.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/download.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/fix_claude.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/gdrive.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/gh.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/__init__.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/_dag.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/_git.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/_paths.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/_render.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/check.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/edit.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/pull.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/show.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/views.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/harness/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/hwid.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/hwid_reset.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/localproxy.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/mcp.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/miniconda.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/netcheck.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/opencode.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/plantuml.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/site2s.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/ssh.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/sysmon.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/tts.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/update.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/commands/wifi.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/console.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/credentials/__init__.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/credentials/doctor.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/credentials/google_oauth.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/credentials/migrate.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/credentials/oauth_flow.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/credentials/registry.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/credentials/store.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/credentials/sync.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/mcp_registry.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/tts/__init__.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/tts/chunking.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/tts/core.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/tts/creds.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/tts/errors.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/tts/http.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/tts/openai.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/tts/player.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli/tts/vbee.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli.egg-info/dependency_links.txt +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli.egg-info/entry_points.txt +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli.egg-info/requires.txt +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/evo_cli.egg-info/top_level.txt +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/pyproject.toml +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/setup.cfg +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/__init__.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_claude_code.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_cli.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_console.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_cred.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_download.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_fix_claude.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_gh.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_harness_dag.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_mcp.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_opencode.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_plantuml.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_sysmon.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_tts.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_update.py +0 -0
- {evo_cli-0.14.4 → evo_cli-0.15.0}/tests/test_wifi.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.15.0
|
|
@@ -32,6 +32,10 @@ def contracts_file(manifest_path: Path) -> Path:
|
|
|
32
32
|
return harness_root(manifest_path) / "contracts.yaml"
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def deployments_file(manifest_path: Path) -> Path:
|
|
36
|
+
return harness_root(manifest_path) / "deployments.yaml"
|
|
37
|
+
|
|
38
|
+
|
|
35
39
|
def tone_of(section: str, status: str | None) -> str:
|
|
36
40
|
value = (status or "").strip().lower()
|
|
37
41
|
if not value:
|
|
@@ -218,6 +222,60 @@ def load_seams(manifest_path: Path) -> list[dict]:
|
|
|
218
222
|
return out
|
|
219
223
|
|
|
220
224
|
|
|
225
|
+
def load_deployments(manifest_path: Path) -> dict:
|
|
226
|
+
path = deployments_file(manifest_path)
|
|
227
|
+
empty = {"version": 0, "configVersion": "", "environments": [], "tenants": [], "deployments": []}
|
|
228
|
+
if not path.is_file():
|
|
229
|
+
return empty
|
|
230
|
+
raw = read_yaml(path, required=False)
|
|
231
|
+
if not isinstance(raw, dict):
|
|
232
|
+
return empty
|
|
233
|
+
|
|
234
|
+
environments = [str(e) for e in (raw.get("environments") or []) if e is not None]
|
|
235
|
+
|
|
236
|
+
tenants = []
|
|
237
|
+
for tenant in raw.get("tenants") or []:
|
|
238
|
+
if not isinstance(tenant, dict):
|
|
239
|
+
continue
|
|
240
|
+
tenants.append(
|
|
241
|
+
{
|
|
242
|
+
"id": str(tenant.get("id") or ""),
|
|
243
|
+
"code": str(tenant.get("code") or ""),
|
|
244
|
+
"name": str(tenant.get("name") or ""),
|
|
245
|
+
"aliases": [str(a) for a in (tenant.get("aliases") or [])],
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
deployments = []
|
|
250
|
+
for index, dep in enumerate(raw.get("deployments") or []):
|
|
251
|
+
if not isinstance(dep, dict):
|
|
252
|
+
continue
|
|
253
|
+
deployments.append(
|
|
254
|
+
{
|
|
255
|
+
"index": index,
|
|
256
|
+
"deploymentId": str(dep.get("deployment_id") or f"deployment-{index}"),
|
|
257
|
+
"product": str(dep.get("product") or ""),
|
|
258
|
+
"tenantId": str(dep.get("tenant_id")) if dep.get("tenant_id") else "",
|
|
259
|
+
"environment": str(dep.get("environment") or ""),
|
|
260
|
+
"kind": str(dep.get("deployment_kind") or ""),
|
|
261
|
+
"webUrl": str(dep.get("web_url")) if dep.get("web_url") else "",
|
|
262
|
+
"apiUrl": str(dep.get("api_url")) if dep.get("api_url") else "",
|
|
263
|
+
"authUrl": str(dep.get("auth_url")) if dep.get("auth_url") else "",
|
|
264
|
+
"capabilities": [str(c) for c in (dep.get("capabilities") or [])],
|
|
265
|
+
"status": str(dep.get("status") or ""),
|
|
266
|
+
"aliases": [str(a) for a in (dep.get("aliases") or [])],
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
"version": raw.get("version") or 0,
|
|
272
|
+
"configVersion": str(raw.get("config_version") or ""),
|
|
273
|
+
"environments": environments,
|
|
274
|
+
"tenants": tenants,
|
|
275
|
+
"deployments": deployments,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
|
|
221
279
|
def _read_text(path: Path) -> str:
|
|
222
280
|
try:
|
|
223
281
|
return path.read_text(encoding="utf-8")
|
|
@@ -262,7 +320,7 @@ def cluster(manifest_path: Path) -> dict:
|
|
|
262
320
|
def digest(manifest_path: Path) -> str:
|
|
263
321
|
root = harness_root(manifest_path)
|
|
264
322
|
parts = []
|
|
265
|
-
targets = [manifest_path, contracts_file(manifest_path)]
|
|
323
|
+
targets = [manifest_path, contracts_file(manifest_path), deployments_file(manifest_path)]
|
|
266
324
|
targets.extend(sorted(plans_dir(manifest_path).rglob("*.yaml")) if plans_dir(manifest_path).is_dir() else [])
|
|
267
325
|
targets.extend(sorted((root / "principles").glob("*.md")) if (root / "principles").is_dir() else [])
|
|
268
326
|
for path in targets:
|
|
@@ -7,6 +7,8 @@ from pathlib import Path
|
|
|
7
7
|
import rich_click as click
|
|
8
8
|
import yaml
|
|
9
9
|
|
|
10
|
+
from evo_cli.commands.harness._model import Plan, load_plan_file
|
|
11
|
+
|
|
10
12
|
PLAIN_SAFE = re.compile(r"^[A-Za-z][\w./#@-]*$")
|
|
11
13
|
|
|
12
14
|
|
|
@@ -93,3 +95,20 @@ def update_item(path: Path, section: str, index: int, updates: dict) -> dict:
|
|
|
93
95
|
|
|
94
96
|
path.write_text(updated, encoding="utf-8")
|
|
95
97
|
return {"old": old, "new": updates}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def complete_plan(plan: Plan) -> Plan:
|
|
101
|
+
"""Move an active plan into the completed area without rewriting its YAML."""
|
|
102
|
+
if plan.area == "completed":
|
|
103
|
+
return plan
|
|
104
|
+
if plan.area != "active":
|
|
105
|
+
raise click.ClickException(f"Plan {plan.id!r} is in an unsupported area: {plan.area!r}.")
|
|
106
|
+
|
|
107
|
+
destination_dir = plan.path.parent.parent / "completed"
|
|
108
|
+
destination = destination_dir / plan.path.name
|
|
109
|
+
if destination.exists():
|
|
110
|
+
raise click.ClickException(f"Cannot complete {plan.id!r}: {destination} already exists.")
|
|
111
|
+
|
|
112
|
+
destination_dir.mkdir(parents=True, exist_ok=True)
|
|
113
|
+
plan.path.rename(destination)
|
|
114
|
+
return load_plan_file(destination)
|
|
@@ -11,7 +11,15 @@ import rich_click as click
|
|
|
11
11
|
|
|
12
12
|
from evo_cli.commands.harness._dag import plan_graphs, seam_graph
|
|
13
13
|
from evo_cli.commands.harness._git import overlay
|
|
14
|
-
from evo_cli.commands.harness._model import
|
|
14
|
+
from evo_cli.commands.harness._model import (
|
|
15
|
+
cluster,
|
|
16
|
+
digest,
|
|
17
|
+
find_plan,
|
|
18
|
+
load_deployments,
|
|
19
|
+
load_plans,
|
|
20
|
+
load_seams,
|
|
21
|
+
)
|
|
22
|
+
from evo_cli.commands.harness._mutate import complete_plan
|
|
15
23
|
|
|
16
24
|
WEB_DIR = Path(__file__).resolve().parent / "web"
|
|
17
25
|
INDEX = WEB_DIR / "index.html"
|
|
@@ -68,6 +76,20 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
68
76
|
except Exception as exc: # a dashboard must not die because one plan has broken YAML
|
|
69
77
|
self._json({"error": str(exc)}, status=500)
|
|
70
78
|
|
|
79
|
+
def do_POST(self):
|
|
80
|
+
route = unquote(urlparse(self.path).path)
|
|
81
|
+
try:
|
|
82
|
+
if self.headers.get("X-Evo-Harness-Write") != "1":
|
|
83
|
+
self._json({"error": "missing harness write header"}, status=403)
|
|
84
|
+
elif route.startswith("/api/"):
|
|
85
|
+
self._write_api(route)
|
|
86
|
+
else:
|
|
87
|
+
self._json({"error": "not found"}, status=404)
|
|
88
|
+
except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError):
|
|
89
|
+
pass
|
|
90
|
+
except Exception as exc:
|
|
91
|
+
self._json({"error": str(exc)}, status=500)
|
|
92
|
+
|
|
71
93
|
def _send(self, body: bytes, content_type: str, status: int = 200, headers=None):
|
|
72
94
|
self.send_response(status)
|
|
73
95
|
self.send_header("Content-Type", content_type)
|
|
@@ -106,6 +128,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
106
128
|
"cluster": cluster(manifest),
|
|
107
129
|
"seams": load_seams(manifest),
|
|
108
130
|
"seamGraph": seam_graph(manifest),
|
|
131
|
+
"deployments": load_deployments(manifest),
|
|
109
132
|
"plans": [p.summary() for p in load_plans(manifest)],
|
|
110
133
|
}
|
|
111
134
|
)
|
|
@@ -127,6 +150,19 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
127
150
|
return
|
|
128
151
|
self._json({"error": f"no route {route}"}, status=404)
|
|
129
152
|
|
|
153
|
+
def _write_api(self, route: str):
|
|
154
|
+
parts = route.strip("/").split("/")
|
|
155
|
+
if len(parts) == 4 and parts[:2] == ["api", "plans"] and parts[3] == "complete":
|
|
156
|
+
try:
|
|
157
|
+
plan = find_plan(self.manifest_path, parts[2])
|
|
158
|
+
completed = complete_plan(plan)
|
|
159
|
+
except click.ClickException as exc:
|
|
160
|
+
self._json({"error": exc.format_message()}, status=409)
|
|
161
|
+
return
|
|
162
|
+
self._json({"plan": completed.detail(), "graphs": plan_graphs(completed)})
|
|
163
|
+
return
|
|
164
|
+
self._json({"error": f"no route {route}"}, status=404)
|
|
165
|
+
|
|
130
166
|
def _stream(self):
|
|
131
167
|
self.send_response(200)
|
|
132
168
|
self.send_header("Content-Type", "text/event-stream")
|
|
@@ -17,8 +17,8 @@ from evo_cli.console import console
|
|
|
17
17
|
@click.option("--host", default="127.0.0.1", show_default=True)
|
|
18
18
|
@click.option("--open/--no-open", "open_browser", default=True, help="Open a browser once the server is up.")
|
|
19
19
|
def serve(harness_path, port, host, open_browser):
|
|
20
|
-
"""
|
|
21
|
-
|
|
20
|
+
"""Inspect the harness and complete plans from the UI. Edit item statuses with
|
|
21
|
+
`evo harness step|debt|question|repo`; the dashboard follows changes within seconds."""
|
|
22
22
|
manifest_path = find_manifest(harness_path)
|
|
23
23
|
if not bundle_ready():
|
|
24
24
|
raise click.ClickException(MISSING_BUNDLE)
|
|
@@ -32,7 +32,7 @@ def serve(harness_path, port, host, open_browser):
|
|
|
32
32
|
console.print(f"[accent]{info['name']}[/] harness -> [bold]{url}[/]")
|
|
33
33
|
console.print(
|
|
34
34
|
f"[dim]{len(info['repos'])} repos, {len(seams)} seams, {len(plans)} plans from {info['root']}"
|
|
35
|
-
f" |
|
|
35
|
+
f" | plan completion enabled; edit items with `evo harness step ...`[/]"
|
|
36
36
|
)
|
|
37
37
|
console.print("[dim]Ctrl+C to stop.[/]")
|
|
38
38
|
|