awmine 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
awmine/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ """awmine -- mine agent transcripts for tool outcomes, lessons, procedures and cost.
2
+
3
+ Aither World Mine: what your own sessions already taught you, extracted as
4
+ rows other tools can learn from, with every string redacted before it lands.
5
+ """
6
+
7
+ __version__ = "0.2.0"
8
+
9
+ __all__ = ["__version__"]
awmine/__main__.py ADDED
@@ -0,0 +1,8 @@
1
+ """``python -m awmine`` entry point."""
2
+
3
+ import sys
4
+
5
+ from .cli import main
6
+
7
+ if __name__ == "__main__":
8
+ sys.exit(main())
awmine/_doctor.py ADDED
@@ -0,0 +1,190 @@
1
+ """Stack-aware `doctor` for awmine.
2
+
3
+ GENERATED BY gen_aw_doctor.py -- DO NOT EDIT.
4
+ Regenerate it with the generator named above; a hand-edit here is reverted by
5
+ the next run and fails the parity gate.
6
+
7
+ Why a doctor exists at all: the aw* bricks are designed to COMPOSE, so the
8
+ interesting failures live BETWEEN them. "awmine is installed" is not the useful
9
+ fact -- "awmine is installed and the thing it pairs with is not" is. This reports
10
+ the whole stack, not just itself.
11
+
12
+ stdlib only, on purpose: a diagnostic that cannot run because a dependency is
13
+ missing is worthless precisely when you need it.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import importlib.util
18
+ import os
19
+ import shutil
20
+ import sys
21
+
22
+ #: Frozen from the Aither World registry at generation time. A shipped
23
+ #: package cannot read the registry, and a doctor that guessed at the family
24
+ #: would go stale in silence. Regenerate to update.
25
+ SELF = 'awmine'
26
+ FAMILY = ['awask', 'awavatar', 'awbac', 'awbrain', 'awbrowse', 'awclassify', 'awdecide', 'awdeck', 'awdelphi', 'awdit', 'awembed', 'awevolve', 'awfind', 'awflow', 'awfocus', 'awgit', 'awgraph', 'awgym', 'awiam', 'awkno', 'awm', 'awmail', 'awnboard', 'awnest', 'awnet', 'awnode', 'awpool', 'awpredict', 'awprism', 'awprove', 'awreason', 'awrecover', 'awrecurse', 'awrelay', 'awrena', 'awrepl', 'awreport', 'awresearch', 'awrise', 'awrouter', 'awrtifact', 'awrun', 'awscreen', 'awseal', 'awsettings', 'awshare', 'awsprite', 'awstorage', 'awswarm', 'awtax', 'awtoll', 'awtunnel', 'awvision', 'awvoice', 'awwall', 'gawbbonet']
27
+ PAIRS_WITH = ['awdecide', 'awm', 'awrise', 'awtoll']
28
+
29
+ #: This brick's OWN config, read out of its source at generation time.
30
+ #: ENV_REQUIRED is `os.environ["X"]` -- absent, that is a KeyError the moment
31
+ #: the line runs. ENV_OPTIONAL is `os.getenv("X")`, which returns None and lets
32
+ #: the caller cope. Only this brick's namespace is listed: reporting the
33
+ #: platform-wide vars it also touches would be noise, and a doctor that floods
34
+ #: gets ignored.
35
+ ENV_REQUIRED = []
36
+ ENV_OPTIONAL = ['AWMINE_CODEX_BACKLOG', 'AWMINE_DENY', 'AWMINE_FLUSH_ROWS', 'AWMINE_OUT', 'AWMINE_PUBLISH_GATE', 'AWMINE_ROOTS', 'AWMINE_SELFTEST_BREAK', 'AWMINE_SHARE', 'AWMINE_SHARE_REPO', 'AWMINE_SUSPECTS_FATAL']
37
+
38
+
39
+ def _installed(mod: str) -> "str | None":
40
+ """Version if importable, else None. Never raises -- a broken sibling must
41
+ not take the diagnostic down with it."""
42
+ try:
43
+ if importlib.util.find_spec(mod) is None:
44
+ return None
45
+ except (ImportError, ValueError):
46
+ return None
47
+ try:
48
+ from importlib.metadata import PackageNotFoundError, version
49
+ try:
50
+ return version(mod)
51
+ except PackageNotFoundError:
52
+ return "installed"
53
+ except Exception:
54
+ return "installed"
55
+
56
+
57
+ def report(out=None) -> int:
58
+ """Print the stack picture. 0 = this brick and its pairs are present."""
59
+ out = out or sys.stdout
60
+ print(f"{SELF} doctor", file=out)
61
+
62
+ mine = _installed(SELF)
63
+ print(f" self {SELF} {mine or 'NOT IMPORTABLE'}", file=out)
64
+ shim = shutil.which(SELF)
65
+ print(f" command {shim or 'not on PATH'}", file=out)
66
+
67
+ # The stack. Siblings this brick pairs with are called out separately,
68
+ # because a missing pair is a REASON, while a missing unrelated brick is
69
+ # just a fact about your machine.
70
+ missing_pairs, present = [], []
71
+ for name in FAMILY:
72
+ v = _installed(name)
73
+ if v:
74
+ present.append(name)
75
+ elif name in PAIRS_WITH:
76
+ missing_pairs.append(name)
77
+ print(f" stack {len(present)}/{len(FAMILY)} aw* packages installed",
78
+ file=out)
79
+ if present:
80
+ print(f" {' '.join(sorted(present))}", file=out)
81
+
82
+ missing_req = [v for v in ENV_REQUIRED if not os.environ.get(v)]
83
+ if ENV_REQUIRED or ENV_OPTIONAL:
84
+ have = sum(1 for v in ENV_REQUIRED + ENV_OPTIONAL if os.environ.get(v))
85
+ total = len(ENV_REQUIRED) + len(ENV_OPTIONAL)
86
+ print(f" config {have}/{total} of this brick's own vars set", file=out)
87
+ if missing_req:
88
+ # Not a preference. os.environ[...] raises the moment it runs.
89
+ print(f" MISSING REQUIRED: {' '.join(missing_req)}", file=out)
90
+
91
+ local = _local_checks()
92
+ for line in local:
93
+ print(f" {line}", file=out)
94
+ problems, unjudged = _local_verdict()
95
+
96
+ if mine is None:
97
+ print(f"\nverdict: {SELF} itself is not importable. Reinstall it before "
98
+ f"anything else here means much.", file=out)
99
+ return 1
100
+ if missing_req:
101
+ print(f"\nverdict: {SELF} is missing required config "
102
+ f"({', '.join(missing_req)}). Those are read with os.environ[...], "
103
+ f"so the code path that needs them raises rather than degrades.",
104
+ file=out)
105
+ return 1
106
+ # A measured NO outranks a missing optional pair: awrise printed
107
+ # "nothing wakes run-due" and still exited 0 because the missing-pairs
108
+ # branch returned first (measured 2026-09-20).
109
+ if problems:
110
+ for p in problems:
111
+ print(f"\nverdict: {p}", file=out)
112
+ return 1
113
+ if unjudged:
114
+ for u in unjudged:
115
+ print(f"\nverdict: UNJUDGED -- could not judge: {u}", file=out)
116
+ return 2
117
+ if missing_pairs:
118
+ print(f"\nverdict: {SELF} works, but pairs with "
119
+ f"{', '.join(sorted(missing_pairs))} which "
120
+ f"{'is' if len(missing_pairs) == 1 else 'are'} not installed. "
121
+ f"That is a capability you are missing, not an error.", file=out)
122
+ return 0
123
+ print(f"\nverdict: {SELF} and everything it pairs with are present.", file=out)
124
+ return 0
125
+
126
+
127
+ def _local_verdict() -> tuple:
128
+ """The brick's OWN verdict, folded into the exit code.
129
+
130
+ Without this the local lines are DECORATION. Measured 2026-09-20: awrise
131
+ printed "hostclock NOT INSTALLED -- nothing wakes run-due" and "last tick
132
+ never", then exited 0 -- teaching an operator that a doctor exit code
133
+ carries no information. A brick opts in with `_doctor_local_verdict()`
134
+ returning (problems, unjudged); a brick without one is unaffected.
135
+ """
136
+ try:
137
+ mod = importlib.import_module(f"{SELF}.doctor_local")
138
+ problems, unjudged = mod._doctor_local_verdict()
139
+ except Exception: # noqa: BLE001
140
+ return [], []
141
+ return list(problems or []), list(unjudged or [])
142
+
143
+
144
+ def _local_checks() -> "list[str]":
145
+ """Per-brick checks, if this package defines them.
146
+
147
+ Kept as a HOOK rather than generated guesses: the generator knows the family
148
+ from the registry, but it does not know what awmine needs at runtime, and a
149
+ doctor that invented config requirements would be confidently wrong. A
150
+ package supplies `_doctor_local()` returning display lines; absent, the
151
+ stack picture above still stands on its own.
152
+ """
153
+ try:
154
+ mod = importlib.import_module(f"{SELF}.doctor_local")
155
+ except Exception:
156
+ return []
157
+ try:
158
+ return list(mod._doctor_local())
159
+ except Exception as exc: # noqa: BLE001
160
+ return [f"local checks raised {type(exc).__name__}: {exc}"]
161
+
162
+
163
+ def main(argv: "list[str] | None" = None) -> int:
164
+ # --self-test delegates to a SIBLING module when one exists.
165
+ #
166
+ # This file is generated and a fresh run replaces it, so a self-test
167
+ # written HERE is deleted by the next regeneration. awdelphi learned that
168
+ # the expensive way: 125 lines exercising four real failure paths --
169
+ # convergence, roster anonymization, resume, gateway-down -- lived in this
170
+ # file and were destroyed by a routine regeneration, silently, leaving a
171
+ # --self-test flag that reported PASS while asserting nothing.
172
+ #
173
+ # So the seam is a separate module the generator never writes. A package
174
+ # with real machinery to prove puts it in _selftest.py; everything else
175
+ # keeps the honest answer below rather than a self-test that only ever
176
+ # passes.
177
+ argv = list(argv if argv is not None else __import__("sys").argv[1:])
178
+ if "--self-test" in argv:
179
+ try:
180
+ from . import _selftest as _st
181
+ except Exception:
182
+ print("no _selftest module: this doctor reports the stack, and has",
183
+ "no machinery of its own to prove")
184
+ return 0
185
+ return int(_st.run())
186
+ return report()
187
+
188
+
189
+ if __name__ == "__main__":
190
+ raise SystemExit(main())