langstage-cli 0.6.4__tar.gz → 0.6.5__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.
- {langstage_cli-0.6.4/langstage_cli.egg-info → langstage_cli-0.6.5}/PKG-INFO +16 -3
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/README.md +13 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/langstage_cli/cli.py +32 -5
- {langstage_cli-0.6.4 → langstage_cli-0.6.5/langstage_cli.egg-info}/PKG-INFO +16 -3
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/langstage_cli.egg-info/SOURCES.txt +2 -1
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/langstage_cli.egg-info/requires.txt +2 -2
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/pyproject.toml +4 -3
- langstage_cli-0.6.5/tests/test_verify_flag.py +45 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/LICENSE +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/deepagent_code/__init__.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/langstage_cli/__init__.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/langstage_cli/agui_stream.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/langstage_cli/config.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/langstage_cli.egg-info/dependency_links.txt +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/langstage_cli.egg-info/entry_points.txt +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/langstage_cli.egg-info/top_level.txt +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/setup.cfg +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_agui_stream.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_checkpointer.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_cli.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_cli_help.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_codeconfig.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_config.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_help_render.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_no_interactive_approve.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_quiet_output.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_rename_shim.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_show_config.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_spec_resolution.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_stream_mode.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_streaming_marker.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_turn_exit_and_render.py +0 -0
- {langstage_cli-0.6.4 → langstage_cli-0.6.5}/tests/test_unicode_console.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langstage-cli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: The terminal stage for your LangGraph agent — Claude Code-style CLI for any CompiledGraph
|
|
5
5
|
Author-email: Kedar Dabhadkar <kdabhadk@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,7 +19,7 @@ Requires-Python: >=3.11
|
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
21
|
Requires-Dist: langgraph>=0.2.0
|
|
22
|
-
Requires-Dist: langstage-core[agui]>=1.0.
|
|
22
|
+
Requires-Dist: langstage-core[agui]>=1.0.6
|
|
23
23
|
Requires-Dist: click>=8.0.0
|
|
24
24
|
Requires-Dist: python-dotenv
|
|
25
25
|
Provides-Extra: dev
|
|
@@ -32,7 +32,7 @@ Requires-Dist: deepagents>=0.3; extra == "dev"
|
|
|
32
32
|
Requires-Dist: ag-ui-langgraph>=0.0.41; extra == "dev"
|
|
33
33
|
Requires-Dist: fastapi; extra == "dev"
|
|
34
34
|
Provides-Extra: agui
|
|
35
|
-
Requires-Dist: langstage-core[agui]>=1.0.
|
|
35
|
+
Requires-Dist: langstage-core[agui]>=1.0.6; extra == "agui"
|
|
36
36
|
Dynamic: license-file
|
|
37
37
|
|
|
38
38
|
# langstage-cli
|
|
@@ -204,9 +204,22 @@ Options:
|
|
|
204
204
|
--demo Run with the built-in keyless demo agent
|
|
205
205
|
--show-config Print the resolved configuration and exit
|
|
206
206
|
-q, --quiet Scriptable single-shot output: only the reply
|
|
207
|
+
--verify Preflight the agent (one real turn); exit 0/1
|
|
207
208
|
--version Show the version and exit
|
|
208
209
|
```
|
|
209
210
|
|
|
211
|
+
### Verifying an agent (CI gate)
|
|
212
|
+
|
|
213
|
+
`--verify` loads the configured agent and runs **one real turn**, exiting `0` if it
|
|
214
|
+
completed cleanly and non-zero otherwise — so you can gate on it in CI before
|
|
215
|
+
trusting an agent. It catches a missing key, a broken tool, or a non-runnable graph
|
|
216
|
+
that a static "it imports" check would wave through (it runs the same
|
|
217
|
+
`langstage-core` preflight every LangStage surface uses):
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
langstage-cli --verify -a my_agent.py:graph || { echo "agent broken" >&2; exit 1; }
|
|
221
|
+
```
|
|
222
|
+
|
|
210
223
|
### Scriptable output
|
|
211
224
|
|
|
212
225
|
A single-shot run (a `MESSAGE` argument or `-f/--file`) prints only the agent's
|
|
@@ -167,9 +167,22 @@ Options:
|
|
|
167
167
|
--demo Run with the built-in keyless demo agent
|
|
168
168
|
--show-config Print the resolved configuration and exit
|
|
169
169
|
-q, --quiet Scriptable single-shot output: only the reply
|
|
170
|
+
--verify Preflight the agent (one real turn); exit 0/1
|
|
170
171
|
--version Show the version and exit
|
|
171
172
|
```
|
|
172
173
|
|
|
174
|
+
### Verifying an agent (CI gate)
|
|
175
|
+
|
|
176
|
+
`--verify` loads the configured agent and runs **one real turn**, exiting `0` if it
|
|
177
|
+
completed cleanly and non-zero otherwise — so you can gate on it in CI before
|
|
178
|
+
trusting an agent. It catches a missing key, a broken tool, or a non-runnable graph
|
|
179
|
+
that a static "it imports" check would wave through (it runs the same
|
|
180
|
+
`langstage-core` preflight every LangStage surface uses):
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
langstage-cli --verify -a my_agent.py:graph || { echo "agent broken" >&2; exit 1; }
|
|
184
|
+
```
|
|
185
|
+
|
|
173
186
|
### Scriptable output
|
|
174
187
|
|
|
175
188
|
A single-shot run (a `MESSAGE` argument or `-f/--file`) prints only the agent's
|
|
@@ -1514,6 +1514,15 @@ def run_conversation_loop(
|
|
|
1514
1514
|
"chatter, timing, and color, and emit only the agent's reply. Auto-enabled "
|
|
1515
1515
|
"when a single-shot run is piped (stdout is not a TTY).",
|
|
1516
1516
|
)
|
|
1517
|
+
@click.option(
|
|
1518
|
+
"--verify",
|
|
1519
|
+
"verify_agent",
|
|
1520
|
+
is_flag=True,
|
|
1521
|
+
default=False,
|
|
1522
|
+
help="Preflight the configured agent: run ONE real turn and exit 0 if it "
|
|
1523
|
+
"completed cleanly, non-zero otherwise. Catches a missing key / broken tool "
|
|
1524
|
+
"/ bad graph before you rely on it (e.g. in CI).",
|
|
1525
|
+
)
|
|
1517
1526
|
def main(
|
|
1518
1527
|
message: Optional[str],
|
|
1519
1528
|
agent_spec: Optional[str],
|
|
@@ -1527,6 +1536,7 @@ def main(
|
|
|
1527
1536
|
agui: bool,
|
|
1528
1537
|
show_config: bool,
|
|
1529
1538
|
quiet: bool,
|
|
1539
|
+
verify_agent: bool,
|
|
1530
1540
|
):
|
|
1531
1541
|
"""
|
|
1532
1542
|
Run a LangGraph agent from the command line.
|
|
@@ -1561,6 +1571,7 @@ def main(
|
|
|
1561
1571
|
langstage-cli -f ./prompt.md
|
|
1562
1572
|
langstage-cli --demo "try it with no API key"
|
|
1563
1573
|
langstage-cli --show-config
|
|
1574
|
+
langstage-cli --verify -a my_agent.py # preflight one real turn; exit 0/1
|
|
1564
1575
|
"""
|
|
1565
1576
|
# Windows consoles default to cp1252, where the spinner (Braille frames) and
|
|
1566
1577
|
# status glyphs (✓ ⏺ —) raise UnicodeEncodeError — the documented
|
|
@@ -1572,16 +1583,17 @@ def main(
|
|
|
1572
1583
|
except (AttributeError, ValueError): # non-reconfigurable stream
|
|
1573
1584
|
pass
|
|
1574
1585
|
|
|
1575
|
-
# Scriptable
|
|
1576
|
-
#
|
|
1577
|
-
# consumer gets only the reply
|
|
1578
|
-
#
|
|
1586
|
+
# Scriptable output (gh #53). A single-shot run (a MESSAGE arg or -f/--file) or
|
|
1587
|
+
# a --verify preflight that is piped — stdout is not a TTY — auto-enables quiet
|
|
1588
|
+
# so the consumer gets only the reply/verdict (no spinner or "Loaded" line);
|
|
1589
|
+
# --quiet forces it in a terminal. Color is additionally stripped whenever stdout
|
|
1590
|
+
# is not a TTY, matching well-behaved CLIs.
|
|
1579
1591
|
try:
|
|
1580
1592
|
_is_tty = sys.stdout.isatty()
|
|
1581
1593
|
except (AttributeError, ValueError):
|
|
1582
1594
|
_is_tty = False
|
|
1583
1595
|
global _QUIET
|
|
1584
|
-
_QUIET = quiet or (bool(message or prompt_file) and not _is_tty)
|
|
1596
|
+
_QUIET = quiet or ((bool(message or prompt_file) or verify_agent) and not _is_tty)
|
|
1585
1597
|
if _QUIET or not _is_tty:
|
|
1586
1598
|
_disable_ansi()
|
|
1587
1599
|
|
|
@@ -1700,6 +1712,21 @@ def main(
|
|
|
1700
1712
|
loading.stop()
|
|
1701
1713
|
print(f"{GREEN}✓{RESET} {DIM}Loaded {final_spec}{RESET}")
|
|
1702
1714
|
|
|
1715
|
+
# --verify: preflight the configured agent by running ONE real turn through
|
|
1716
|
+
# the shared core primitive (langstage-core >= 1.0.6), then exit on its
|
|
1717
|
+
# verdict. A green here means the agent actually completed a turn — not just
|
|
1718
|
+
# that it imported — so `langstage-cli --verify -a my_agent.py` is a real CI
|
|
1719
|
+
# gate. Delegates to core.verify so "healthy" means the same across surfaces.
|
|
1720
|
+
if verify_agent:
|
|
1721
|
+
from langstage_core.agui import verify as _core_verify
|
|
1722
|
+
|
|
1723
|
+
result = _core_verify(graph)
|
|
1724
|
+
if result.ok:
|
|
1725
|
+
print(f"{GREEN}✓{RESET} agent verified: {result.reason}")
|
|
1726
|
+
sys.exit(0)
|
|
1727
|
+
_status(f"{RED}✗ agent verification failed: {result.reason}{RESET}")
|
|
1728
|
+
sys.exit(1)
|
|
1729
|
+
|
|
1703
1730
|
# Seed LangGraph RunnableConfig from TOML [configurable] table if present
|
|
1704
1731
|
config_dict: Dict[str, Any] = {"configurable": {}}
|
|
1705
1732
|
toml_configurable = config_module.get(toml_config, "configurable")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langstage-cli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: The terminal stage for your LangGraph agent — Claude Code-style CLI for any CompiledGraph
|
|
5
5
|
Author-email: Kedar Dabhadkar <kdabhadk@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,7 +19,7 @@ Requires-Python: >=3.11
|
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
21
|
Requires-Dist: langgraph>=0.2.0
|
|
22
|
-
Requires-Dist: langstage-core[agui]>=1.0.
|
|
22
|
+
Requires-Dist: langstage-core[agui]>=1.0.6
|
|
23
23
|
Requires-Dist: click>=8.0.0
|
|
24
24
|
Requires-Dist: python-dotenv
|
|
25
25
|
Provides-Extra: dev
|
|
@@ -32,7 +32,7 @@ Requires-Dist: deepagents>=0.3; extra == "dev"
|
|
|
32
32
|
Requires-Dist: ag-ui-langgraph>=0.0.41; extra == "dev"
|
|
33
33
|
Requires-Dist: fastapi; extra == "dev"
|
|
34
34
|
Provides-Extra: agui
|
|
35
|
-
Requires-Dist: langstage-core[agui]>=1.0.
|
|
35
|
+
Requires-Dist: langstage-core[agui]>=1.0.6; extra == "agui"
|
|
36
36
|
Dynamic: license-file
|
|
37
37
|
|
|
38
38
|
# langstage-cli
|
|
@@ -204,9 +204,22 @@ Options:
|
|
|
204
204
|
--demo Run with the built-in keyless demo agent
|
|
205
205
|
--show-config Print the resolved configuration and exit
|
|
206
206
|
-q, --quiet Scriptable single-shot output: only the reply
|
|
207
|
+
--verify Preflight the agent (one real turn); exit 0/1
|
|
207
208
|
--version Show the version and exit
|
|
208
209
|
```
|
|
209
210
|
|
|
211
|
+
### Verifying an agent (CI gate)
|
|
212
|
+
|
|
213
|
+
`--verify` loads the configured agent and runs **one real turn**, exiting `0` if it
|
|
214
|
+
completed cleanly and non-zero otherwise — so you can gate on it in CI before
|
|
215
|
+
trusting an agent. It catches a missing key, a broken tool, or a non-runnable graph
|
|
216
|
+
that a static "it imports" check would wave through (it runs the same
|
|
217
|
+
`langstage-core` preflight every LangStage surface uses):
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
langstage-cli --verify -a my_agent.py:graph || { echo "agent broken" >&2; exit 1; }
|
|
221
|
+
```
|
|
222
|
+
|
|
210
223
|
### Scriptable output
|
|
211
224
|
|
|
212
225
|
A single-shot run (a `MESSAGE` argument or `-f/--file`) prints only the agent's
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "langstage-cli"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.5"
|
|
8
8
|
description = "The terminal stage for your LangGraph agent — Claude Code-style CLI for any CompiledGraph"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -29,7 +29,8 @@ dependencies = [
|
|
|
29
29
|
# in-process AG-UI adapter — there is no built-in parser fallback — so the
|
|
30
30
|
# AG-UI runtime (ag-ui-langgraph[fastapi] + uvicorn, via core's [agui] extra)
|
|
31
31
|
# is a HARD dep. A bare `pip install langstage-cli` must be able to run a turn.
|
|
32
|
-
|
|
32
|
+
# >=1.0.6 for the shared preflight primitive core.verify() used by --verify.
|
|
33
|
+
"langstage-core[agui]>=1.0.6",
|
|
33
34
|
"click>=8.0.0",
|
|
34
35
|
"python-dotenv",
|
|
35
36
|
]
|
|
@@ -53,7 +54,7 @@ dev = [
|
|
|
53
54
|
]
|
|
54
55
|
# Redundant since AG-UI moved into base deps (core 1.0); kept as a no-op alias so
|
|
55
56
|
# existing `pip install langstage-cli[agui]` scripts/READMEs still resolve.
|
|
56
|
-
agui = ["langstage-core[agui]>=1.0.
|
|
57
|
+
agui = ["langstage-core[agui]>=1.0.6"]
|
|
57
58
|
|
|
58
59
|
[project.scripts]
|
|
59
60
|
langstage-cli = "langstage_cli.cli:main"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""`--verify` preflights the configured agent with a real turn (ADR 0004 adoption).
|
|
2
|
+
|
|
3
|
+
It delegates to `langstage_core.agui.verify`, so a green here means the agent
|
|
4
|
+
actually completed a turn — a real CI gate — not just that it imported. Exit 0 on
|
|
5
|
+
success, non-zero on failure, with the diagnostic on stderr so stdout stays clean.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import textwrap
|
|
9
|
+
|
|
10
|
+
from click.testing import CliRunner
|
|
11
|
+
|
|
12
|
+
from langstage_cli.cli import main
|
|
13
|
+
|
|
14
|
+
_BROKEN_AGENT = textwrap.dedent(
|
|
15
|
+
"""
|
|
16
|
+
from langgraph.graph import StateGraph, START, END, MessagesState
|
|
17
|
+
|
|
18
|
+
def boom(state):
|
|
19
|
+
raise RuntimeError("tool exploded")
|
|
20
|
+
|
|
21
|
+
b = StateGraph(MessagesState)
|
|
22
|
+
b.add_node("boom", boom)
|
|
23
|
+
b.add_edge(START, "boom")
|
|
24
|
+
b.add_edge("boom", END)
|
|
25
|
+
graph = b.compile()
|
|
26
|
+
"""
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_verify_demo_passes_exit_zero(tmp_path, monkeypatch):
|
|
31
|
+
monkeypatch.chdir(tmp_path)
|
|
32
|
+
r = CliRunner().invoke(main, ["--demo", "--verify"])
|
|
33
|
+
assert r.exit_code == 0, r.output
|
|
34
|
+
assert "verified" in r.output # the pass verdict, on stdout
|
|
35
|
+
# A preflight is not a chat: no header/marker leaks.
|
|
36
|
+
assert "⏺" not in r.output and "\x1b[" not in r.output, r.output
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_verify_broken_agent_fails_exit_one(tmp_path, monkeypatch):
|
|
40
|
+
(tmp_path / "broken.py").write_text(_BROKEN_AGENT)
|
|
41
|
+
monkeypatch.chdir(tmp_path)
|
|
42
|
+
r = CliRunner().invoke(main, ["-a", "broken.py:graph", "--verify"])
|
|
43
|
+
assert r.exit_code == 1
|
|
44
|
+
assert r.stdout.strip() == "", r.stdout # stdout stays clean for scripting
|
|
45
|
+
assert "verification failed" in r.stderr, r.stderr
|
|
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
|