flywire-coding-cortex 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.
@@ -0,0 +1,54 @@
1
+ {
2
+ "version": 1,
3
+ "active_key": "active_profile",
4
+ "profiles": {
5
+ "seed": {
6
+ "label": "Curated circuit (shipped seed)",
7
+ "approx_size": "~1–5 MB",
8
+ "description": "FlyWire-derived escape/steering subgraph ready for LIF. Offline after install.",
9
+ "kind": "seed",
10
+ "files": []
11
+ },
12
+ "codex": {
13
+ "label": "Codex FAFB v783 CSV dumps + ETL",
14
+ "approx_size": "~60–300 MB",
15
+ "description": "Download public Codex gzipped CSVs and rebuild circuit.json via ETL.",
16
+ "kind": "codex_etl",
17
+ "base_url": "https://storage.googleapis.com/flywire-data/codex/data/fafb/783",
18
+ "files": [
19
+ {"name": "classification.csv.gz", "path": "classification.csv.gz"},
20
+ {"name": "coordinates.csv.gz", "path": "coordinates.csv.gz"},
21
+ {"name": "connections.csv.gz", "path": "connections.csv.gz"},
22
+ {"name": "consolidated_cell_types.csv.gz", "path": "consolidated_cell_types.csv.gz"}
23
+ ]
24
+ },
25
+ "full": {
26
+ "label": "Zenodo connectivity Feathers (~10.6 GB) + ETL",
27
+ "approx_size": "~10.6 GB",
28
+ "description": "Heavy synapse/connectivity Feather tables. Explicit YES confirmation required.",
29
+ "kind": "zenodo_feather",
30
+ "confirm": true,
31
+ "record_url": "https://zenodo.org/records/10676866",
32
+ "files": [
33
+ {
34
+ "name": "proofread_connections_783.feather",
35
+ "url": "https://zenodo.org/records/10676866/files/proofread_connections_783.feather?download=1",
36
+ "approx_bytes": 893000000
37
+ },
38
+ {
39
+ "name": "proofread_root_ids_783.npy",
40
+ "url": "https://zenodo.org/records/10676866/files/proofread_root_ids_783.npy?download=1",
41
+ "approx_bytes": 1100000
42
+ },
43
+ {
44
+ "name": "flywire_synapses_783.feather",
45
+ "url": "https://zenodo.org/records/10676866/files/flywire_synapses_783.feather?download=1",
46
+ "approx_bytes": 9500000000,
47
+ "optional": true,
48
+ "note": "Largest file (~9.5 GB). Skipped unless --include-synapses."
49
+ }
50
+ ],
51
+ "requires_extras": ["pandas", "pyarrow"]
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "nodes": [
3
+ {"id": "role:dnp09", "label": "Forward implement", "kind": "role", "lastSeen": 0},
4
+ {"id": "role:gf", "label": "Stop / escape", "kind": "role", "lastSeen": 0},
5
+ {"id": "role:mdn", "label": "Reverse / revert", "kind": "role", "lastSeen": 0},
6
+ {"id": "role:lc4", "label": "Risk detector", "kind": "role", "lastSeen": 0},
7
+ {"id": "role:ascending", "label": "Read conventions", "kind": "role", "lastSeen": 0},
8
+ {"id": "concept:tests", "label": "Automated tests", "kind": "concept", "lastSeen": 0},
9
+ {"id": "concept:api-boundary", "label": "API boundary", "kind": "concept", "lastSeen": 0},
10
+ {"id": "concept:migration", "label": "Data migration", "kind": "concept", "lastSeen": 0}
11
+ ],
12
+ "edges": [
13
+ {"pre": "role:ascending", "post": "role:dnp09", "weight": 1.0, "why": "read before write", "evidence": []},
14
+ {"pre": "role:lc4", "post": "role:gf", "weight": 1.2, "why": "high risk can stop ship", "evidence": []},
15
+ {"pre": "concept:tests", "post": "role:dnp09", "weight": 1.0, "why": "verify after implement", "evidence": []},
16
+ {"pre": "concept:migration", "post": "role:lc4", "weight": 1.1, "why": "migrations are risky", "evidence": []}
17
+ ]
18
+ }
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: flywire-coding-cortex
3
+ description: >-
4
+ Runs FlyWire-derived LIF neurons via the flywire-cortex connector and maps
5
+ population rates to coding drives. Use when coding, debugging, or when the
6
+ user mentions FlyWire cortex, connectome thinking, or flywire-cortex.
7
+ ---
8
+
9
+ # FlyWire Coding Cortex
10
+
11
+ Install once (`pip install flywire-coding-cortex`), then use
12
+ `flywire-cortex` / MCP in every session.
13
+
14
+ **This is not a metaphor-only skill.** You must call the CLI (or MCP tools)
15
+ so real connectome edges are stepped. Do not invent biological fidelity.
16
+
17
+ ## Ensure installed
18
+
19
+ If `flywire-cortex` is missing:
20
+
21
+ ```bash
22
+ pip install flywire-coding-cortex
23
+ # or from a clone: pip install -e .
24
+ flywire-cortex fetch --profile seed
25
+ flywire-cortex install
26
+ ```
27
+
28
+ Never auto-run `--profile full` (~10.6 GB). Ask the user before `codex` or `full`.
29
+
30
+ ## Every coding turn
31
+
32
+ 1. `flywire-cortex status` — auto-hint if circuit missing; `fetch --profile seed` if needed
33
+ 2. `flywire-cortex sense --text "<task>"` — task → sensory currents
34
+ 3. `flywire-cortex step --ms 50` — run LIF on real edges
35
+ 4. `flywire-cortex signals` — read primary drive + clamps
36
+ 5. **Act only under the primary drive** (see [roles.md](roles.md))
37
+ 6. Follow [practices.md](practices.md) (winner-take-most, inhibition delay, sparse diffs)
38
+ 7. After verify: `flywire-cortex remember strengthen|weaken …`
39
+
40
+ Optional MCP tools: `cortex_status`, `cortex_sense`, `cortex_step`, `cortex_stimulate`, `cortex_signals`, `cortex_remember`.
41
+
42
+ ## Profiles
43
+
44
+ | Profile | Size | Command |
45
+ |---|---|---|
46
+ | seed | ~MB | `flywire-cortex fetch` |
47
+ | codex | ~60–300 MB | `flywire-cortex fetch --profile codex` |
48
+ | full | ~10.6 GB | `flywire-cortex fetch --profile full --yes` |
49
+
50
+ ## Hard rules
51
+
52
+ - One primary drive per turn
53
+ - After high risk / `gf` stop: list blast radius before edits
54
+ - Strengthen memory only when tests/user verification pass
55
+ - Cite FlyWire data as CC BY-NC derived; dynamics are a model
@@ -0,0 +1,14 @@
1
+ # Thinking practices
2
+
3
+ Apply on top of real LIF rates:
4
+
5
+ 1. **Winner-take-most** — Exactly one primary role; no feature+refactor+rename in one turn.
6
+ 2. **Inhibition delay** — If risk/loom is hot, list blast radius and invariants *before* edits.
7
+ 3. **Recurrent working set** — Keep ≤5 active files/APIs; re-touch them each turn.
8
+ 4. **Hebbian writeback** — `remember strengthen` only after verify; `weaken` on failure.
9
+ 5. **Sparse coding** — Smallest change that satisfies the primary drive.
10
+ 6. **Bilateral steer** — Design forks: one paragraph L vs R, then pick (`dna01`/`dna02`).
11
+ 7. **Proprioception first** — Read neighbors/conventions (`ascending`) before new patterns.
12
+ 8. **Refractory** — After `gf` stop or `mdn` reverse, no new feature drive that turn.
13
+ 9. **Population scores** — Prefer CLI sense scores over vibes.
14
+ 10. **Consolidate** — End of session: 3 new synapses + 1 weakened belief into memory.
@@ -0,0 +1,17 @@
1
+ # Role → coding drive map
2
+
3
+ FlyWire-style role names are **coding lenses** driven by real population rates.
4
+
5
+ | Role | Drive | Agent action |
6
+ |---|---|---|
7
+ | `lc4` / `lplc2` | risk / loom | Scan blast radius, security, regressions |
8
+ | `gf` | escape / stop | Do not ship; unblocker; halt feature work |
9
+ | `dnp09` | forward | Implement the requested change |
10
+ | `mdn` | reverse | Revert / undo bad direction |
11
+ | `dna01` / `dna02` | steer | Write L vs R design tradeoff, then choose |
12
+ | `dng11` | groom | Cleanup, rename, lint-only |
13
+ | `escw` | effort under pressure | Polish without scope creep |
14
+ | `ascending` | proprioception | Read local conventions before inventing |
15
+ | `sensory` | external constraint | Honor ticket / failing test / user constraint |
16
+
17
+ Primary drive comes from `flywire-cortex signals` (`primary` field). Support roles may advise but must not override.
@@ -0,0 +1,85 @@
1
+ """Clamp LIF population rates into coding-agent drives (SignalBuilder analogue)."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import asdict, dataclass, field
5
+ from typing import Any
6
+
7
+
8
+ @dataclass
9
+ class CodingSignals:
10
+ primary: str = "dnp09"
11
+ escape: bool = False
12
+ stop: bool = False
13
+ reverse: bool = False
14
+ walk_drive: float = 0.0
15
+ groom_drive: float = 0.0
16
+ wing_drive: float = 0.0
17
+ nervous: float = 0.0
18
+ turn_bias: float = 0.0
19
+ arousal: float = 0.0
20
+ support: list[str] = field(default_factory=list)
21
+ rates: dict[str, float] = field(default_factory=dict)
22
+ rationale: str = ""
23
+
24
+ def to_dict(self) -> dict[str, Any]:
25
+ return asdict(self)
26
+
27
+
28
+ def build_signals(
29
+ rates: dict[str, float],
30
+ *,
31
+ gf_spike: bool = False,
32
+ dna_baseline: float = 0.0,
33
+ ) -> CodingSignals:
34
+ nervous = _clamp(rates.get("loom", 0.0) / 80.0)
35
+ walk = _clamp(rates.get("fwd", 0.0) / 10.0, 0.0, 1.3)
36
+ groom = rates.get("groom", 0.0) / 8.0
37
+ wing = _clamp(rates.get("escw", 0.0) / 10.0, 0.0, 1.3)
38
+ arousal = _clamp(rates.get("pop", 0.0) / 20.0)
39
+ diff = rates.get("dna_l", 0.0) - rates.get("dna_r", 0.0)
40
+ turn = _clamp((diff - dna_baseline) * 0.04, -1.0, 1.0)
41
+ reverse = rates.get("mdn", 0.0) > 8.0
42
+ escape = bool(gf_spike)
43
+ stop = escape or nervous > 0.85
44
+
45
+ scores = {
46
+ "gf": 1.0 if escape or stop else nervous * 0.5,
47
+ "mdn": 0.9 if reverse else 0.0,
48
+ "dng11": _clamp(groom / 1.5),
49
+ "dnp09": _clamp(walk / 1.3) + 0.05,
50
+ "dna": abs(turn),
51
+ "escw": wing,
52
+ "lc4": nervous,
53
+ }
54
+ primary = max(scores, key=scores.get)
55
+ if primary == "dna":
56
+ primary = "dna01"
57
+ if primary == "lc4" and not stop:
58
+ primary = "dnp09" if walk >= 0.15 else "ascending"
59
+
60
+ support = [
61
+ k for k, v in sorted(scores.items(), key=lambda kv: -kv[1]) if k != primary and v > 0.2
62
+ ][:3]
63
+ rationale = (
64
+ f"primary={primary} walk={walk:.2f} nervous={nervous:.2f} "
65
+ f"mdn={rates.get('mdn', 0):.1f} gf={escape}"
66
+ )
67
+ return CodingSignals(
68
+ primary=primary,
69
+ escape=escape,
70
+ stop=stop,
71
+ reverse=reverse,
72
+ walk_drive=walk,
73
+ groom_drive=groom,
74
+ wing_drive=wing,
75
+ nervous=nervous,
76
+ turn_bias=turn,
77
+ arousal=arousal,
78
+ support=support,
79
+ rates=dict(rates),
80
+ rationale=rationale,
81
+ )
82
+
83
+
84
+ def _clamp(x: float, lo: float = 0.0, hi: float = 1.0) -> float:
85
+ return max(lo, min(hi, float(x)))
@@ -0,0 +1,304 @@
1
+ """CLI: fetch, status, sense, step, stimulate, signals, remember, install, mcp."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from . import bridge, memory, sense
10
+ from .etl import download as dl
11
+ from .lif import Circuit, LIFSim
12
+ from .paths import (
13
+ active_profile,
14
+ circuit_path,
15
+ home,
16
+ load_config,
17
+ set_active_profile,
18
+ state_path,
19
+ )
20
+
21
+
22
+ def _sim(profile: str | None = None) -> LIFSim:
23
+ path = circuit_path(profile)
24
+ if not path.exists():
25
+ if (profile or active_profile()) == "seed":
26
+ dl.fetch_seed()
27
+ else:
28
+ raise SystemExit(
29
+ f"No circuit for profile {profile or active_profile()}. "
30
+ f"Run: flywire-cortex fetch --profile {profile or active_profile()}"
31
+ )
32
+ return LIFSim(Circuit.load(circuit_path(profile)))
33
+
34
+
35
+ _SIM: LIFSim | None = None
36
+
37
+
38
+ def _save_sim(sim: LIFSim) -> None:
39
+ state_path().write_text(json.dumps(sim.to_state()) + "\n", encoding="utf-8")
40
+
41
+
42
+ def get_sim(reload: bool = False) -> LIFSim:
43
+ global _SIM
44
+ if _SIM is None or reload:
45
+ _SIM = _sim()
46
+ sp = state_path()
47
+ if sp.exists() and not reload:
48
+ try:
49
+ _SIM.load_state(json.loads(sp.read_text(encoding="utf-8")))
50
+ except (json.JSONDecodeError, ValueError, KeyError):
51
+ pass
52
+ return _SIM
53
+
54
+
55
+ def cmd_fetch(args: argparse.Namespace) -> int:
56
+ if args.list_profiles:
57
+ profiles = dl.list_profiles()
58
+ active = active_profile()
59
+ for name, meta in profiles.items():
60
+ mark = " (active)" if name == active else ""
61
+ print(f"{name}{mark}: {meta.get('approx_size')} — {meta.get('label')}")
62
+ print(f" {meta.get('description')}")
63
+ return 0
64
+
65
+ profile = args.profile or "seed"
66
+ if profile == "seed":
67
+ path = dl.fetch_seed(force=args.force)
68
+ elif profile == "codex":
69
+ path = dl.fetch_codex(force=args.force)
70
+ elif profile == "full":
71
+ path = dl.fetch_full(
72
+ force=args.force,
73
+ include_synapses=args.include_synapses,
74
+ yes=args.yes,
75
+ )
76
+ else:
77
+ raise SystemExit(f"Unknown profile: {profile}")
78
+ set_active_profile(profile)
79
+ # new circuit → clear LIF state
80
+ global _SIM
81
+ _SIM = None
82
+ sp = state_path()
83
+ if sp.exists():
84
+ sp.unlink()
85
+ data = json.loads(path.read_text(encoding="utf-8"))
86
+ print(
87
+ json.dumps(
88
+ {
89
+ "ok": True,
90
+ "profile": profile,
91
+ "circuit": str(path),
92
+ "neurons": len(data["neurons"]),
93
+ "edges": len(data["edges"]),
94
+ },
95
+ indent=2,
96
+ )
97
+ )
98
+ return 0
99
+
100
+
101
+ def cmd_status(_: argparse.Namespace) -> int:
102
+ cfg = load_config()
103
+ profile = cfg.get("active_profile", "seed")
104
+ path = circuit_path(profile)
105
+ info: dict = {
106
+ "home": str(home()),
107
+ "active_profile": profile,
108
+ "circuit": str(path),
109
+ "circuit_exists": path.exists(),
110
+ }
111
+ if path.exists():
112
+ data = json.loads(path.read_text(encoding="utf-8"))
113
+ roles: dict[str, int] = {}
114
+ for n in data["neurons"]:
115
+ roles[n.get("role", "?")] = roles.get(n.get("role", "?"), 0) + 1
116
+ info.update(
117
+ {
118
+ "neurons": len(data["neurons"]),
119
+ "edges": len(data["edges"]),
120
+ "source": data.get("source"),
121
+ "roles": roles,
122
+ }
123
+ )
124
+ else:
125
+ info["hint"] = "flywire-cortex fetch --profile seed"
126
+ print(json.dumps(info, indent=2))
127
+ return 0
128
+
129
+
130
+ def cmd_sense(args: argparse.Namespace) -> int:
131
+ text = args.text or " ".join(args.words) or ""
132
+ scores = sense.sense_text(
133
+ text,
134
+ risk=args.risk,
135
+ ambiguity=args.ambiguity,
136
+ test_red=args.test_red,
137
+ urgency=args.urgency,
138
+ )
139
+ sim = get_sim()
140
+ sim.set_inputs(**scores.as_inputs())
141
+ _save_sim(sim)
142
+ print(json.dumps({"text": text, "inputs": scores.as_inputs()}, indent=2))
143
+ return 0
144
+
145
+
146
+ def cmd_step(args: argparse.Namespace) -> int:
147
+ sim = get_sim()
148
+ if args.text:
149
+ scores = sense.sense_text(args.text)
150
+ sim.set_inputs(**scores.as_inputs())
151
+ out = sim.step(args.ms)
152
+ _save_sim(sim)
153
+ print(json.dumps(out, indent=2))
154
+ return 0
155
+
156
+
157
+ def cmd_stimulate(args: argparse.Namespace) -> int:
158
+ sim = get_sim()
159
+ n = sim.stimulate_group(args.group, strength=args.strength, duration_ms=args.ms)
160
+ if n == 0:
161
+ raise SystemExit(f"Unknown or empty group: {args.group}. Try: {list(sim.groups)}")
162
+ _save_sim(sim)
163
+ print(json.dumps({"group": args.group, "neurons": n, "strength": args.strength, "ms": args.ms}, indent=2))
164
+ return 0
165
+
166
+
167
+ def cmd_signals(args: argparse.Namespace) -> int:
168
+ sim = get_sim()
169
+ if args.ms:
170
+ sim.step(args.ms)
171
+ gf = sim.consume_gf()
172
+ diff = sim.rates["dna_l"] - sim.rates["dna_r"]
173
+ base = float(getattr(sim, "dna_baseline", 0.0))
174
+ base += (diff - base) * (1.0 / 8.0)
175
+ sim.dna_baseline = base
176
+ sig = bridge.build_signals(sim.rates, gf_spike=gf, dna_baseline=base)
177
+ _save_sim(sim)
178
+ print(json.dumps(sig.to_dict(), indent=2))
179
+ return 0
180
+
181
+
182
+ def cmd_reset(_: argparse.Namespace) -> int:
183
+ global _SIM
184
+ _SIM = None
185
+ sp = state_path()
186
+ if sp.exists():
187
+ sp.unlink()
188
+ print(json.dumps({"ok": True, "reset": True}, indent=2))
189
+ return 0
190
+
191
+
192
+ def cmd_remember(args: argparse.Namespace) -> int:
193
+ if args.action == "query":
194
+ print(json.dumps(memory.query(args.text or ""), indent=2))
195
+ elif args.action == "strengthen":
196
+ print(json.dumps(memory.strengthen(args.pre, args.post, why=args.why or ""), indent=2))
197
+ elif args.action == "weaken":
198
+ print(json.dumps(memory.weaken(args.pre, args.post, why=args.why or ""), indent=2))
199
+ elif args.action == "add-node":
200
+ print(json.dumps(memory.add_node(args.pre, args.label or args.pre, args.kind or "concept"), indent=2))
201
+ elif args.action == "add-edge":
202
+ print(json.dumps(memory.add_edge(args.pre, args.post, why=args.why or ""), indent=2))
203
+ else:
204
+ raise SystemExit(f"Unknown remember action: {args.action}")
205
+ return 0
206
+
207
+
208
+ def cmd_install(args: argparse.Namespace) -> int:
209
+ from .install_skills import install_all
210
+
211
+ result = install_all(targets=args.targets)
212
+ print(json.dumps(result, indent=2))
213
+ return 0
214
+
215
+
216
+ def cmd_mcp(_: argparse.Namespace) -> int:
217
+ from .mcp_server import run_stdio
218
+
219
+ run_stdio()
220
+ return 0
221
+
222
+
223
+ def build_parser() -> argparse.ArgumentParser:
224
+ p = argparse.ArgumentParser(
225
+ prog="flywire-cortex",
226
+ description="Run FlyWire-derived LIF neurons and map rates to coding-agent drives.",
227
+ )
228
+ sub = p.add_subparsers(dest="cmd", required=True)
229
+
230
+ f = sub.add_parser("fetch", help="Download / activate a circuit profile")
231
+ f.add_argument("--profile", choices=["seed", "codex", "full"], default="seed")
232
+ f.add_argument("--list-profiles", action="store_true")
233
+ f.add_argument("--force", action="store_true")
234
+ f.add_argument("--yes", action="store_true", help="Confirm full (~10.6 GB) download")
235
+ f.add_argument("--include-synapses", action="store_true", help="Also fetch ~9.5 GB synapse Feather")
236
+ f.set_defaults(func=cmd_fetch)
237
+
238
+ s = sub.add_parser("status", help="Show active profile and circuit stats")
239
+ s.set_defaults(func=cmd_status)
240
+
241
+ se = sub.add_parser("sense", help="Encode a coding task into sensory currents")
242
+ se.add_argument("words", nargs="*", help="Task text")
243
+ se.add_argument("--text", default="")
244
+ se.add_argument("--risk", type=float, default=None)
245
+ se.add_argument("--ambiguity", type=float, default=None)
246
+ se.add_argument("--test-red", dest="test_red", type=float, default=None)
247
+ se.add_argument("--urgency", type=float, default=None)
248
+ se.set_defaults(func=cmd_sense)
249
+
250
+ st = sub.add_parser("step", help="Advance LIF simulation")
251
+ st.add_argument("--ms", type=int, default=50)
252
+ st.add_argument("--text", default="", help="Optional sense text before stepping")
253
+ st.set_defaults(func=cmd_step)
254
+
255
+ sm = sub.add_parser("stimulate", help="Optogenetic-style population stim")
256
+ sm.add_argument("group", help="Role group: gf, dnp09, mdn, lc4, …")
257
+ sm.add_argument("--strength", type=float, default=0.25)
258
+ sm.add_argument("--ms", type=int, default=400)
259
+ sm.set_defaults(func=cmd_stimulate)
260
+
261
+ sg = sub.add_parser("signals", help="Emit clamped coding drives from rates")
262
+ sg.add_argument("--ms", type=int, default=0, help="Optional step before reading")
263
+ sg.set_defaults(func=cmd_signals)
264
+
265
+ rs = sub.add_parser("reset", help="Clear persisted LIF state")
266
+ rs.set_defaults(func=cmd_reset)
267
+
268
+ rm = sub.add_parser("remember", help="Hebbian coding-memory graph")
269
+ rm.add_argument("action", choices=["query", "strengthen", "weaken", "add-node", "add-edge"])
270
+ rm.add_argument("--text", default="")
271
+ rm.add_argument("--pre", default="")
272
+ rm.add_argument("--post", default="")
273
+ rm.add_argument("--why", default="")
274
+ rm.add_argument("--label", default="")
275
+ rm.add_argument("--kind", default="concept")
276
+ rm.set_defaults(func=cmd_remember)
277
+
278
+ ins = sub.add_parser("install", help="Wire Cursor / OpenClaw / Claude skills + print MCP snippet")
279
+ ins.add_argument(
280
+ "--targets",
281
+ nargs="*",
282
+ default=["cursor", "openclaw", "claude"],
283
+ help="Subset of: cursor openclaw claude",
284
+ )
285
+ ins.set_defaults(func=cmd_install)
286
+
287
+ m = sub.add_parser("mcp", help="Run MCP stdio connector")
288
+ m.set_defaults(func=cmd_mcp)
289
+
290
+ return p
291
+
292
+
293
+ def main(argv: list[str] | None = None) -> None:
294
+ parser = build_parser()
295
+ args = parser.parse_args(argv)
296
+ try:
297
+ code = args.func(args)
298
+ except BrokenPipeError:
299
+ code = 0
300
+ sys.exit(code or 0)
301
+
302
+
303
+ if __name__ == "__main__":
304
+ main()
@@ -0,0 +1 @@
1
+ """ETL package."""