chaincompiler-si 0.3.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.
- chaincompiler_si-0.3.0.dist-info/METADATA +12 -0
- chaincompiler_si-0.3.0.dist-info/RECORD +12 -0
- chaincompiler_si-0.3.0.dist-info/WHEEL +5 -0
- chaincompiler_si-0.3.0.dist-info/entry_points.txt +2 -0
- chaincompiler_si-0.3.0.dist-info/top_level.txt +1 -0
- si/__init__.py +20 -0
- si/interp.py +58 -0
- si/mcp_tree.py +57 -0
- si/runtime.py +62 -0
- si/scaffold.py +71 -0
- si/server.py +77 -0
- si/skill/SKILL.md +28 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chaincompiler-si
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Self-Interpreter for the ChainCompiler chain dialect — an MCP that is also a skill
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: chaincompiler==0.3.0
|
|
7
|
+
Requires-Dist: chainaios==0.3.0
|
|
8
|
+
Requires-Dist: chaincompiler-accc==0.3.0
|
|
9
|
+
Requires-Dist: corcc==0.3.0
|
|
10
|
+
Requires-Dist: sccc==0.3.0
|
|
11
|
+
Requires-Dist: agent-skilltree>=0.4.0
|
|
12
|
+
Requires-Dist: fastmcp
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
si/__init__.py,sha256=LiAsjD1XqFcNXB9Xb6pRZIhMpUorjskBl0fHytJ7Ado,795
|
|
2
|
+
si/interp.py,sha256=FGCTluGe_zEBaXUhwm_DEA2kp6HVT9aHkiJedHPf7UQ,2460
|
|
3
|
+
si/mcp_tree.py,sha256=YQIm7SYEbgmrDEM9t_RCQe32mxqlYJ1mCkQKbJSlBBA,2081
|
|
4
|
+
si/runtime.py,sha256=2ZfRVBCZqGa_ySb77DE8qPTGDjtFcjKhQQBUwZGCEY4,2514
|
|
5
|
+
si/scaffold.py,sha256=TlwSCtB0GovfdpoVy8B5fCTqkN1faWOikT7vs8KWenQ,3077
|
|
6
|
+
si/server.py,sha256=uu5STYqrxdixvpvMs3JbOFl9BW7uXQ6TTNNj_aMXmMI,2925
|
|
7
|
+
si/skill/SKILL.md,sha256=ZVVcQ4T5g5pP84_lnhIG-34zxRbwqSC0ltSC_Fk6zJ0,1115
|
|
8
|
+
chaincompiler_si-0.3.0.dist-info/METADATA,sha256=gAh6iaQT2d3E12MO4BHc36bew92EILd4q8dj00Fvcwg,403
|
|
9
|
+
chaincompiler_si-0.3.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
10
|
+
chaincompiler_si-0.3.0.dist-info/entry_points.txt,sha256=xVUAifQ0n2exgnBr_RRWfcoM-4vReFS6FqztxRYNy_M,45
|
|
11
|
+
chaincompiler_si-0.3.0.dist-info/top_level.txt,sha256=Gefnx6gufmA_0rBNGHzBXTHNRIj_pTEQKMF9CbSP1mw,3
|
|
12
|
+
chaincompiler_si-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
si
|
si/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""si — the Self-Interpreter for the ChainCompiler chain dialect.
|
|
2
|
+
|
|
3
|
+
A Python dialect: the interpreter defers to native Python and only injects the
|
|
4
|
+
ChainCompiler vocabulary. Ships as an MCP server (si.server) that is ALSO a skill
|
|
5
|
+
(si/skill/SKILL.md), and can turn skill trees / exchange masters into MCPs.
|
|
6
|
+
The execute arm of Compilero Bandito; the self-hosting capstone.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
__version__ = "0.3.0"
|
|
11
|
+
|
|
12
|
+
from .interp import interpret, make_env
|
|
13
|
+
from .mcp_tree import build_tree_server, tree_to_mcp
|
|
14
|
+
from .runtime import reachable, read_skill, walk
|
|
15
|
+
from .scaffold import scaffold_repo, validate_node
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"interpret", "make_env", "walk", "reachable", "read_skill",
|
|
19
|
+
"build_tree_server", "tree_to_mcp", "scaffold_repo", "validate_node", "__version__",
|
|
20
|
+
]
|
si/interp.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""The Self-Interpreter for the chain dialect.
|
|
2
|
+
|
|
3
|
+
The dialect is **a Python dialect** — so the interpreter does NOT re-wrap Python's
|
|
4
|
+
native ops (arithmetic, control flow, data). It runs the program with Python, and
|
|
5
|
+
only *injects the ChainCompiler vocabulary* as builtins: forge/gate/package the
|
|
6
|
+
chains, build/walk skill trees, build exchanges, turn trees into MCPs. A program
|
|
7
|
+
sets `result` (or the namespace is returned) — that's the value.
|
|
8
|
+
|
|
9
|
+
Self-interpreter: programs can themselves forge languages, build trees, and emit
|
|
10
|
+
MCPs — including ones that interpret further. ChainCompiler thereby runs itself.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def make_env(extra: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
18
|
+
"""The dialect's vocabulary — ChainCompiler primitives, injected as names."""
|
|
19
|
+
import accc
|
|
20
|
+
import chaincompiler
|
|
21
|
+
import chainaios
|
|
22
|
+
import corcc
|
|
23
|
+
import sccc
|
|
24
|
+
import skilltree
|
|
25
|
+
|
|
26
|
+
from . import runtime
|
|
27
|
+
from .mcp_tree import tree_to_mcp
|
|
28
|
+
|
|
29
|
+
env: dict[str, Any] = {
|
|
30
|
+
# construct (the AIOS top-door now lives in chainaios; write_skill stays in the CC base)
|
|
31
|
+
"construct_language": chainaios.construct_language,
|
|
32
|
+
"write_skill": chaincompiler.write_skill,
|
|
33
|
+
"forge_ac": accc.forge, "package_ac": accc.package, "gate_ac": accc.gate,
|
|
34
|
+
"forge_cor": corcc.forge_persona, "package_cor": corcc.package, "lint_cor": corcc.lint,
|
|
35
|
+
"forge_sc": sccc.forge, "package_sc": sccc.package, "lint_sc": sccc.lint,
|
|
36
|
+
# organize
|
|
37
|
+
"SkillTree": skilltree.SkillTree, "TreeNode": skilltree.TreeNode,
|
|
38
|
+
"materialize": skilltree.materialize, "validate_tree": skilltree.validate,
|
|
39
|
+
"build_exchange": skilltree.build_exchange, "load_exchange": skilltree.load_exchange,
|
|
40
|
+
# execute / search / emit
|
|
41
|
+
"walk": runtime.walk, "reachable": runtime.reachable, "read_skill": runtime.read_skill,
|
|
42
|
+
"tree_to_mcp": tree_to_mcp,
|
|
43
|
+
"result": None,
|
|
44
|
+
}
|
|
45
|
+
if extra:
|
|
46
|
+
env.update(extra)
|
|
47
|
+
return env
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def interpret(source: str, *, env: dict[str, Any] | None = None) -> Any:
|
|
51
|
+
"""Interpret a chain-dialect program. Returns `result` (or the eval of a single expr)."""
|
|
52
|
+
ns = make_env(env)
|
|
53
|
+
try:
|
|
54
|
+
code = compile(source, "<si>", "eval") # single expression?
|
|
55
|
+
return eval(code, ns)
|
|
56
|
+
except SyntaxError:
|
|
57
|
+
exec(compile(source, "<si>", "exec"), ns) # full program
|
|
58
|
+
return ns.get("result")
|
si/mcp_tree.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Turn a skill tree (or exchange master) into an MCP server.
|
|
2
|
+
|
|
3
|
+
`build_tree_server` makes a FastMCP server that exposes a materialized tree's
|
|
4
|
+
skills as tools (list / read / walk). `tree_to_mcp` writes a tiny launcher so any
|
|
5
|
+
tree or master becomes a runnable MCP — that's "make trees and masters into MCPs".
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from . import runtime
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_tree_server(root_dir: str | Path, root_name: str, *, name: str | None = None):
|
|
15
|
+
"""A FastMCP server exposing the tree's skills. (Run with .run() for stdio.)"""
|
|
16
|
+
from fastmcp import FastMCP
|
|
17
|
+
|
|
18
|
+
root_dir = str(root_dir)
|
|
19
|
+
server = FastMCP(name or f"skilltree:{root_name}")
|
|
20
|
+
|
|
21
|
+
@server.tool()
|
|
22
|
+
def list_skills() -> list[str]:
|
|
23
|
+
"""List every skill reachable from this tree's root."""
|
|
24
|
+
return runtime.reachable(root_dir, root_name)
|
|
25
|
+
|
|
26
|
+
@server.tool()
|
|
27
|
+
def read_skill(skill: str) -> str:
|
|
28
|
+
"""Return the body of a skill in this tree by name."""
|
|
29
|
+
body = runtime.read_skill(root_dir, skill)
|
|
30
|
+
return body if body is not None else f"(no skill named {skill!r} in this tree)"
|
|
31
|
+
|
|
32
|
+
@server.tool()
|
|
33
|
+
def walk() -> dict:
|
|
34
|
+
"""Return the full tree, traversed by following its breadcrumbs."""
|
|
35
|
+
return runtime.walk(root_dir, root_name)
|
|
36
|
+
|
|
37
|
+
return server
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_LAUNCHER = '''#!/usr/bin/env python3
|
|
41
|
+
"""Auto-generated MCP launcher for skill tree {name!r} (root {root!r})."""
|
|
42
|
+
from si.mcp_tree import build_tree_server
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
build_tree_server({root_dir!r}, {root!r}, name={name!r}).run()
|
|
46
|
+
'''
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def tree_to_mcp(root_dir: str | Path, root_name: str, out_path: str | Path,
|
|
50
|
+
*, name: str | None = None) -> Path:
|
|
51
|
+
"""Write a runnable MCP launcher that serves this tree/master. Returns its path."""
|
|
52
|
+
out = Path(out_path)
|
|
53
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
out.write_text(_LAUNCHER.format(root_dir=str(Path(root_dir).resolve()),
|
|
55
|
+
root=root_name, name=name or f"skilltree:{root_name}"),
|
|
56
|
+
encoding="utf-8")
|
|
57
|
+
return out
|
si/runtime.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""The execute arm — walk a SkillTree by following its Read-breadcrumbs.
|
|
2
|
+
|
|
3
|
+
This is what "running" a tree means: start at the root SKILL.md, parse the
|
|
4
|
+
breadcrumb paths, and recurse. No new mechanism — the same breadcrumbs the
|
|
5
|
+
validator checks are the ones the runtime follows. (The breadcrumb verb is the
|
|
6
|
+
Read tool, not `cat`; the path is what we follow.)
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
_CRUMB = re.compile(r"`([^`]+/SKILL\.md)`") # the backticked SKILL.md path, verb-agnostic
|
|
14
|
+
_COORD = re.compile(r"^\d+(?:\.\d+)*-") # a coordinate-addressed folder: `0.1-<name>`
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _skill_md(node_dir: Path, name: str) -> Path:
|
|
18
|
+
return Path(node_dir) / ".claude" / "skills" / name / "SKILL.md"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _strip_front(text: str) -> str:
|
|
22
|
+
return text.split("---", 2)[-1].strip() if text.lstrip().startswith("---") else text.strip()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _walk_md(md: Path) -> dict:
|
|
26
|
+
node = {"name": md.parent.name, "skill": str(md), "children": []}
|
|
27
|
+
if not md.is_file():
|
|
28
|
+
node["error"] = "missing"
|
|
29
|
+
return node
|
|
30
|
+
node_dir = md.parents[3] # <node>/.claude/skills/<name>/SKILL.md → <node>
|
|
31
|
+
for p in _CRUMB.findall(md.read_text(encoding="utf-8")):
|
|
32
|
+
child = Path(p)
|
|
33
|
+
if not child.is_absolute(): # a portable crumb is relative to the node's folder
|
|
34
|
+
child = node_dir / child # (SkillTree ≥0.4.0); a localized one is absolute
|
|
35
|
+
node["children"].append(_walk_md(child))
|
|
36
|
+
return node
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def walk(root_dir: str | Path, root_name: str) -> dict:
|
|
40
|
+
"""Traverse the tree from its root by following breadcrumbs. Returns a nested dict."""
|
|
41
|
+
return _walk_md(_skill_md(Path(root_dir), root_name))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def reachable(root_dir: str | Path, root_name: str) -> list[str]:
|
|
45
|
+
"""Flat list of every skill name reachable from the root (the execute frontier)."""
|
|
46
|
+
out: list[str] = []
|
|
47
|
+
def rec(n):
|
|
48
|
+
out.append(n["name"])
|
|
49
|
+
for c in n["children"]:
|
|
50
|
+
rec(c)
|
|
51
|
+
rec(walk(root_dir, root_name))
|
|
52
|
+
return out
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def read_skill(root_dir: str | Path, name: str) -> str | None:
|
|
56
|
+
"""Search the tree for a skill by name; return its body (the search arm). A coordinate-addressed
|
|
57
|
+
tree names each skill's folder `<coord>-<name>`; the name matches with or without the coordinate."""
|
|
58
|
+
for md in Path(root_dir).rglob("SKILL.md"):
|
|
59
|
+
folder = md.parent.name
|
|
60
|
+
if folder == name or _COORD.sub("", folder) == name:
|
|
61
|
+
return _strip_front(md.read_text(encoding="utf-8"))
|
|
62
|
+
return None
|
si/scaffold.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""P5.1 — scaffold a federated node repo from a skill tree.
|
|
2
|
+
|
|
3
|
+
When the SI emits an MCP, it can also stamp a ChainCompiler-SHAPED node repo: the
|
|
4
|
+
skill tree (breadcrumbs), a marketplace registry that **federates under a
|
|
5
|
+
parent**, a runnable MCP launcher, and a README. The fractal made real — each
|
|
6
|
+
emitted MCP becomes a repo like ours, its marketplace joining under ours.
|
|
7
|
+
|
|
8
|
+
Full CI mechanics (deploy workflow, site generator) are P5.2; this stamps the
|
|
9
|
+
load-bearing shape: tree + registry + MCP + README, all validatable.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from skilltree import SkillTree, Violation, materialize
|
|
17
|
+
from skilltree import validate as validate_tree
|
|
18
|
+
|
|
19
|
+
from .mcp_tree import tree_to_mcp
|
|
20
|
+
|
|
21
|
+
_NODE_README = """# {name} — a ChainCompiler node
|
|
22
|
+
|
|
23
|
+
A self-contained skill tree, served as an MCP, federated under `{parent}`.
|
|
24
|
+
|
|
25
|
+
- **Walk it** — start at `.claude/skills/{root}/SKILL.md` and Read each breadcrumb to descend.
|
|
26
|
+
- **Serve it** — `python serve_mcp.py` (an MCP exposing this tree's skills).
|
|
27
|
+
- **Marketplace** — see `registry.json`; this node registers under its parent.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def scaffold_repo(name: str, tree: SkillTree | str | Path, out_dir: str | Path,
|
|
32
|
+
*, parent: str | None = None) -> Path:
|
|
33
|
+
"""Stamp a federated node repo at <out_dir>/<name>. Returns the repo path."""
|
|
34
|
+
if isinstance(tree, (str, Path)):
|
|
35
|
+
tree = SkillTree.load(tree)
|
|
36
|
+
repo = Path(out_dir) / name
|
|
37
|
+
repo.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
|
|
39
|
+
materialize(tree, repo) # the node's skill tree (breadcrumbs)
|
|
40
|
+
root = tree.root.name
|
|
41
|
+
|
|
42
|
+
(repo / "registry.json").write_text(json.dumps({ # marketplace, federated
|
|
43
|
+
"name": name,
|
|
44
|
+
"parent": parent, # null at the root; else a repo ref
|
|
45
|
+
"entries": [{"name": root, "kind": "tree", "manifest": "skilltree.json",
|
|
46
|
+
"trust": "unverified"}],
|
|
47
|
+
}, indent=2), encoding="utf-8")
|
|
48
|
+
|
|
49
|
+
tree_to_mcp(repo, root, repo / "serve_mcp.py", name=f"node:{name}") # the runnable MCP
|
|
50
|
+
(repo / "README.md").write_text(
|
|
51
|
+
_NODE_README.format(name=name, parent=parent or "(root)", root=root), encoding="utf-8")
|
|
52
|
+
return repo
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def validate_node(repo: str | Path) -> list[Violation]:
|
|
56
|
+
"""Validate a scaffolded node: the tree + the registry + the MCP launcher."""
|
|
57
|
+
repo = Path(repo)
|
|
58
|
+
out = list(validate_tree(repo))
|
|
59
|
+
reg = repo / "registry.json"
|
|
60
|
+
if not reg.is_file():
|
|
61
|
+
out.append(Violation("error", repo.name, "missing registry.json"))
|
|
62
|
+
else:
|
|
63
|
+
try:
|
|
64
|
+
data = json.loads(reg.read_text(encoding="utf-8"))
|
|
65
|
+
if "parent" not in data or "entries" not in data:
|
|
66
|
+
out.append(Violation("error", repo.name, "registry.json missing parent/entries"))
|
|
67
|
+
except json.JSONDecodeError:
|
|
68
|
+
out.append(Violation("error", repo.name, "registry.json is not valid JSON"))
|
|
69
|
+
if not (repo / "serve_mcp.py").is_file():
|
|
70
|
+
out.append(Violation("error", repo.name, "missing serve_mcp.py launcher"))
|
|
71
|
+
return out
|
si/server.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""The Self-Interpreter as an MCP server (and it is also a skill — see si/skill/).
|
|
2
|
+
|
|
3
|
+
Exposes the chain dialect to any MCP client:
|
|
4
|
+
- si_interpret(source) run a chain-dialect program
|
|
5
|
+
- si_walk(root_dir, root_name) traverse a materialized tree
|
|
6
|
+
- si_reachable(root_dir, root_name) list reachable skills
|
|
7
|
+
- si_read_skill(root_dir, skill) fetch a skill body by name
|
|
8
|
+
- si_tree_to_mcp(root_dir, name, out) turn a tree/master into its own MCP
|
|
9
|
+
- si_search(root_dir, query) BM25 over a tree, optionally coordinate-scoped
|
|
10
|
+
|
|
11
|
+
Run: python -m si.server (or the `si-server` console script)
|
|
12
|
+
|
|
13
|
+
The tool functions stay plain callables (registered, not rebound) so they're
|
|
14
|
+
usable directly and in tests, not only over the wire.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
|
|
20
|
+
from fastmcp import FastMCP
|
|
21
|
+
|
|
22
|
+
from . import runtime
|
|
23
|
+
from .interp import interpret
|
|
24
|
+
from .mcp_tree import tree_to_mcp
|
|
25
|
+
|
|
26
|
+
mcp = FastMCP("self-interpreter")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def si_interpret(source: str) -> str:
|
|
30
|
+
"""Interpret a ChainCompiler chain-dialect program; returns repr(result)."""
|
|
31
|
+
return repr(interpret(source))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def si_walk(root_dir: str, root_name: str) -> dict:
|
|
35
|
+
"""Traverse a materialized skill tree by following its breadcrumbs."""
|
|
36
|
+
return runtime.walk(root_dir, root_name)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def si_reachable(root_dir: str, root_name: str) -> list[str]:
|
|
40
|
+
"""List every skill reachable from a tree's root."""
|
|
41
|
+
return runtime.reachable(root_dir, root_name)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def si_read_skill(root_dir: str, skill: str) -> str:
|
|
45
|
+
"""Search a tree for a skill by name and return its body."""
|
|
46
|
+
body = runtime.read_skill(root_dir, skill)
|
|
47
|
+
return body if body is not None else f"(no skill named {skill!r})"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def si_tree_to_mcp(root_dir: str, root_name: str, out_path: str) -> str:
|
|
51
|
+
"""Turn a skill tree (or exchange master) into a runnable MCP launcher."""
|
|
52
|
+
return str(tree_to_mcp(root_dir, root_name, out_path))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def si_search(root_dir: str, query: str, scope_coord: str | None = None, limit: int = 10) -> list[dict]:
|
|
56
|
+
"""Search the skill tree (BM25), optionally scoped to a coordinate subtree (e.g. '0.1'). SkillTree's search hands a
|
|
57
|
+
query's words to FTS5 unquoted, so it raises on a query with no word and on an operator word (`NOT`, `AND`); this
|
|
58
|
+
door sends it the query's words alone, lowercased — FTS5's operators are capitals and its index folds case, so the
|
|
59
|
+
words match as before — and answers a query with no word with nothing."""
|
|
60
|
+
words = re.findall(r"\w+", query.lower())
|
|
61
|
+
if not words:
|
|
62
|
+
return []
|
|
63
|
+
from skilltree import search_tree
|
|
64
|
+
return search_tree(root_dir, " ".join(words), scope_coord=scope_coord, limit=limit)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# Register as MCP tools without rebinding the names (keep them plain callables).
|
|
68
|
+
for _fn in (si_interpret, si_walk, si_reachable, si_read_skill, si_tree_to_mcp, si_search):
|
|
69
|
+
mcp.tool()(_fn)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def main() -> None:
|
|
73
|
+
mcp.run()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
main()
|
si/skill/SKILL.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: self-interpreter
|
|
3
|
+
description: Run ChainCompiler chain-dialect programs and turn skill trees into MCPs. Use to forge languages, build/walk skill trees, or execute a cognition chain.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Self-Interpreter (SI)
|
|
7
|
+
|
|
8
|
+
The SI runs the **chain dialect** — a Python dialect whose vocabulary is the
|
|
9
|
+
ChainCompiler primitives (`forge_ac`, `forge_cor`, `forge_sc`, `construct_language`,
|
|
10
|
+
`SkillTree`, `TreeNode`, `materialize`, `walk`, `build_exchange`, `tree_to_mcp`, …).
|
|
11
|
+
It defers to native Python for everything else.
|
|
12
|
+
|
|
13
|
+
## As an MCP
|
|
14
|
+
|
|
15
|
+
Start it: `python -m si.server`. Tools:
|
|
16
|
+
|
|
17
|
+
- `si_interpret(source)` — run a chain-dialect program; returns `repr(result)`.
|
|
18
|
+
- `si_walk(root_dir, root_name)` / `si_reachable(...)` — traverse a skill tree.
|
|
19
|
+
- `si_read_skill(root_dir, skill)` — fetch a skill body by name.
|
|
20
|
+
- `si_tree_to_mcp(root_dir, root_name, out)` — turn a tree/master into its own MCP.
|
|
21
|
+
|
|
22
|
+
## Example program
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
tree = SkillTree(TreeNode("root", "sc", children=[TreeNode("a", "ac"), TreeNode("b", "cor")]))
|
|
26
|
+
materialize(tree, OUT)
|
|
27
|
+
result = reachable(OUT, "root") # the execute arm: walk the tree
|
|
28
|
+
```
|