faultsguards 0.1.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.
- faultsguards/__init__.py +4 -0
- faultsguards/__main__.py +3 -0
- faultsguards/cli.py +34 -0
- faultsguards/core.py +34 -0
- faultsguards/data/faults.json +603 -0
- faultsguards/data/guards.json +552 -0
- faultsguards/mcp_server.py +30 -0
- faultsguards/prompts.py +47 -0
- faultsguards-0.1.0.dist-info/METADATA +81 -0
- faultsguards-0.1.0.dist-info/RECORD +14 -0
- faultsguards-0.1.0.dist-info/WHEEL +5 -0
- faultsguards-0.1.0.dist-info/entry_points.txt +2 -0
- faultsguards-0.1.0.dist-info/licenses/LICENSE.txt +8 -0
- faultsguards-0.1.0.dist-info/top_level.txt +1 -0
faultsguards/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"""faultsguards: the Faults and Guards of Heuremen LLC as data, prompts and an MCP server. https://heuremen.org/faults.pdf https://heuremen.org/guards.pdf"""
|
|
2
|
+
__version__ = '0.1.0'
|
|
3
|
+
from .core import load, faults, guards, get_fault, get_guard, families, search, source_info
|
|
4
|
+
from .prompts import checklist, preflight, system_prompt, review_prompt, tool_schemas, call_tool, OUTCOMES
|
faultsguards/__main__.py
ADDED
faultsguards/cli.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""python -m faultsguards COMMAND. faults, guards, show, search, prompt, preflight, review, tools, mcp."""
|
|
2
|
+
import sys, json, argparse
|
|
3
|
+
from . import __version__
|
|
4
|
+
from .core import faults, guards, get_fault, get_guard, search, families
|
|
5
|
+
from .prompts import system_prompt, preflight, review_prompt, tool_schemas
|
|
6
|
+
def main(argv=None):
|
|
7
|
+
ap = argparse.ArgumentParser(prog='faultsguards', description='Faults and Guards, Heuremen LLC. https://heuremen.org/faults.pdf')
|
|
8
|
+
ap.add_argument('--version', action='version', version=__version__); sub = ap.add_subparsers(dest='cmd')
|
|
9
|
+
sub.add_parser('faults', help='list the failure modes'); sub.add_parser('guards', help='list the procedures')
|
|
10
|
+
s = sub.add_parser('show', help='print one entry in full'); s.add_argument('which', choices=['fault', 'guard']); s.add_argument('number', type=int)
|
|
11
|
+
s = sub.add_parser('search', help='search by words'); s.add_argument('query', nargs='+')
|
|
12
|
+
s = sub.add_parser('prompt', help='a system prompt listing the guards'); s.add_argument('--full', action='store_true')
|
|
13
|
+
s = sub.add_parser('preflight', help='the guards that run before the work'); s.add_argument('--task'); s.add_argument('--full', action='store_true')
|
|
14
|
+
s = sub.add_parser('review', help='a prompt reviewing a text file against the faults'); s.add_argument('file'); s.add_argument('--numbers', type=int, nargs='*')
|
|
15
|
+
sub.add_parser('tools', help='function-calling schemas as JSON'); sub.add_parser('mcp', help='run the MCP server over stdio')
|
|
16
|
+
a = ap.parse_args(argv)
|
|
17
|
+
if a.cmd == 'faults':
|
|
18
|
+
for f in families('faults'):
|
|
19
|
+
print(f['part']); [print(' %d. %s' % (e['number'], e['title'])) for e in faults() if e['number'] in f['numbers']]
|
|
20
|
+
elif a.cmd == 'guards':
|
|
21
|
+
for f in families('guards'):
|
|
22
|
+
print(f['part']); [print(' %d. %s' % (e['number'], e['title'])) for e in guards() if e['number'] in f['numbers']]
|
|
23
|
+
elif a.cmd == 'show': e = (get_fault if a.which == 'fault' else get_guard)(a.number); print('%d. %s\n%s\n\n%s' % (e['number'], e['title'], e['part'], e['text']))
|
|
24
|
+
elif a.cmd == 'search':
|
|
25
|
+
for h in search(' '.join(a.query)): print('%s %d. %s' % (h['document'], h['number'], h['title']))
|
|
26
|
+
elif a.cmd == 'prompt': print(system_prompt(a.full))
|
|
27
|
+
elif a.cmd == 'preflight': print(preflight(a.task, a.full))
|
|
28
|
+
elif a.cmd == 'review': print(review_prompt(open(a.file, encoding='utf-8').read(), a.numbers))
|
|
29
|
+
elif a.cmd == 'tools': print(json.dumps(tool_schemas(), indent=1))
|
|
30
|
+
elif a.cmd == 'mcp':
|
|
31
|
+
from .mcp_server import serve; serve()
|
|
32
|
+
else: ap.print_help()
|
|
33
|
+
return 0
|
|
34
|
+
if __name__ == '__main__': sys.exit(main())
|
faultsguards/core.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Data access for the Faults and Guards. The data is generated from the published sources (tools/extract.py) and never edited by hand."""
|
|
2
|
+
import json, os, re
|
|
3
|
+
_D = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
|
|
4
|
+
_cache = {}
|
|
5
|
+
def load(which):
|
|
6
|
+
"""which is 'faults' or 'guards'. Returns the whole document: intro, parts, entries, sections."""
|
|
7
|
+
if which not in ('faults', 'guards'): raise ValueError("which must be 'faults' or 'guards'")
|
|
8
|
+
if which not in _cache: _cache[which] = json.load(open(os.path.join(_D, which + '.json'), encoding='utf-8'))
|
|
9
|
+
return _cache[which]
|
|
10
|
+
def faults(): return load('faults')['entries']
|
|
11
|
+
def guards(): return load('guards')['entries']
|
|
12
|
+
def _get(entries, n):
|
|
13
|
+
for e in entries:
|
|
14
|
+
if e['number'] == int(n): return e
|
|
15
|
+
raise KeyError(n)
|
|
16
|
+
def get_fault(n): return _get(faults(), n)
|
|
17
|
+
def get_guard(n): return _get(guards(), n)
|
|
18
|
+
def families(which='faults'):
|
|
19
|
+
"""The parts of the document in order, each with its entry numbers."""
|
|
20
|
+
out = []
|
|
21
|
+
for e in load(which)['entries']:
|
|
22
|
+
if not out or out[-1]['part'] != e['part']: out.append(dict(part=e['part'], numbers=[]))
|
|
23
|
+
out[-1]['numbers'].append(e['number'])
|
|
24
|
+
return out
|
|
25
|
+
def search(query, which=None):
|
|
26
|
+
"""Entries whose title or text contain every word of the query, case-insensitive. which: 'faults', 'guards' or None for both."""
|
|
27
|
+
words = [w for w in re.findall(r'\w+', query.lower()) if w]; hits = []
|
|
28
|
+
for w in ((which,) if which else ('faults', 'guards')):
|
|
29
|
+
for e in load(w)['entries']:
|
|
30
|
+
hay = (e['title'] + ' ' + e['text']).lower()
|
|
31
|
+
if all(x in hay for x in words): hits.append(dict(document=w, number=e['number'], title=e['title'], part=e['part']))
|
|
32
|
+
return hits
|
|
33
|
+
def source_info():
|
|
34
|
+
return dict(faults='https://heuremen.org/faults.pdf', guards='https://heuremen.org/guards.pdf', publisher='Heuremen LLC')
|