sccc 0.3.0__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.
sccc-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: sccc
3
+ Version: 0.3.0
4
+ Summary: Skillchain Compiler-Compiler — chains ACs + CoRs + skills into one SKILL.md
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: chaincompiler-accc==0.3.0
7
+ Requires-Dist: corcc==0.3.0
8
+ Requires-Dist: chaincompiler==0.3.0
9
+ Requires-Dist: rulecatcher==0.3.0
10
+ Requires-Dist: skillchain-compiler==0.3.0
11
+ Requires-Dist: click
@@ -0,0 +1,16 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sccc"
7
+ version = "0.3.0"
8
+ description = "Skillchain Compiler-Compiler — chains ACs + CoRs + skills into one SKILL.md"
9
+ requires-python = ">=3.11"
10
+ dependencies = ["chaincompiler-accc==0.3.0", "corcc==0.3.0", "chaincompiler==0.3.0", "rulecatcher==0.3.0", "skillchain-compiler==0.3.0", "click"]
11
+
12
+ [project.scripts]
13
+ sccc = "sccc.cli:main"
14
+
15
+ [tool.setuptools.packages.find]
16
+ where = ["src"]
sccc-0.3.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,15 @@
1
+ """SCCC — Skillchain Compiler-Compiler. The highest composite: AC + CoR + skills.
2
+
3
+ Same *CC shape (catch → lint → compile → package as SKILL.md). Built on the
4
+ `skillchain` module. ACCC → CORCC → SCCC.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ __version__ = "0.3.0"
9
+
10
+ from .forge import SCLanguage, StepRef, forge, lint, package, parse_sequence, resolve_steps
11
+
12
+ __all__ = [
13
+ "forge", "lint", "package", "parse_sequence", "resolve_steps",
14
+ "SCLanguage", "StepRef", "__version__",
15
+ ]
@@ -0,0 +1,86 @@
1
+ """sccc — chain forged ACs + CoRs + regular skills into one SKILL.md rollup."""
2
+ from __future__ import annotations
3
+
4
+ from pathlib import Path
5
+ import tempfile
6
+
7
+ import click
8
+
9
+ from . import forge, lint, package, resolve_steps
10
+ from .notation import SEED_SEQUENCES
11
+
12
+ _STUB_SKILL = """---
13
+ name: summarize
14
+ description: Condense the prior step's result into a short answer.
15
+ ---
16
+
17
+ Summarize the input into 3 sentences and state the single most important takeaway.
18
+ """
19
+
20
+
21
+ @click.group()
22
+ @click.version_option(message="sccc %(version)s")
23
+ def main() -> None:
24
+ """Skillchain Compiler-Compiler — the highest composite (AC + CoR + skills)."""
25
+
26
+
27
+ @main.command()
28
+ def demo() -> None:
29
+ """Forge AC + CoR + a regular skill, chain them into one SKILL.md rollup."""
30
+ import accc
31
+ import corcc
32
+ from corcc.notation import EINSTEIN
33
+
34
+ with tempfile.TemporaryDirectory() as tmp:
35
+ db = str(Path(tmp) / "cc.db")
36
+ skills_dir = str(Path(tmp) / "skills")
37
+ out_dir = str(Path(tmp) / "dist")
38
+
39
+ click.echo("══ 1. FORGE + PACKAGE the parts (each a SKILL.md) ══")
40
+ accc.forge("debug-attention", ["[Symptom] ⇒ [Repro] ⇒ [Hypothesis] ⇒ |Localize|"], db=db)
41
+ ac_path = accc.package("debug-attention",
42
+ "[Symptom] ⇒ [Repro] ⇒ [Hypothesis] ⇒ |Localize|", out_dir=skills_dir)
43
+ persona = corcc.forge_persona(EINSTEIN, db=db)
44
+ cor_path = corcc.package(persona, out_dir=skills_dir)
45
+ skill_path = Path(skills_dir) / "summarize" / "SKILL.md"
46
+ skill_path.parent.mkdir(parents=True, exist_ok=True)
47
+ skill_path.write_text(_STUB_SKILL, encoding="utf-8")
48
+ ac_slug, cor_slug = ac_path.parent.name, cor_path.parent.name
49
+ click.echo(f" AC → {ac_slug}/SKILL.md")
50
+ click.echo(f" CoR → {cor_slug}/SKILL.md")
51
+ click.echo(f" skill → summarize/SKILL.md (a regular skill)")
52
+
53
+ click.echo("\n══ 2. FORGE the SC sequence grammar (shape) ══")
54
+ scl = forge("debug-then-explain", SEED_SEQUENCES, db=db)
55
+ click.echo(f" forged scope={scl.scope}, {scl.rule_count} rules")
56
+
57
+ sequence = f"[ac:{ac_slug}] ⇒ [cor:{cor_slug}] ⇒ [skill:summarize] ⇒ |Answer|"
58
+ click.echo(f"\n══ 3. LINT the SC sequence (syntax) ══\n {sequence}")
59
+ v, fixes = lint(scl, sequence)
60
+ click.echo(f" → {'clean ✓' if not v else 'violations'}")
61
+
62
+ click.echo("\n══ 4. LINT a malformed sequence (dropped ⇒) ══")
63
+ bad = f"[ac:{ac_slug}] [skill:summarize] ⇒ |Answer|"
64
+ v2, fx2 = lint(scl, bad)
65
+ if v2:
66
+ click.echo(f" {bad}\n ✗ {v2[0].verdict}: expected {v2[0].expected_token!r}, "
67
+ f"found {v2[0].found_token!r}; fix insert {list(fx2[0].candidate_tokens)}")
68
+
69
+ click.echo("\n══ 5. RESOLVE steps (each must exist on disk) ══")
70
+ rows, missing = resolve_steps(sequence, skills_dir=skills_dir)
71
+ for i, kind, ref, status, path in rows:
72
+ click.echo(f" {'✓' if status=='ok' else '✗'} [{i}] {kind:5} {ref}")
73
+
74
+ click.echo("\n══ 6. PACKAGE — roll the chain up into one SKILL.md ══")
75
+ sc_path = package("debug-then-explain", sequence, out_dir=out_dir, skills_dir=skills_dir)
76
+ click.echo(f" wrote {sc_path}")
77
+ click.echo(" ── SKILL.md (rollup) ──")
78
+ click.echo("\n".join(" " + ln for ln in sc_path.read_text().splitlines()[:24]))
79
+
80
+ click.echo("\n══ 7. The capstone ══")
81
+ click.echo(" AC → CoR → SC: an attention template, a spoken reasoning chain, and a regular "
82
+ "skill, composed into ONE publishable skillchain. All SKILL.md. All syntax-checked.")
83
+
84
+
85
+ if __name__ == "__main__":
86
+ main()
@@ -0,0 +1,106 @@
1
+ """SCCC — the Skillchain Compiler-Compiler. Composes ACCC + CORCC + regular skills.
2
+
3
+ Same *CC shape: catch the SC sequence grammar → LINT its syntax → resolve each
4
+ step to a SKILL.md package (forged AC/CoR or regular skill) → compile the rollup
5
+ to `<name>/SKILL.md`. Built on the existing `skillchain` module for indexing,
6
+ resolution, and rollup compilation. Syntax + resolution only — never content.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from pathlib import Path
12
+ import re
13
+
14
+ import skillchain as sc
15
+
16
+ from chaincompiler import slugify, learn # made FROM CC (CC re-exports APE's slugify/learn)
17
+ from chaincompiler import gate as cp_gate # the persistent grammar gate, re-exported by CC
18
+ from rulecatcher.db import connect
19
+
20
+ _STEP_RE = re.compile(r"\[(ac|cor|skill):([^\]]+)\]")
21
+ _HELD_RE = re.compile(r"\|([^|]+)\|")
22
+
23
+
24
+ @dataclass
25
+ class SCLanguage:
26
+ name: str
27
+ scope: str
28
+ db: str
29
+ rule_count: int
30
+
31
+
32
+ @dataclass
33
+ class StepRef:
34
+ kind: str # ac | cor | skill
35
+ ref: str # the referenced package name
36
+ slug: str # resolved package slug
37
+
38
+
39
+ def forge(name: str, sequences: list[str], *, db: str) -> SCLanguage:
40
+ """Catch + ratify the SC sequence notation grammar (shape only)."""
41
+ scope = f"sc:{name}"
42
+ with connect(db) as cx:
43
+ adopted = learn(cx, sequences, scope=scope, rule_types=("next_kind",), label=f"sc:{name}")
44
+ return SCLanguage(name=name, scope=scope, db=db, rule_count=len(adopted))
45
+
46
+
47
+ def lint(language: SCLanguage, sequence: str) -> tuple[list, list]:
48
+ """Syntax-lint an SC sequence against the ratified grammar (the *CC check)."""
49
+ with connect(language.db) as cx:
50
+ return cp_gate(cx, sequence, scope=language.scope)
51
+
52
+
53
+ def parse_sequence(sequence: str) -> tuple[list[StepRef], str | None]:
54
+ steps = [StepRef(kind=m.group(1), ref=m.group(2).strip(), slug=slugify(m.group(2).strip()))
55
+ for m in _STEP_RE.finditer(sequence)]
56
+ held = _HELD_RE.search(sequence)
57
+ return steps, (held.group(1).strip() if held else None)
58
+
59
+
60
+ def _index(skills_dir: str | None):
61
+ roots = list(sc.SKILL_ROOTS)
62
+ if skills_dir:
63
+ roots.insert(0, Path(skills_dir))
64
+ return sc.index_skills(roots=roots)
65
+
66
+
67
+ def resolve_steps(sequence: str, *, skills_dir: str | None = None) -> tuple[list, list[str]]:
68
+ """Resolve each step to a package. Returns (rows, missing)."""
69
+ steps, _ = parse_sequence(sequence)
70
+ idx = _index(skills_dir)
71
+ rows, missing = [], []
72
+ for i, step in enumerate(steps, 1):
73
+ rec = sc.resolve(step.slug, idx) or sc.resolve(step.ref, idx)
74
+ if rec:
75
+ rows.append((i, step.kind, step.ref, "ok", str(rec["path"])))
76
+ else:
77
+ rows.append((i, step.kind, step.ref, "MISSING", ""))
78
+ missing.append(f"{step.kind}:{step.ref}")
79
+ return rows, missing
80
+
81
+
82
+ def package(name: str, sequence: str, *, out_dir: str, skills_dir: str | None = None,
83
+ description: str | None = None) -> Path:
84
+ """Compile an SC sequence into a rollup `<name>/SKILL.md`.
85
+
86
+ Resolves AC/CoR/skill steps to their packages (validated present) and rolls
87
+ them up in order via the skillchain compiler.
88
+ """
89
+ steps, held = parse_sequence(sequence)
90
+ spec = {
91
+ "name": slugify(name),
92
+ # NOTE: no ": " prefix in the default — write_skill (APE) does not YAML-escape
93
+ # the description; a plain scalar containing ": " emits unparseable frontmatter.
94
+ # (The step refs keep their `kind:name` form — colon-no-space is YAML-safe.)
95
+ "description": description or (
96
+ f"Skillchain — {' → '.join(f'{s.kind}:{s.ref}' for s in steps)}"
97
+ + (f" → {held}" if held else "")
98
+ ),
99
+ "steps": [{"skill": s.slug} for s in steps],
100
+ }
101
+ idx = _index(skills_dir)
102
+ ok, _rows, missing = sc.validate(spec, idx)
103
+ if not ok:
104
+ raise ValueError(f"cannot compile SC '{name}': unresolved steps {missing}")
105
+ pkg, _deps = sc.compile_package(spec, idx, out_dir)
106
+ return pkg / "SKILL.md"
@@ -0,0 +1,19 @@
1
+ """SC (skillchain) sequence notation.
2
+
3
+ An SC IS A sequence chaining ACs + CoRs + regular skills, converging on a result:
4
+
5
+ [ac:<name>] ⇒ [cor:<name>] ⇒ [skill:<name>] ⇒ ... ⇒ |Result|
6
+
7
+ Each `[kind:name]` step references a SKILL.md package on disk — a forged
8
+ attention chain (kind=ac), a forged chain-of-reasoning (kind=cor), or a regular
9
+ skill (kind=skill). Written in Dense Rune-Chain so it is syntax-linted like every
10
+ other *CC notation. SC is the highest composite: AC → CoR → SC.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ # Seed SC sequences (used to forge the SC notation grammar — the shape only).
15
+ SEED_SEQUENCES = [
16
+ "[ac:debug-attention] ⇒ [cor:think-like-einstein] ⇒ [skill:summarize] ⇒ |Answer|",
17
+ "[ac:debug-attention] ⇒ [skill:summarize] ⇒ |Answer|",
18
+ "[cor:think-like-einstein] ⇒ [skill:summarize] ⇒ |Answer|",
19
+ ]
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: sccc
3
+ Version: 0.3.0
4
+ Summary: Skillchain Compiler-Compiler — chains ACs + CoRs + skills into one SKILL.md
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: chaincompiler-accc==0.3.0
7
+ Requires-Dist: corcc==0.3.0
8
+ Requires-Dist: chaincompiler==0.3.0
9
+ Requires-Dist: rulecatcher==0.3.0
10
+ Requires-Dist: skillchain-compiler==0.3.0
11
+ Requires-Dist: click
@@ -0,0 +1,12 @@
1
+ pyproject.toml
2
+ src/sccc/__init__.py
3
+ src/sccc/cli.py
4
+ src/sccc/forge.py
5
+ src/sccc/notation.py
6
+ src/sccc.egg-info/PKG-INFO
7
+ src/sccc.egg-info/SOURCES.txt
8
+ src/sccc.egg-info/dependency_links.txt
9
+ src/sccc.egg-info/entry_points.txt
10
+ src/sccc.egg-info/requires.txt
11
+ src/sccc.egg-info/top_level.txt
12
+ tests/test_sccc.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sccc = sccc.cli:main
@@ -0,0 +1,6 @@
1
+ chaincompiler-accc==0.3.0
2
+ corcc==0.3.0
3
+ chaincompiler==0.3.0
4
+ rulecatcher==0.3.0
5
+ skillchain-compiler==0.3.0
6
+ click
@@ -0,0 +1 @@
1
+ sccc
@@ -0,0 +1,83 @@
1
+ """SCCC tests: forging the sequence grammar, lint, resolution, rollup packaging."""
2
+ from __future__ import annotations
3
+
4
+ from pathlib import Path
5
+
6
+ import accc
7
+ import corcc
8
+ from corcc.notation import EINSTEIN
9
+
10
+ from sccc import forge, lint, package, parse_sequence, resolve_steps
11
+ from sccc.notation import SEED_SEQUENCES
12
+
13
+ _STUB = "---\nname: summarize\ndescription: condense\n---\n\nSummarize it.\n"
14
+
15
+
16
+ def _setup(tmp_path: Path):
17
+ db = str(tmp_path / "cc.db")
18
+ skills = tmp_path / "skills"
19
+ accc.forge("debug-attention", ["[Symptom] ⇒ [Repro] ⇒ |Localize|"], db=db)
20
+ ac = accc.package("debug-attention", "[Symptom] ⇒ [Repro] ⇒ |Localize|", out_dir=str(skills))
21
+ persona = corcc.forge_persona(EINSTEIN, db=db)
22
+ cor = corcc.package(persona, out_dir=str(skills))
23
+ (skills / "summarize").mkdir(parents=True, exist_ok=True)
24
+ (skills / "summarize" / "SKILL.md").write_text(_STUB)
25
+ seq = f"[ac:{ac.parent.name}] ⇒ [cor:{cor.parent.name}] ⇒ [skill:summarize] ⇒ |Answer|"
26
+ return db, str(skills), seq
27
+
28
+
29
+ def test_parse_sequence_extracts_typed_steps():
30
+ steps, held = parse_sequence("[ac:foo] ⇒ [cor:bar] ⇒ [skill:baz] ⇒ |Result|")
31
+ assert [(s.kind, s.ref) for s in steps] == [("ac", "foo"), ("cor", "bar"), ("skill", "baz")]
32
+ assert held == "Result"
33
+
34
+
35
+ def test_closure_a_skillchain_can_chain_another_skillchain(tmp_path: Path):
36
+ """Everything is a skill dir, so an SC can reference an SC. The algebra closes."""
37
+ db, skills, seq = _setup(tmp_path)
38
+ # SC#1 compiled INTO the skills dir → it is itself an indexable skill dir.
39
+ forge("inner", [seq], db=db)
40
+ sc1 = package("inner", seq, out_dir=skills, skills_dir=skills)
41
+ assert sc1.exists()
42
+ # SC#2 references SC#1 as a plain skill (closure) + another skill.
43
+ seq2 = f"[skill:{sc1.parent.name}] ⇒ [skill:summarize] ⇒ |Done|"
44
+ _rows, missing = resolve_steps(seq2, skills_dir=skills)
45
+ assert missing == []
46
+ sc2 = package("outer", seq2, out_dir=skills, skills_dir=skills)
47
+ assert sc1.parent.name in sc2.read_text() # SC#2 rolls up SC#1
48
+
49
+
50
+ def test_forge_and_lint_sequence(tmp_path: Path):
51
+ db, skills, seq = _setup(tmp_path)
52
+ scl = forge("demo", SEED_SEQUENCES, db=db)
53
+ assert scl.rule_count > 0
54
+ assert lint(scl, seq)[0] == [] # well-formed
55
+ assert lint(scl, "[ac:foo] [skill:baz] ⇒ |R|")[0] # malformed (dropped ⇒)
56
+
57
+
58
+ def test_resolve_steps_finds_forged_packages(tmp_path: Path):
59
+ _db, skills, seq = _setup(tmp_path)
60
+ rows, missing = resolve_steps(seq, skills_dir=skills)
61
+ assert missing == []
62
+ assert all(status == "ok" for _, _, _, status, _ in rows)
63
+
64
+
65
+ def test_package_rolls_up_into_one_skill(tmp_path: Path):
66
+ _db, skills, seq = _setup(tmp_path)
67
+ out = tmp_path / "dist"
68
+ sc_path = package("debug-then-explain", seq, out_dir=str(out), skills_dir=skills)
69
+ text = sc_path.read_text()
70
+ assert sc_path.name == "SKILL.md"
71
+ assert "debug-attention" in text # AC step referenced
72
+ assert "thinklikeeinstein" in text # CoR step referenced
73
+ assert "summarize" in text # regular skill referenced
74
+
75
+
76
+ def test_package_refuses_when_step_missing(tmp_path: Path):
77
+ db, skills, _seq = _setup(tmp_path)
78
+ bad_seq = "[skill:does-not-exist] ⇒ |R|"
79
+ try:
80
+ package("broken", bad_seq, out_dir=str(tmp_path / "d"), skills_dir=skills)
81
+ assert False, "should refuse to compile with a missing step"
82
+ except ValueError:
83
+ pass