speclogician 0.0.0b1__py3-none-any.whl → 0.0.0.dev1__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.
- speclogician/agent/funcs.py +29 -0
- speclogician/cmd/agent_cmd.py +89 -0
- speclogician/cmd/data_cmd.py +24 -0
- speclogician/cmd/model_cmd.py +42 -0
- speclogician/cmd/overlay_cmd.py +30 -0
- speclogician/cmd/scenario_cmd.py +61 -0
- speclogician/cmd/state_cmd.py +52 -0
- speclogician/data/artifact.py +8 -50
- speclogician/data/container.py +18 -384
- speclogician/data/mapping.py +18 -17
- speclogician/data/refs.py +12 -11
- speclogician/data/reports.py +11 -0
- speclogician/data/traces.py +15 -6
- speclogician/llms/llmtools.py +102 -0
- speclogician/llms/overlay.py +264 -0
- speclogician/main.py +36 -102
- speclogician/modeling/__init__.py +0 -31
- speclogician/modeling/component.py +4 -60
- speclogician/modeling/conflict.py +5 -19
- speclogician/modeling/domain.py +93 -280
- speclogician/modeling/model.py +206 -0
- speclogician/modeling/predicates.py +20 -22
- speclogician/modeling/report.py +33 -0
- speclogician/modeling/scenario.py +119 -87
- speclogician/sl_cmd.py +76 -0
- speclogician/state/change.py +98 -378
- speclogician/state/state.py +183 -399
- speclogician/tui/box.tcss +10 -0
- speclogician/tui/tui.py +131 -0
- speclogician/utils/__init__.py +1 -70
- speclogician/utils/imx.py +195 -0
- speclogician/utils/load.py +25 -147
- speclogician/utils/prompt.md +1 -325
- speclogician-0.0.0.dev1.dist-info/METADATA +21 -0
- speclogician-0.0.0.dev1.dist-info/RECORD +43 -0
- speclogician/commands/__init__.py +0 -15
- speclogician/commands/cmd_ch.py +0 -616
- speclogician/commands/cmd_find.py +0 -256
- speclogician/commands/cmd_view.py +0 -202
- speclogician/commands/runner.py +0 -149
- speclogician/commands/utils.py +0 -101
- speclogician/demos/.DS_Store +0 -0
- speclogician/demos/cmd_demo.py +0 -278
- speclogician/demos/loader.py +0 -135
- speclogician/demos/model.py +0 -27
- speclogician/demos/runner.py +0 -51
- speclogician/logic/__init__.py +0 -11
- speclogician/logic/api/__init__.py +0 -29
- speclogician/logic/api/client.py +0 -606
- speclogician/logic/api/decomp.py +0 -67
- speclogician/logic/api/scenario.py +0 -102
- speclogician/logic/api/traces.py +0 -59
- speclogician/logic/lib/__init__.py +0 -19
- speclogician/logic/lib/complement.py +0 -107
- speclogician/logic/lib/domain_model.py +0 -59
- speclogician/logic/lib/predicates.py +0 -151
- speclogician/logic/lib/scenarios.py +0 -369
- speclogician/logic/lib/traces.py +0 -114
- speclogician/logic/lib/transitions.py +0 -104
- speclogician/logic/main.py +0 -246
- speclogician/logic/strings.py +0 -194
- speclogician/logic/utils.py +0 -135
- speclogician/modeling/complement.py +0 -104
- speclogician/modeling/spec.py +0 -306
- speclogician/modeling/spec_stats.py +0 -39
- speclogician/presentation/api.py +0 -244
- speclogician/presentation/builders/_links.py +0 -44
- speclogician/presentation/builders/container.py +0 -53
- speclogician/presentation/builders/data_artifact.py +0 -42
- speclogician/presentation/builders/domain.py +0 -54
- speclogician/presentation/builders/instances_list.py +0 -38
- speclogician/presentation/builders/predicate.py +0 -51
- speclogician/presentation/builders/recommendations.py +0 -41
- speclogician/presentation/builders/scenario.py +0 -41
- speclogician/presentation/builders/scenario_complement.py +0 -82
- speclogician/presentation/builders/smart_find.py +0 -39
- speclogician/presentation/builders/spec.py +0 -39
- speclogician/presentation/builders/state_diff.py +0 -150
- speclogician/presentation/builders/state_instance.py +0 -42
- speclogician/presentation/builders/state_instance_summary.py +0 -84
- speclogician/presentation/builders/trace.py +0 -58
- speclogician/presentation/ctx.py +0 -38
- speclogician/presentation/models/container.py +0 -44
- speclogician/presentation/models/data_artifact.py +0 -33
- speclogician/presentation/models/domain.py +0 -50
- speclogician/presentation/models/instances_list.py +0 -23
- speclogician/presentation/models/predicate.py +0 -60
- speclogician/presentation/models/recommendations.py +0 -34
- speclogician/presentation/models/scenario.py +0 -31
- speclogician/presentation/models/scenario_complement.py +0 -40
- speclogician/presentation/models/smart_find.py +0 -34
- speclogician/presentation/models/spec.py +0 -32
- speclogician/presentation/models/state_diff.py +0 -34
- speclogician/presentation/models/state_instance.py +0 -31
- speclogician/presentation/models/state_instance_summary.py +0 -102
- speclogician/presentation/models/trace.py +0 -42
- speclogician/presentation/preview/__init__.py +0 -13
- speclogician/presentation/preview/cli.py +0 -50
- speclogician/presentation/preview/fixtures/__init__.py +0 -205
- speclogician/presentation/preview/fixtures/artifact_container.py +0 -150
- speclogician/presentation/preview/fixtures/data_artifact.py +0 -144
- speclogician/presentation/preview/fixtures/domain_model.py +0 -162
- speclogician/presentation/preview/fixtures/instances_list.py +0 -162
- speclogician/presentation/preview/fixtures/predicate.py +0 -184
- speclogician/presentation/preview/fixtures/scenario.py +0 -84
- speclogician/presentation/preview/fixtures/scenario_complement.py +0 -81
- speclogician/presentation/preview/fixtures/smart_find.py +0 -140
- speclogician/presentation/preview/fixtures/spec.py +0 -95
- speclogician/presentation/preview/fixtures/state_diff.py +0 -158
- speclogician/presentation/preview/fixtures/state_instance.py +0 -128
- speclogician/presentation/preview/fixtures/state_instance_summary.py +0 -80
- speclogician/presentation/preview/fixtures/trace.py +0 -206
- speclogician/presentation/preview/registry.py +0 -42
- speclogician/presentation/renderers/__init__.py +0 -24
- speclogician/presentation/renderers/container.py +0 -136
- speclogician/presentation/renderers/data_artifact.py +0 -144
- speclogician/presentation/renderers/domain.py +0 -123
- speclogician/presentation/renderers/instances_list.py +0 -120
- speclogician/presentation/renderers/predicate.py +0 -180
- speclogician/presentation/renderers/recommendations.py +0 -90
- speclogician/presentation/renderers/scenario.py +0 -94
- speclogician/presentation/renderers/scenario_complement.py +0 -59
- speclogician/presentation/renderers/smart_find.py +0 -307
- speclogician/presentation/renderers/spec.py +0 -105
- speclogician/presentation/renderers/state_diff.py +0 -102
- speclogician/presentation/renderers/state_instance.py +0 -82
- speclogician/presentation/renderers/state_instance_summary.py +0 -143
- speclogician/presentation/renderers/trace.py +0 -122
- speclogician/shell/app.py +0 -170
- speclogician/shell/shell_ch.py +0 -263
- speclogician/shell/shell_view.py +0 -153
- speclogician/state/change_result.py +0 -32
- speclogician/state/diff.py +0 -191
- speclogician/state/inst.py +0 -574
- speclogician/state/recommendation.py +0 -13
- speclogician/state/recommender.py +0 -577
- speclogician/state/state_stats.py +0 -133
- speclogician/tui/__init__.py +0 -0
- speclogician/tui/app.py +0 -257
- speclogician/tui/app.tcss +0 -160
- speclogician/tui/demo.py +0 -45
- speclogician/tui/images/speclogician-full.png +0 -0
- speclogician/tui/images/speclogician-minimal.png +0 -0
- speclogician/tui/main_screen.py +0 -454
- speclogician/tui/splash_screen.py +0 -51
- speclogician/tui/stats_screen.py +0 -125
- speclogician/utils/testing.py +0 -151
- speclogician-0.0.0b1.dist-info/METADATA +0 -116
- speclogician-0.0.0b1.dist-info/RECORD +0 -139
- /speclogician/{presentation → agent}/__init__.py +0 -0
- /speclogician/{presentation/builders → cmd}/__init__.py +0 -0
- /speclogician/{presentation/models → llms}/__init__.py +0 -0
- {speclogician-0.0.0b1.dist-info → speclogician-0.0.0.dev1.dist-info}/WHEEL +0 -0
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/commands/find_cmd.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
|
-
import typer
|
|
12
|
-
from rich.table import Table
|
|
13
|
-
|
|
14
|
-
from .utils import get_state
|
|
15
|
-
from ..utils import console
|
|
16
|
-
|
|
17
|
-
app = typer.Typer(help="Find various components of the state")
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# -----------------------------------------------------------------------------
|
|
21
|
-
# helpers
|
|
22
|
-
# -----------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
def _matches_component(q: str, *, name: str, src_code: str) -> bool:
|
|
25
|
-
qq = (q or "").lower()
|
|
26
|
-
return (qq in (name or "").lower()) or (qq in (src_code or "").lower())
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _emit_json(payload: dict[str, Any]) -> None:
|
|
30
|
-
# Keep stdout clean for machine mode (matches your convention elsewhere)
|
|
31
|
-
import json as _json
|
|
32
|
-
typer.echo(_json.dumps(payload, indent=2, sort_keys=True))
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def _preview(src: str, n: int = 160) -> str:
|
|
36
|
-
return (src or "")[:n].replace("\n", " ")
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# -----------------------------------------------------------------------------
|
|
40
|
-
# predicates
|
|
41
|
-
# -----------------------------------------------------------------------------
|
|
42
|
-
|
|
43
|
-
@app.command(name="predicates", help="Search state/action predicates by name or source text")
|
|
44
|
-
def search_predicates(
|
|
45
|
-
ctx: typer.Context,
|
|
46
|
-
q: str = typer.Argument(..., help="Search string (name or source snippet)"),
|
|
47
|
-
kind: str = typer.Option(
|
|
48
|
-
"all",
|
|
49
|
-
"--kind",
|
|
50
|
-
help="Search scope: all|state|action",
|
|
51
|
-
),
|
|
52
|
-
json_only: bool = typer.Option(
|
|
53
|
-
False,
|
|
54
|
-
"--json",
|
|
55
|
-
help="Emit JSON only (no rich output)",
|
|
56
|
-
),
|
|
57
|
-
) -> None:
|
|
58
|
-
state = get_state(ctx=ctx)
|
|
59
|
-
dm = state.curr_state().spec.domain_model
|
|
60
|
-
|
|
61
|
-
k = (kind or "all").lower().strip()
|
|
62
|
-
if k not in ("all", "state", "action"):
|
|
63
|
-
raise typer.BadParameter("kind must be one of: all|state|action")
|
|
64
|
-
|
|
65
|
-
matches: list[dict[str, Any]] = []
|
|
66
|
-
|
|
67
|
-
if k in ("all", "state"):
|
|
68
|
-
for p in getattr(dm, "state_preds", []):
|
|
69
|
-
name = getattr(p, "name", "")
|
|
70
|
-
src_code = getattr(p, "src_code", "")
|
|
71
|
-
if _matches_component(q, name=name, src_code=src_code):
|
|
72
|
-
matches.append(
|
|
73
|
-
{
|
|
74
|
-
"type": "StatePredicate",
|
|
75
|
-
"kind": getattr(p, "kind", "state_predicate"),
|
|
76
|
-
"name": name,
|
|
77
|
-
"src_preview": _preview(src_code),
|
|
78
|
-
}
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
if k in ("all", "action"):
|
|
82
|
-
for p in getattr(dm, "action_preds", []):
|
|
83
|
-
name = getattr(p, "name", "")
|
|
84
|
-
src_code = getattr(p, "src_code", "")
|
|
85
|
-
if _matches_component(q, name=name, src_code=src_code):
|
|
86
|
-
matches.append(
|
|
87
|
-
{
|
|
88
|
-
"type": "ActionPredicate",
|
|
89
|
-
"kind": getattr(p, "kind", "action_predicate"),
|
|
90
|
-
"name": name,
|
|
91
|
-
"src_preview": _preview(src_code),
|
|
92
|
-
}
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
# Deterministic output (nice for tests)
|
|
96
|
-
matches.sort(key=lambda m: (m.get("type", ""), m.get("name", "")))
|
|
97
|
-
|
|
98
|
-
if json_only:
|
|
99
|
-
_emit_json(
|
|
100
|
-
{
|
|
101
|
-
"query": q,
|
|
102
|
-
"kind": k,
|
|
103
|
-
"count": len(matches),
|
|
104
|
-
"results": matches,
|
|
105
|
-
}
|
|
106
|
-
)
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
t = Table(title=f"Predicate search for {q!r}", show_header=True, header_style="bold")
|
|
110
|
-
t.add_column("Type", no_wrap=True)
|
|
111
|
-
t.add_column("Name", no_wrap=True)
|
|
112
|
-
t.add_column("Preview")
|
|
113
|
-
for m in matches:
|
|
114
|
-
prev = m["src_preview"]
|
|
115
|
-
t.add_row(m["type"], m["name"], prev + ("…" if len(prev) == 160 else ""))
|
|
116
|
-
console.print(t)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# -----------------------------------------------------------------------------
|
|
120
|
-
# transitions
|
|
121
|
-
# -----------------------------------------------------------------------------
|
|
122
|
-
|
|
123
|
-
@app.command(name="transitions", help="Search transition functions by name or source text")
|
|
124
|
-
def search_transitions(
|
|
125
|
-
ctx: typer.Context,
|
|
126
|
-
q: str = typer.Argument(..., help="Search string (name or source snippet)"),
|
|
127
|
-
json_only: bool = typer.Option(
|
|
128
|
-
False,
|
|
129
|
-
"--json",
|
|
130
|
-
help="Emit JSON only (no rich output)",
|
|
131
|
-
),
|
|
132
|
-
) -> None:
|
|
133
|
-
state = get_state(ctx=ctx)
|
|
134
|
-
dm = state.curr_state().spec.domain_model
|
|
135
|
-
|
|
136
|
-
matches: list[dict[str, Any]] = []
|
|
137
|
-
|
|
138
|
-
for tr in getattr(dm, "transitions", []):
|
|
139
|
-
name = getattr(tr, "name", "")
|
|
140
|
-
src_code = getattr(tr, "src_code", "")
|
|
141
|
-
if _matches_component(q, name=name, src_code=src_code):
|
|
142
|
-
matches.append(
|
|
143
|
-
{
|
|
144
|
-
"type": "Transition",
|
|
145
|
-
"kind": getattr(tr, "kind", "transition"),
|
|
146
|
-
"name": name,
|
|
147
|
-
"src_preview": _preview(src_code),
|
|
148
|
-
}
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
matches.sort(key=lambda m: m.get("name", ""))
|
|
152
|
-
|
|
153
|
-
if json_only:
|
|
154
|
-
_emit_json(
|
|
155
|
-
{
|
|
156
|
-
"query": q,
|
|
157
|
-
"count": len(matches),
|
|
158
|
-
"results": matches,
|
|
159
|
-
}
|
|
160
|
-
)
|
|
161
|
-
return
|
|
162
|
-
|
|
163
|
-
t = Table(title=f"Transition search for {q!r}", show_header=True, header_style="bold")
|
|
164
|
-
t.add_column("Name", no_wrap=True)
|
|
165
|
-
t.add_column("Preview")
|
|
166
|
-
for m in matches:
|
|
167
|
-
prev = m["src_preview"]
|
|
168
|
-
t.add_row(m["name"], prev + ("…" if len(prev) == 160 else ""))
|
|
169
|
-
console.print(t)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
# -----------------------------------------------------------------------------
|
|
173
|
-
# artifacts (smart_find + PM renderer)
|
|
174
|
-
# -----------------------------------------------------------------------------
|
|
175
|
-
|
|
176
|
-
@app.command("artifacts")
|
|
177
|
-
def search_artifacts(
|
|
178
|
-
ctx: typer.Context,
|
|
179
|
-
q: str = typer.Argument(..., help="Search string"),
|
|
180
|
-
kind: str = typer.Option(
|
|
181
|
-
"all",
|
|
182
|
-
"--kind",
|
|
183
|
-
help="Search scope: all|tests|logs|docs|src",
|
|
184
|
-
),
|
|
185
|
-
json_only: bool = typer.Option(
|
|
186
|
-
False,
|
|
187
|
-
"--json",
|
|
188
|
-
help="Emit JSON only (no rich output)",
|
|
189
|
-
),
|
|
190
|
-
ids: bool = typer.Option(
|
|
191
|
-
False,
|
|
192
|
-
"--ids",
|
|
193
|
-
help="Show artifact IDs in rich output",
|
|
194
|
-
),
|
|
195
|
-
compact: bool = typer.Option(
|
|
196
|
-
False,
|
|
197
|
-
"--compact",
|
|
198
|
-
help="Use compact tables/panels in rich output",
|
|
199
|
-
),
|
|
200
|
-
show_empty: bool = typer.Option(
|
|
201
|
-
False,
|
|
202
|
-
"--show-empty",
|
|
203
|
-
help="Render empty sections in rich output",
|
|
204
|
-
),
|
|
205
|
-
max_per_type: int = typer.Option(
|
|
206
|
-
50,
|
|
207
|
-
"--max-per-type",
|
|
208
|
-
min=1,
|
|
209
|
-
help="Maximum results to show per artifact type",
|
|
210
|
-
),
|
|
211
|
-
) -> None:
|
|
212
|
-
"""Smart text search across artifacts (tests/logs/docs/src)."""
|
|
213
|
-
state = get_state(ctx=ctx)
|
|
214
|
-
ac = state.curr_state().art_container
|
|
215
|
-
|
|
216
|
-
k = (kind or "all").lower().strip()
|
|
217
|
-
include: set[str] | None
|
|
218
|
-
if k in ("all", "*"):
|
|
219
|
-
include = None
|
|
220
|
-
elif k in ("tests", "test"):
|
|
221
|
-
include = {"test"}
|
|
222
|
-
elif k in ("logs", "log"):
|
|
223
|
-
include = {"log"}
|
|
224
|
-
elif k in ("docs", "doc"):
|
|
225
|
-
include = {"doc"}
|
|
226
|
-
elif k in ("src", "source"):
|
|
227
|
-
include = {"src"}
|
|
228
|
-
else:
|
|
229
|
-
raise typer.BadParameter("kind must be one of: all|tests|logs|docs|src")
|
|
230
|
-
|
|
231
|
-
# --- search ---
|
|
232
|
-
res = ac.smart_find(q, include=include, max_per_type=max_per_type)
|
|
233
|
-
|
|
234
|
-
# --- build PM + render ---
|
|
235
|
-
from speclogician.presentation.ctx import RenderCtx
|
|
236
|
-
from speclogician.presentation.builders.smart_find import build_smart_find_pm
|
|
237
|
-
from speclogician.presentation.renderers.smart_find import render_smart_find
|
|
238
|
-
|
|
239
|
-
rctx = RenderCtx(
|
|
240
|
-
show_art_ids=ids,
|
|
241
|
-
show_ids=ids,
|
|
242
|
-
compact=compact,
|
|
243
|
-
show_empty_sections=show_empty, # now a real field on RenderCtx
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
pm = build_smart_find_pm(res, rctx)
|
|
247
|
-
|
|
248
|
-
if json_only:
|
|
249
|
-
payload = pm.model_dump()
|
|
250
|
-
# test-friendly extra metadata
|
|
251
|
-
payload["query"] = q
|
|
252
|
-
payload["kind"] = k
|
|
253
|
-
_emit_json(payload)
|
|
254
|
-
return
|
|
255
|
-
|
|
256
|
-
console.print(render_smart_find(pm, ctx=rctx))
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/cli/view_cmd.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
from typing import Optional
|
|
11
|
-
import typer
|
|
12
|
-
|
|
13
|
-
from speclogician.presentation.ctx import RenderCtx
|
|
14
|
-
from speclogician.presentation.api import (
|
|
15
|
-
present_state_instance,
|
|
16
|
-
present_spec,
|
|
17
|
-
present_domain_model,
|
|
18
|
-
present_artifact_container,
|
|
19
|
-
present_state_diff,
|
|
20
|
-
present_instances_list,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
from .utils import get_state
|
|
24
|
-
|
|
25
|
-
app = typer.Typer(help="View components of the current state (rich or JSON).")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def _mk_ctx(
|
|
29
|
-
*,
|
|
30
|
-
compact: bool,
|
|
31
|
-
show_stats_only: bool,
|
|
32
|
-
show_code: bool,
|
|
33
|
-
show_ids: bool,
|
|
34
|
-
show_art_ids: bool,
|
|
35
|
-
) -> RenderCtx:
|
|
36
|
-
return RenderCtx(
|
|
37
|
-
compact=compact,
|
|
38
|
-
show_stats_only=show_stats_only,
|
|
39
|
-
show_code=show_code,
|
|
40
|
-
show_ids=show_ids,
|
|
41
|
-
show_art_ids=show_art_ids,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _emit(obj: object, *, json_only: bool) -> None:
|
|
46
|
-
if json_only:
|
|
47
|
-
typer.echo(json.dumps(obj, indent=2, default=str))
|
|
48
|
-
else:
|
|
49
|
-
# rich renderables can be printed by your console; if you want,
|
|
50
|
-
# import speclogician.utils.console and console.print(obj)
|
|
51
|
-
from speclogician.utils import console
|
|
52
|
-
console.print(obj)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# ---------------------------
|
|
56
|
-
# view state (full)
|
|
57
|
-
# ---------------------------
|
|
58
|
-
|
|
59
|
-
@app.command("state")
|
|
60
|
-
def view_state(
|
|
61
|
-
ctx: typer.Context,
|
|
62
|
-
idx: Optional[int] = typer.Option(None, "--idx", help="State instance index (default: latest/0)"),
|
|
63
|
-
json_only: bool = typer.Option(False, "--json", help="Emit JSON only"),
|
|
64
|
-
compact: bool = typer.Option(False, "--compact"),
|
|
65
|
-
show_stats_only: bool = typer.Option(False, "--stats-only"),
|
|
66
|
-
show_code: bool = typer.Option(True, "--code/--no-code"),
|
|
67
|
-
show_ids: bool = typer.Option(False, "--ids"),
|
|
68
|
-
show_art_ids: bool = typer.Option(False, "--art-ids"),
|
|
69
|
-
) -> None:
|
|
70
|
-
st = get_state(ctx)
|
|
71
|
-
i = 0 if idx is None else idx
|
|
72
|
-
si = st.instances[i]
|
|
73
|
-
|
|
74
|
-
rctx = _mk_ctx(
|
|
75
|
-
compact=compact,
|
|
76
|
-
show_stats_only=show_stats_only,
|
|
77
|
-
show_code=show_code,
|
|
78
|
-
show_ids=show_ids,
|
|
79
|
-
show_art_ids=show_art_ids,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
out = present_state_instance(si, ctx=rctx, json_only=json_only)
|
|
83
|
-
_emit(out, json_only=json_only)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
# ---------------------------
|
|
87
|
-
# view spec
|
|
88
|
-
# ---------------------------
|
|
89
|
-
|
|
90
|
-
@app.command("spec")
|
|
91
|
-
def view_spec_cmd(
|
|
92
|
-
ctx: typer.Context,
|
|
93
|
-
idx: Optional[int] = typer.Option(None, "--idx"),
|
|
94
|
-
json_only: bool = typer.Option(False, "--json"),
|
|
95
|
-
compact: bool = typer.Option(False, "--compact"),
|
|
96
|
-
show_stats_only: bool = typer.Option(False, "--stats-only"),
|
|
97
|
-
show_code: bool = typer.Option(True, "--code/--no-code"),
|
|
98
|
-
show_ids: bool = typer.Option(False, "--ids"),
|
|
99
|
-
show_art_ids: bool = typer.Option(False, "--art-ids"),
|
|
100
|
-
) -> None:
|
|
101
|
-
st = get_state(ctx)
|
|
102
|
-
i = 0 if idx is None else idx
|
|
103
|
-
si = st.instances[i]
|
|
104
|
-
|
|
105
|
-
rctx = _mk_ctx(compact=compact, show_stats_only=show_stats_only, show_code=show_code, show_ids=show_ids, show_art_ids=show_art_ids)
|
|
106
|
-
out = present_spec(si.spec, ctx=rctx, json_only=json_only)
|
|
107
|
-
_emit(out, json_only=json_only)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# ---------------------------
|
|
111
|
-
# view domain model
|
|
112
|
-
# ---------------------------
|
|
113
|
-
|
|
114
|
-
@app.command("domain")
|
|
115
|
-
def view_domain_cmd(
|
|
116
|
-
ctx: typer.Context,
|
|
117
|
-
idx: Optional[int] = typer.Option(None, "--idx"),
|
|
118
|
-
json_only: bool = typer.Option(False, "--json"),
|
|
119
|
-
compact: bool = typer.Option(False, "--compact"),
|
|
120
|
-
show_stats_only: bool = typer.Option(False, "--stats-only"),
|
|
121
|
-
show_code: bool = typer.Option(True, "--code/--no-code"),
|
|
122
|
-
show_ids: bool = typer.Option(False, "--ids"),
|
|
123
|
-
show_art_ids: bool = typer.Option(False, "--art-ids"),
|
|
124
|
-
) -> None:
|
|
125
|
-
st = get_state(ctx)
|
|
126
|
-
i = 0 if idx is None else idx
|
|
127
|
-
si = st.instances[i]
|
|
128
|
-
|
|
129
|
-
rctx = _mk_ctx(compact=compact, show_stats_only=show_stats_only, show_code=show_code, show_ids=show_ids, show_art_ids=show_art_ids)
|
|
130
|
-
out = present_domain_model(si.spec.domain_model, ctx=rctx, json_only=json_only)
|
|
131
|
-
_emit(out, json_only=json_only)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# ---------------------------
|
|
135
|
-
# view artifacts container
|
|
136
|
-
# ---------------------------
|
|
137
|
-
|
|
138
|
-
@app.command("artifacts")
|
|
139
|
-
def view_artifacts_cmd(
|
|
140
|
-
ctx: typer.Context,
|
|
141
|
-
idx: Optional[int] = typer.Option(None, "--idx"),
|
|
142
|
-
json_only: bool = typer.Option(False, "--json"),
|
|
143
|
-
compact: bool = typer.Option(False, "--compact"),
|
|
144
|
-
show_stats_only: bool = typer.Option(False, "--stats-only"),
|
|
145
|
-
show_code: bool = typer.Option(True, "--code/--no-code"),
|
|
146
|
-
show_ids: bool = typer.Option(False, "--ids"),
|
|
147
|
-
show_art_ids: bool = typer.Option(False, "--art-ids"),
|
|
148
|
-
) -> None:
|
|
149
|
-
st = get_state(ctx)
|
|
150
|
-
i = 0 if idx is None else idx
|
|
151
|
-
si = st.instances[i]
|
|
152
|
-
|
|
153
|
-
rctx = _mk_ctx(compact=compact, show_stats_only=show_stats_only, show_code=show_code, show_ids=show_ids, show_art_ids=show_art_ids)
|
|
154
|
-
out = present_artifact_container(si.art_container, ctx=rctx, json_only=json_only)
|
|
155
|
-
_emit(out, json_only=json_only)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
# ---------------------------
|
|
159
|
-
# view diff (latest instance must have it computed/stored, or compute outside)
|
|
160
|
-
# ---------------------------
|
|
161
|
-
|
|
162
|
-
@app.command("diff")
|
|
163
|
-
def view_diff_cmd(
|
|
164
|
-
ctx: typer.Context,
|
|
165
|
-
idx: Optional[int] = typer.Option(None, "--idx"),
|
|
166
|
-
json_only: bool = typer.Option(False, "--json"),
|
|
167
|
-
compact: bool = typer.Option(False, "--compact"),
|
|
168
|
-
show_stats_only: bool = typer.Option(False, "--stats-only"),
|
|
169
|
-
show_code: bool = typer.Option(True, "--code/--no-code"),
|
|
170
|
-
show_ids: bool = typer.Option(False, "--ids"),
|
|
171
|
-
show_art_ids: bool = typer.Option(False, "--art-ids"),
|
|
172
|
-
) -> None:
|
|
173
|
-
st = get_state(ctx)
|
|
174
|
-
i = 0 if idx is None else idx
|
|
175
|
-
si = st.instances[i]
|
|
176
|
-
|
|
177
|
-
if si.state_diff is None:
|
|
178
|
-
raise typer.BadParameter("No state_diff available on this instance.")
|
|
179
|
-
|
|
180
|
-
rctx = _mk_ctx(compact=compact, show_stats_only=show_stats_only, show_code=show_code, show_ids=show_ids, show_art_ids=show_art_ids)
|
|
181
|
-
out = present_state_diff(si.state_diff, ctx=rctx, json_only=json_only)
|
|
182
|
-
_emit(out, json_only=json_only)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
# ---------------------------
|
|
186
|
-
# view list of instances
|
|
187
|
-
# ---------------------------
|
|
188
|
-
|
|
189
|
-
@app.command("instances")
|
|
190
|
-
def view_instances_cmd(
|
|
191
|
-
ctx: typer.Context,
|
|
192
|
-
json_only: bool = typer.Option(False, "--json"),
|
|
193
|
-
compact: bool = typer.Option(False, "--compact"),
|
|
194
|
-
show_stats_only: bool = typer.Option(False, "--stats-only"),
|
|
195
|
-
show_code: bool = typer.Option(True, "--code/--no-code"),
|
|
196
|
-
show_ids: bool = typer.Option(False, "--ids"),
|
|
197
|
-
show_art_ids: bool = typer.Option(False, "--art-ids"),
|
|
198
|
-
) -> None:
|
|
199
|
-
st = get_state(ctx)
|
|
200
|
-
rctx = _mk_ctx(compact=compact, show_stats_only=show_stats_only, show_code=show_code, show_ids=show_ids, show_art_ids=show_art_ids)
|
|
201
|
-
out = present_instances_list(st.instances, ctx=rctx, json_only=json_only)
|
|
202
|
-
_emit(out, json_only=json_only)
|
speclogician/commands/runner.py
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/commands/runner.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import typer
|
|
11
|
-
from enum import Enum
|
|
12
|
-
from datetime import datetime
|
|
13
|
-
from typing import NoReturn, Any
|
|
14
|
-
from rich.console import Console
|
|
15
|
-
|
|
16
|
-
from speclogician.state.change import StateChange
|
|
17
|
-
from speclogician.state.state import State # adjust import
|
|
18
|
-
from speclogician.state.change_result import (
|
|
19
|
-
ProcessChangeResult,
|
|
20
|
-
ProcessChangeError,
|
|
21
|
-
ProcessChangeSuccess,
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
from speclogician.presentation.ctx import RenderCtx
|
|
25
|
-
from speclogician.presentation.builders.state_instance import build_state_instance_pm
|
|
26
|
-
from speclogician.presentation.builders.state_diff import build_state_diff_pm
|
|
27
|
-
|
|
28
|
-
from speclogician.presentation.renderers.state_instance import render_state_instance
|
|
29
|
-
from speclogician.presentation.renderers.state_diff import render_state_diff # assuming you have it
|
|
30
|
-
|
|
31
|
-
console = Console()
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def _dump_json(obj: Any) -> str:
|
|
35
|
-
def _default(o: Any):
|
|
36
|
-
if hasattr(o, "model_dump"):
|
|
37
|
-
return o.model_dump(mode="json")
|
|
38
|
-
if isinstance(o, Enum):
|
|
39
|
-
return o.value
|
|
40
|
-
if isinstance(o, datetime):
|
|
41
|
-
return o.isoformat()
|
|
42
|
-
raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
|
|
43
|
-
|
|
44
|
-
return json.dumps(
|
|
45
|
-
obj,
|
|
46
|
-
default=_default,
|
|
47
|
-
indent=2,
|
|
48
|
-
sort_keys=True,
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
def run_change_and_render(
|
|
52
|
-
st: State,
|
|
53
|
-
change: StateChange,
|
|
54
|
-
*,
|
|
55
|
-
json_only: bool,
|
|
56
|
-
) -> NoReturn:
|
|
57
|
-
"""
|
|
58
|
-
Unified runner for CLI commands.
|
|
59
|
-
|
|
60
|
-
- json_only=True: emits JSON PM payload to stdout (stable, parseable).
|
|
61
|
-
- json_only=False: pretty-prints PMs via Rich renderers.
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
# Always take the object path so we can drive presentation from PMs.
|
|
65
|
-
result_obj: ProcessChangeResult = st.process_change(change, json_only=json_only)
|
|
66
|
-
st.save()
|
|
67
|
-
|
|
68
|
-
# -------------------------
|
|
69
|
-
# Error branch
|
|
70
|
-
# -------------------------
|
|
71
|
-
if not result_obj["ok"]:
|
|
72
|
-
err_res: ProcessChangeError = result_obj
|
|
73
|
-
|
|
74
|
-
# Build ctx from the old state if possible
|
|
75
|
-
old_state = err_res["old_state"]
|
|
76
|
-
ctx = RenderCtx(
|
|
77
|
-
art_cont=old_state.art_container,
|
|
78
|
-
art_map=old_state.art_map,
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
old_pm = build_state_instance_pm(old_state, ctx=ctx)
|
|
82
|
-
|
|
83
|
-
if json:
|
|
84
|
-
payload = {
|
|
85
|
-
"ok": False,
|
|
86
|
-
"stage": err_res["stage"],
|
|
87
|
-
"change": err_res["change"].model_dump(mode="json"),
|
|
88
|
-
"error": {
|
|
89
|
-
"type": type(err_res["error"]).__name__,
|
|
90
|
-
"message": str(err_res["error"]),
|
|
91
|
-
},
|
|
92
|
-
"old_state": old_pm.model_dump(mode="json"),
|
|
93
|
-
}
|
|
94
|
-
typer.echo(_dump_json(payload))
|
|
95
|
-
raise typer.Exit(code=1)
|
|
96
|
-
|
|
97
|
-
# human output
|
|
98
|
-
console.print(f"[bold red]Error[/bold red] stage={err_res['stage']}")
|
|
99
|
-
console.print(f"[red]{type(err_res['error']).__name__}: {err_res['error']}[/red]\n")
|
|
100
|
-
console.print("[bold]Old state[/bold]")
|
|
101
|
-
console.print(render_state_instance(old_pm, ctx=ctx))
|
|
102
|
-
raise typer.Exit(code=1)
|
|
103
|
-
|
|
104
|
-
# -------------------------
|
|
105
|
-
# Success branch
|
|
106
|
-
# -------------------------
|
|
107
|
-
ok_res: ProcessChangeSuccess = result_obj
|
|
108
|
-
|
|
109
|
-
old_state = ok_res["old_state"]
|
|
110
|
-
new_state = ok_res["new_state"]
|
|
111
|
-
sd = ok_res["state_diff"]
|
|
112
|
-
|
|
113
|
-
# Prefer new-state context for presentation (has freshest art_map/art_cont)
|
|
114
|
-
ctx = RenderCtx(
|
|
115
|
-
art_cont=new_state.art_container,
|
|
116
|
-
art_map=new_state.art_map,
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
old_pm = build_state_instance_pm(old_state, ctx=ctx)
|
|
120
|
-
new_pm = build_state_instance_pm(new_state, ctx=ctx)
|
|
121
|
-
diff_pm = build_state_diff_pm(sd, ctx=ctx)
|
|
122
|
-
|
|
123
|
-
if json:
|
|
124
|
-
payload = {
|
|
125
|
-
"ok": True,
|
|
126
|
-
"stage": "ok",
|
|
127
|
-
"change": ok_res["change"].model_dump(mode="json"),
|
|
128
|
-
"old_state": old_pm.model_dump(mode="json"),
|
|
129
|
-
"new_state": new_pm.model_dump(mode="json"),
|
|
130
|
-
"state_diff": diff_pm.model_dump(mode="json"),
|
|
131
|
-
"new_num_instances": ok_res["new_num_instances"],
|
|
132
|
-
}
|
|
133
|
-
typer.echo(_dump_json(payload))
|
|
134
|
-
raise typer.Exit(code=0)
|
|
135
|
-
|
|
136
|
-
# human output
|
|
137
|
-
console.print("[bold]Old state[/bold]")
|
|
138
|
-
console.print(render_state_instance(old_pm, ctx=ctx))
|
|
139
|
-
console.print()
|
|
140
|
-
|
|
141
|
-
console.print("[bold]New state[/bold]")
|
|
142
|
-
console.print(render_state_instance(new_pm, ctx=ctx))
|
|
143
|
-
console.print()
|
|
144
|
-
|
|
145
|
-
console.print("[bold]Diff[/bold]")
|
|
146
|
-
console.print(render_state_diff(diff_pm, ctx=ctx))
|
|
147
|
-
console.print()
|
|
148
|
-
|
|
149
|
-
raise typer.Exit(code=0)
|
speclogician/commands/utils.py
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/commands/utils.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
from collections.abc import Mapping, Sequence
|
|
9
|
-
|
|
10
|
-
import json
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
from typing import TypeVar
|
|
13
|
-
|
|
14
|
-
import typer
|
|
15
|
-
from ..utils.load import load_state
|
|
16
|
-
from ..state.change import StateChange
|
|
17
|
-
from ..state.state import State
|
|
18
|
-
|
|
19
|
-
from ..modeling.scenario import ScenarioDelta
|
|
20
|
-
|
|
21
|
-
T = TypeVar("T", bound=StateChange)
|
|
22
|
-
|
|
23
|
-
def get_state(ctx: typer.Context) -> State:
|
|
24
|
-
state = ctx.obj.state
|
|
25
|
-
if state is None:
|
|
26
|
-
state = load_state()
|
|
27
|
-
return state
|
|
28
|
-
|
|
29
|
-
def normalize_string_escapes(v: str) -> str:
|
|
30
|
-
"""
|
|
31
|
-
Convert escaped sequences like '\\n', '\\t' into actual characters.
|
|
32
|
-
"""
|
|
33
|
-
try:
|
|
34
|
-
return v.encode("utf-8").decode("unicode_escape")
|
|
35
|
-
except Exception:
|
|
36
|
-
return v
|
|
37
|
-
|
|
38
|
-
def normalize_data(obj):
|
|
39
|
-
if isinstance(obj, str):
|
|
40
|
-
return normalize_string_escapes(obj)
|
|
41
|
-
|
|
42
|
-
if isinstance(obj, Mapping):
|
|
43
|
-
return {k: normalize_data(v) for k, v in obj.items()}
|
|
44
|
-
|
|
45
|
-
if isinstance(obj, Sequence) and not isinstance(obj, (str, bytes)):
|
|
46
|
-
return [normalize_data(v) for v in obj]
|
|
47
|
-
|
|
48
|
-
return obj
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def parse_scenario_delta(value: str | None) -> ScenarioDelta | None:
|
|
52
|
-
"""
|
|
53
|
-
Parse a ScenarioDelta from CLI input.
|
|
54
|
-
|
|
55
|
-
Accepted forms:
|
|
56
|
-
1) None / "" -> None
|
|
57
|
-
2) Comma-separated tokens:
|
|
58
|
-
"+p1,+p2,-p3" => add=["p1","p2"], remove=["p3"]
|
|
59
|
-
"p1,p2" => add=["p1","p2"], remove=[]
|
|
60
|
-
"-p3" => add=[], remove=["p3"]
|
|
61
|
-
3) JSON:
|
|
62
|
-
'{"add":["p1"],"remove":["p2"]}'
|
|
63
|
-
or a path to a JSON file containing that object.
|
|
64
|
-
|
|
65
|
-
Validation (dedup, identifier check, overlap) is enforced by ScenarioDelta itself.
|
|
66
|
-
"""
|
|
67
|
-
if value is None:
|
|
68
|
-
return None
|
|
69
|
-
|
|
70
|
-
s = value.strip()
|
|
71
|
-
if not s:
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
# File path to JSON
|
|
75
|
-
p = Path(s)
|
|
76
|
-
if p.exists() and p.is_file():
|
|
77
|
-
data = json.loads(p.read_text())
|
|
78
|
-
delta = ScenarioDelta.model_validate(data)
|
|
79
|
-
return None if delta.is_empty() else delta
|
|
80
|
-
|
|
81
|
-
# Inline JSON
|
|
82
|
-
if s.startswith("{"):
|
|
83
|
-
data = json.loads(s)
|
|
84
|
-
delta = ScenarioDelta.model_validate(data)
|
|
85
|
-
return None if delta.is_empty() else delta
|
|
86
|
-
|
|
87
|
-
# Token format: "+a,+b,-c" or "a,b"
|
|
88
|
-
add: list[str] = []
|
|
89
|
-
remove: list[str] = []
|
|
90
|
-
|
|
91
|
-
for tok in (t.strip() for t in s.split(",") if t.strip()):
|
|
92
|
-
if tok.startswith("+"):
|
|
93
|
-
add.append(tok[1:].strip())
|
|
94
|
-
elif tok.startswith("-"):
|
|
95
|
-
remove.append(tok[1:].strip())
|
|
96
|
-
else:
|
|
97
|
-
# default: treat as add
|
|
98
|
-
add.append(tok)
|
|
99
|
-
|
|
100
|
-
delta = ScenarioDelta(add=add, remove=remove)
|
|
101
|
-
return None if delta.is_empty() else delta
|
speclogician/demos/.DS_Store
DELETED
|
Binary file
|