agentsbridge 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 agentsbridge contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,194 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentsbridge
3
+ Version: 0.1.0
4
+ Summary: Bridge AGENTS.md to every AI coding tool's rules file - one source of truth for Claude Code, Copilot, Cursor, Windsurf, Cline, and Aider.
5
+ Author: agentsbridge contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/DavidStarYu/agentsbridge
8
+ Project-URL: Repository, https://github.com/DavidStarYu/agentsbridge
9
+ Project-URL: Changelog, https://github.com/DavidStarYu/agentsbridge/blob/main/CHANGELOG.md
10
+ Keywords: agents-md,claude,claude-code,cursor,copilot,windsurf,cline,aider,ai,rules,cli,developer-tools
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development
23
+ Classifier: Topic :: Utilities
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Dynamic: license-file
28
+
29
+ <div align="center">
30
+
31
+ <img src="assets/banner.svg" alt="agentsbridge" width="640">
32
+
33
+ **One `AGENTS.md`. Every AI coding tool. Zero drift.**
34
+
35
+ [![CI](https://github.com/DavidStarYu/agentsbridge/actions/workflows/ci.yml/badge.svg)](https://github.com/DavidStarYu/agentsbridge/actions/workflows/ci.yml)
36
+ [![PyPI](https://img.shields.io/pypi/v/agentsbridge)](https://pypi.org/project/agentsbridge/)
37
+ [![Python](https://img.shields.io/pypi/pyversions/agentsbridge)](https://pypi.org/project/agentsbridge/)
38
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
39
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
40
+
41
+ [Bridge](#why) · [Quick start](#quick-start) · [Supported tools](#supported-tools) · [CI drift check](#ci-drift-check) · [FAQ](#faq)
42
+
43
+ </div>
44
+
45
+ ---
46
+
47
+ ## Why
48
+
49
+ Your team uses Claude Code. Your teammate uses Cursor. Someone just switched
50
+ to Codex CLI. And suddenly your coding standards live in **five different
51
+ files** that silently drift apart:
52
+
53
+ ```text
54
+ AGENTS.md ← Codex, Gemini CLI, Jules, Amp, Zed read this
55
+ CLAUDE.md ← Claude Code reads this (not AGENTS.md)
56
+ .github/copilot-instructions.md ← GitHub Copilot reads this (not AGENTS.md)
57
+ .cursor/rules/*.mdc ← Cursor reads this (not AGENTS.md, older versions)
58
+ .windsurfrules / .clinerules ← Windsurf / Cline read these
59
+ CONVENTIONS.md ← Aider reads this
60
+ ```
61
+
62
+ `agentsbridge` fixes this with one command. You maintain **one** `AGENTS.md`
63
+ (the emerging industry standard) — it generates and keeps every tool's file
64
+ in sync. Edit once, sync everywhere, and let CI catch anything that drifts.
65
+
66
+ ## Quick start
67
+
68
+ ```bash
69
+ pipx install agentsbridge # or: pip install agentsbridge / uv tool install agentsbridge
70
+ ```
71
+
72
+ **Already have rules somewhere?** Import them:
73
+
74
+ ```bash
75
+ agentsbridge import # seeds AGENTS.md from CLAUDE.md, .cursorrules, etc.
76
+ ```
77
+
78
+ **Starting fresh?**
79
+
80
+ ```bash
81
+ agentsbridge init # creates a starter AGENTS.md
82
+ ```
83
+
84
+ Then — the only command you'll ever need again:
85
+
86
+ ```bash
87
+ agentsbridge sync
88
+ ```
89
+
90
+ ```text
91
+ + created CLAUDE.md
92
+ + created .github/copilot-instructions.md
93
+ + created .cursor/rules/agentsbridge.mdc
94
+ + created .windsurfrules
95
+ + created .clinerules
96
+ + created CONVENTIONS.md
97
+
98
+ wrote 6 file(s)
99
+ ```
100
+
101
+ Edit `AGENTS.md`, run `agentsbridge sync`, commit. That's the whole workflow.
102
+
103
+ ## Supported tools
104
+
105
+ | Tool | File generated | Notes |
106
+ |---|---|---|
107
+ | **Claude Code** | `CLAUDE.md` | |
108
+ | **GitHub Copilot** | `.github/copilot-instructions.md` | |
109
+ | **Cursor** | `.cursor/rules/agentsbridge.mdc` | with `alwaysApply` frontmatter |
110
+ | **Windsurf** | `.windsurfrules` | |
111
+ | **Cline** | `.clinerules` | |
112
+ | **Aider** | `CONVENTIONS.md` | |
113
+ | **Codex CLI, Gemini CLI, Jules, Amp, Zed, opencode** | — | read `AGENTS.md` natively, nothing to generate |
114
+
115
+ Missing a tool? [Open an issue](https://github.com/DavidStarYu/agentsbridge/issues) —
116
+ adding a target is ~10 lines.
117
+
118
+ ## Safety
119
+
120
+ Generated files are marked:
121
+
122
+ ```markdown
123
+ <!-- generated by agentsbridge; do not edit -->
124
+ ```
125
+
126
+ If a target file already exists **without** that marker (i.e. you wrote it by
127
+ hand), `sync` skips it instead of clobbering your work. Use `--force` to
128
+ adopt it deliberately.
129
+
130
+ ```bash
131
+ agentsbridge sync --force # adopt existing files
132
+ agentsbridge sync --dry-run # preview changes
133
+ agentsbridge sync -t claude,copilot # subset of targets
134
+ ```
135
+
136
+ ## CI drift check
137
+
138
+ The whole point: rules that drift are rules nobody follows. Add the check to
139
+ your workflow and CI fails whenever someone edits a generated file — or
140
+ forgets to re-sync after editing `AGENTS.md`:
141
+
142
+ ```yaml
143
+ # .github/workflows/ci.yml
144
+ name: rules
145
+ on: [push, pull_request]
146
+ jobs:
147
+ agentsbridge:
148
+ runs-on: ubuntu-latest
149
+ steps:
150
+ - uses: actions/checkout@v4
151
+ - uses: DavidStarYu/agentsbridge/action@main
152
+ ```
153
+
154
+ Or invoke the CLI directly: `agentsbridge check` (exit code 1 on drift).
155
+
156
+ This repository runs the same check on itself — [dogfooding](.github/workflows/rules.yml).
157
+
158
+ ## FAQ
159
+
160
+ **Why AGENTS.md as the source, not my own config?**
161
+ AGENTS.md is the emerging standard (adopted by OpenAI Codex, Gemini CLI, Jules,
162
+ Amp, Zed and others — see [agents.md](https://agents.md)). Tools that don't
163
+ read it yet are exactly the ones this bridge targets. You keep zero
164
+ agentsbridge-specific config: the source file is a standard.
165
+
166
+ **Cursor already reads AGENTS.md, why generate an .mdc?**
167
+ Newer Cursor versions read `AGENTS.md` directly; many teams run versions that
168
+ don't, or want explicit rule scoping. The generated `.mdc` is harmless if
169
+ redundant — and you can exclude it: `agentsbridge sync -t claude,copilot,...`.
170
+
171
+ **Does it send my code anywhere?**
172
+ No. No network calls, no telemetry, no API keys. It's a template engine over
173
+ one markdown file. [Zero dependencies](pyproject.toml).
174
+
175
+ **How is this different from X?**
176
+ rulesync (Node.js) covers more features — MCP servers, subagents, commands —
177
+ at the cost of a bigger footprint and a `.rulesync/` directory convention.
178
+ agentsbridge does one thing: keep rule *files* in sync, in pure Python with
179
+ zero dependencies. If you live in `pipx`/`uv` and want the simple thing, this
180
+ is for you.
181
+
182
+ ## Development
183
+
184
+ ```bash
185
+ git clone https://github.com/DavidStarYu/agentsbridge
186
+ cd agentsbridge
187
+ pip install -e . pytest ruff
188
+ pytest # 36 tests
189
+ ruff check .
190
+ ```
191
+
192
+ ## License
193
+
194
+ [MIT](LICENSE)
@@ -0,0 +1,166 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/banner.svg" alt="agentsbridge" width="640">
4
+
5
+ **One `AGENTS.md`. Every AI coding tool. Zero drift.**
6
+
7
+ [![CI](https://github.com/DavidStarYu/agentsbridge/actions/workflows/ci.yml/badge.svg)](https://github.com/DavidStarYu/agentsbridge/actions/workflows/ci.yml)
8
+ [![PyPI](https://img.shields.io/pypi/v/agentsbridge)](https://pypi.org/project/agentsbridge/)
9
+ [![Python](https://img.shields.io/pypi/pyversions/agentsbridge)](https://pypi.org/project/agentsbridge/)
10
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
11
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
12
+
13
+ [Bridge](#why) · [Quick start](#quick-start) · [Supported tools](#supported-tools) · [CI drift check](#ci-drift-check) · [FAQ](#faq)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## Why
20
+
21
+ Your team uses Claude Code. Your teammate uses Cursor. Someone just switched
22
+ to Codex CLI. And suddenly your coding standards live in **five different
23
+ files** that silently drift apart:
24
+
25
+ ```text
26
+ AGENTS.md ← Codex, Gemini CLI, Jules, Amp, Zed read this
27
+ CLAUDE.md ← Claude Code reads this (not AGENTS.md)
28
+ .github/copilot-instructions.md ← GitHub Copilot reads this (not AGENTS.md)
29
+ .cursor/rules/*.mdc ← Cursor reads this (not AGENTS.md, older versions)
30
+ .windsurfrules / .clinerules ← Windsurf / Cline read these
31
+ CONVENTIONS.md ← Aider reads this
32
+ ```
33
+
34
+ `agentsbridge` fixes this with one command. You maintain **one** `AGENTS.md`
35
+ (the emerging industry standard) — it generates and keeps every tool's file
36
+ in sync. Edit once, sync everywhere, and let CI catch anything that drifts.
37
+
38
+ ## Quick start
39
+
40
+ ```bash
41
+ pipx install agentsbridge # or: pip install agentsbridge / uv tool install agentsbridge
42
+ ```
43
+
44
+ **Already have rules somewhere?** Import them:
45
+
46
+ ```bash
47
+ agentsbridge import # seeds AGENTS.md from CLAUDE.md, .cursorrules, etc.
48
+ ```
49
+
50
+ **Starting fresh?**
51
+
52
+ ```bash
53
+ agentsbridge init # creates a starter AGENTS.md
54
+ ```
55
+
56
+ Then — the only command you'll ever need again:
57
+
58
+ ```bash
59
+ agentsbridge sync
60
+ ```
61
+
62
+ ```text
63
+ + created CLAUDE.md
64
+ + created .github/copilot-instructions.md
65
+ + created .cursor/rules/agentsbridge.mdc
66
+ + created .windsurfrules
67
+ + created .clinerules
68
+ + created CONVENTIONS.md
69
+
70
+ wrote 6 file(s)
71
+ ```
72
+
73
+ Edit `AGENTS.md`, run `agentsbridge sync`, commit. That's the whole workflow.
74
+
75
+ ## Supported tools
76
+
77
+ | Tool | File generated | Notes |
78
+ |---|---|---|
79
+ | **Claude Code** | `CLAUDE.md` | |
80
+ | **GitHub Copilot** | `.github/copilot-instructions.md` | |
81
+ | **Cursor** | `.cursor/rules/agentsbridge.mdc` | with `alwaysApply` frontmatter |
82
+ | **Windsurf** | `.windsurfrules` | |
83
+ | **Cline** | `.clinerules` | |
84
+ | **Aider** | `CONVENTIONS.md` | |
85
+ | **Codex CLI, Gemini CLI, Jules, Amp, Zed, opencode** | — | read `AGENTS.md` natively, nothing to generate |
86
+
87
+ Missing a tool? [Open an issue](https://github.com/DavidStarYu/agentsbridge/issues) —
88
+ adding a target is ~10 lines.
89
+
90
+ ## Safety
91
+
92
+ Generated files are marked:
93
+
94
+ ```markdown
95
+ <!-- generated by agentsbridge; do not edit -->
96
+ ```
97
+
98
+ If a target file already exists **without** that marker (i.e. you wrote it by
99
+ hand), `sync` skips it instead of clobbering your work. Use `--force` to
100
+ adopt it deliberately.
101
+
102
+ ```bash
103
+ agentsbridge sync --force # adopt existing files
104
+ agentsbridge sync --dry-run # preview changes
105
+ agentsbridge sync -t claude,copilot # subset of targets
106
+ ```
107
+
108
+ ## CI drift check
109
+
110
+ The whole point: rules that drift are rules nobody follows. Add the check to
111
+ your workflow and CI fails whenever someone edits a generated file — or
112
+ forgets to re-sync after editing `AGENTS.md`:
113
+
114
+ ```yaml
115
+ # .github/workflows/ci.yml
116
+ name: rules
117
+ on: [push, pull_request]
118
+ jobs:
119
+ agentsbridge:
120
+ runs-on: ubuntu-latest
121
+ steps:
122
+ - uses: actions/checkout@v4
123
+ - uses: DavidStarYu/agentsbridge/action@main
124
+ ```
125
+
126
+ Or invoke the CLI directly: `agentsbridge check` (exit code 1 on drift).
127
+
128
+ This repository runs the same check on itself — [dogfooding](.github/workflows/rules.yml).
129
+
130
+ ## FAQ
131
+
132
+ **Why AGENTS.md as the source, not my own config?**
133
+ AGENTS.md is the emerging standard (adopted by OpenAI Codex, Gemini CLI, Jules,
134
+ Amp, Zed and others — see [agents.md](https://agents.md)). Tools that don't
135
+ read it yet are exactly the ones this bridge targets. You keep zero
136
+ agentsbridge-specific config: the source file is a standard.
137
+
138
+ **Cursor already reads AGENTS.md, why generate an .mdc?**
139
+ Newer Cursor versions read `AGENTS.md` directly; many teams run versions that
140
+ don't, or want explicit rule scoping. The generated `.mdc` is harmless if
141
+ redundant — and you can exclude it: `agentsbridge sync -t claude,copilot,...`.
142
+
143
+ **Does it send my code anywhere?**
144
+ No. No network calls, no telemetry, no API keys. It's a template engine over
145
+ one markdown file. [Zero dependencies](pyproject.toml).
146
+
147
+ **How is this different from X?**
148
+ rulesync (Node.js) covers more features — MCP servers, subagents, commands —
149
+ at the cost of a bigger footprint and a `.rulesync/` directory convention.
150
+ agentsbridge does one thing: keep rule *files* in sync, in pure Python with
151
+ zero dependencies. If you live in `pipx`/`uv` and want the simple thing, this
152
+ is for you.
153
+
154
+ ## Development
155
+
156
+ ```bash
157
+ git clone https://github.com/DavidStarYu/agentsbridge
158
+ cd agentsbridge
159
+ pip install -e . pytest ruff
160
+ pytest # 36 tests
161
+ ruff check .
162
+ ```
163
+
164
+ ## License
165
+
166
+ [MIT](LICENSE)
@@ -0,0 +1,3 @@
1
+ """agentsbridge - Bridge AGENTS.md to every AI coding tool's rules file."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,174 @@
1
+ """agentsbridge CLI - bridge AGENTS.md to every AI coding tool's rules file."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from . import __version__, core
10
+
11
+
12
+ def build_parser() -> argparse.ArgumentParser:
13
+ parser = argparse.ArgumentParser(
14
+ prog="agentsbridge",
15
+ description=(
16
+ "Bridge AGENTS.md to every AI coding tool's rules file. "
17
+ "Maintain one source of truth; generate CLAUDE.md, "
18
+ "copilot-instructions.md, Cursor rules, and more."
19
+ ),
20
+ )
21
+ parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
22
+ sub = parser.add_subparsers(dest="command")
23
+
24
+ p_sync = sub.add_parser("sync", help="generate all target rules files from AGENTS.md")
25
+ p_sync.add_argument(
26
+ "-t",
27
+ "--targets",
28
+ default="",
29
+ help="comma-separated subset of targets (default: all)",
30
+ )
31
+ p_sync.add_argument(
32
+ "--force", action="store_true", help="overwrite files not generated by agentsbridge"
33
+ )
34
+ p_sync.add_argument(
35
+ "--dry-run", action="store_true", help="show what would change without writing"
36
+ )
37
+
38
+ p_check = sub.add_parser(
39
+ "check", help="exit non-zero if any generated file drifted from AGENTS.md (for CI)"
40
+ )
41
+ p_check.add_argument(
42
+ "-t",
43
+ "--targets",
44
+ default="",
45
+ help="comma-separated subset of targets (default: all)",
46
+ )
47
+
48
+ p_import = sub.add_parser(
49
+ "import", help="seed AGENTS.md from an existing rules file (CLAUDE.md, .cursorrules, ...)"
50
+ )
51
+ p_import.add_argument("--out", default="AGENTS.md", help="output path (default: AGENTS.md)")
52
+
53
+ p_init = sub.add_parser("init", help="create a starter AGENTS.md")
54
+ p_init.add_argument("--force", action="store_true", help="overwrite existing AGENTS.md")
55
+
56
+ return parser
57
+
58
+
59
+ def _parse_targets(value: str) -> list[str] | None:
60
+ value = value.strip()
61
+ if not value:
62
+ return None
63
+ return [v.strip() for v in value.split(",") if v.strip()]
64
+
65
+
66
+ def _root() -> Path:
67
+ return Path.cwd()
68
+
69
+
70
+ def cmd_sync(args: argparse.Namespace) -> int:
71
+ root = core.walk_up(_root())
72
+ try:
73
+ results = core.sync(
74
+ root, _parse_targets(args.targets), force=args.force, dry_run=args.dry_run
75
+ )
76
+ except FileNotFoundError as e:
77
+ print(f"error: {e}", file=sys.stderr)
78
+ return 1
79
+ except ValueError as e:
80
+ print(f"error: {e}", file=sys.stderr)
81
+ return 2
82
+
83
+ label = "would create/update" if args.dry_run else "wrote"
84
+ for r in results:
85
+ icon = {
86
+ "created": "+",
87
+ "updated": "~",
88
+ "unchanged": "=",
89
+ "skipped": "!",
90
+ "missing": "?",
91
+ }.get(r.action, " ")
92
+ detail = f" ({r.detail})" if r.detail else ""
93
+ print(f" {icon} {r.action:<9} {r.path}{detail}")
94
+ created = sum(1 for r in results if r.action in ("created", "updated"))
95
+ skipped = sum(1 for r in results if r.action == "skipped")
96
+ print(
97
+ f"\n{label} {created} file(s)"
98
+ + (f", skipped {skipped}" if skipped else "")
99
+ + (" (dry run)" if args.dry_run else "")
100
+ )
101
+ return 0
102
+
103
+
104
+ def cmd_check(args: argparse.Namespace) -> int:
105
+ root = core.walk_up(_root())
106
+ try:
107
+ clean, results = core.check(root, _parse_targets(args.targets))
108
+ except FileNotFoundError as e:
109
+ print(f"error: {e}", file=sys.stderr)
110
+ return 1
111
+ except ValueError as e:
112
+ print(f"error: {e}", file=sys.stderr)
113
+ return 2
114
+
115
+ for r in results:
116
+ icon = {"ok": "=", "drifted": "!", "missing": "?", "skipped": "-"}.get(r.action, " ")
117
+ detail = f" ({r.detail})" if r.detail else ""
118
+ print(f" {icon} {r.action:<9} {r.path}{detail}")
119
+ if clean:
120
+ print("\nall target files are up to date")
121
+ return 0
122
+ print("\ndrift detected: run `agentsbridge sync` to fix", file=sys.stderr)
123
+ return 1
124
+
125
+
126
+ def cmd_import(args: argparse.Namespace) -> int:
127
+ root = _root()
128
+ try:
129
+ body, src = core.import_source(root)
130
+ except FileNotFoundError as e:
131
+ print(f"error: {e}", file=sys.stderr)
132
+ return 1
133
+
134
+ out_path = root / args.out
135
+ if out_path.exists():
136
+ print(f"error: {out_path} already exists. Delete it first or use --out.", file=sys.stderr)
137
+ return 1
138
+ out_path.write_text(body.rstrip() + "\n", encoding="utf-8")
139
+ print(f"created {out_path} from {src.name}")
140
+ print("review it, then run `agentsbridge sync` to propagate.")
141
+ return 0
142
+
143
+
144
+ def cmd_init(args: argparse.Namespace) -> int:
145
+ root = _root()
146
+ if (root / core.SOURCE_FILENAME).exists() and not args.force:
147
+ print("AGENTS.md already exists (use --force to overwrite)")
148
+ return 0
149
+ r = core.ensure_agents_md(root, dry_run=False)
150
+ if r.action == "created":
151
+ print(f"created {r.path}")
152
+ print("edit it, then run `agentsbridge sync`")
153
+ return 0
154
+
155
+
156
+ COMMANDS = {
157
+ "sync": cmd_sync,
158
+ "check": cmd_check,
159
+ "import": cmd_import,
160
+ "init": cmd_init,
161
+ }
162
+
163
+
164
+ def main(argv: list[str] | None = None) -> int:
165
+ parser = build_parser()
166
+ args = parser.parse_args(argv)
167
+ if not args.command:
168
+ parser.print_help()
169
+ return 0
170
+ return COMMANDS[args.command](args)
171
+
172
+
173
+ if __name__ == "__main__":
174
+ sys.exit(main())