oats-scan 0.2.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,33 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pheo Inc.
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.
22
+
23
+ ---
24
+
25
+ NOTE ON THE CLASSIFIER
26
+
27
+ The MIT licence above covers the Python source in this repository.
28
+
29
+ Since version 0.2.0, oats-scan bundles no binaries. The compiled classifier
30
+ it runs is part of pheo-oats, which oats-scan installs as a dependency;
31
+ pheo-oats is proprietary software of Pheo Inc., distributed under its own
32
+ terms. Versions before 0.2.0 bundled the same classifier under
33
+ src/oats_scan/_bin/, under the terms stated in those versions.
@@ -0,0 +1,214 @@
1
+ Metadata-Version: 2.4
2
+ Name: oats-scan
3
+ Version: 0.2.0
4
+ Summary: See what the agent skills on your machine tell an AI agent to run (the `oats scan` of pheo-oats)
5
+ Author-email: Pheo <hello@pheo.ai>
6
+ Project-URL: Homepage, https://pheo.ai
7
+ Project-URL: Source, https://github.com/pheo-ai/oats-scan
8
+ Project-URL: Issues, https://github.com/pheo-ai/oats-scan/issues
9
+ Keywords: ai,agents,agent-skills,security,governance,claude,cursor
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Security
15
+ Classifier: Topic :: Software Development :: Quality Assurance
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: pheo-oats>=0.7.1
20
+ Dynamic: license-file
21
+
22
+ # oats-scan
23
+
24
+ **See what the agent skills on your machine tell an AI agent to run.**
25
+
26
+ ```bash
27
+ pip install oats-scan
28
+ oats-scan
29
+ ```
30
+
31
+ Thirty seconds. No account, no sign up, no configuration, and nothing leaves
32
+ your machine.
33
+
34
+ **oats-scan is now the same scanner as `oats scan` in
35
+ [pheo-oats](https://pypi.org/project/pheo-oats/).** `pip install oats-scan`
36
+ installs pheo-oats, and `oats-scan` runs `oats scan` with the flags, defaults
37
+ and exit codes it always had, so the two print the same numbers. If you
38
+ already use pheo-oats, `oats scan` is all you need.
39
+
40
+ ## You already installed these
41
+
42
+ Agent skills are markdown files that tell an AI what commands to run on your
43
+ computer. You install them the way people installed browser extensions in 2010:
44
+ on a recommendation, without reading them.
45
+
46
+ Your agent reads them. You usually don't.
47
+
48
+ `oats-scan` reads them for you. On a working laptop it found 1,528 commands
49
+ in 4,331 files, and 19 of them in classes that can never run unattended.
50
+ Nineteen. In plugins from well known vendors, all of them legitimate. Here is
51
+ what a run looks like, on a single skill file:
52
+
53
+ ```
54
+ Scanning /home/you/skills
55
+
56
+ 1 files, 5 shell blocks. Classifying ...
57
+
58
+ What these instruct an agent to do
59
+
60
+ Remote code execution 1 never graduates
61
+ Credential access 1 never graduates
62
+ Shell command 3
63
+
64
+ 2 of 5 actions, in 2 classes, can never run unattended.
65
+ Those are the ones a person has to approve, every time.
66
+ ```
67
+
68
+ One of the laptop's nineteen is this line, sitting in an installed skill,
69
+ waiting for the agent to decide to run it:
70
+
71
+ ```bash
72
+ curl -fsSL https://downloads.cursor.com/origin/install.sh | sh
73
+ ```
74
+
75
+ Nothing there is an attack. That is the point. **You still want to know.**
76
+
77
+ ```bash
78
+ oats-scan --files # which skill each one came from
79
+ ```
80
+
81
+ ## Install
82
+
83
+ ```bash
84
+ pip install oats-scan
85
+ ```
86
+
87
+ Python 3.9 or newer. oats-scan itself is pure Python; it installs pheo-oats,
88
+ which carries the compiled classifier.
89
+
90
+ **Platform support** is pheo-oats': wheels for macOS (Apple silicon and
91
+ Intel), Linux x86-64, Linux aarch64 and Windows x86-64, and `pip` downloads
92
+ only yours.
93
+
94
+ **Working from source.** This repository holds the Python and the tests. To run
95
+ a source checkout, install pheo-oats for the classifier and put the checkout
96
+ ahead of it:
97
+
98
+ ```bash
99
+ git clone https://github.com/pheo-ai/oats-scan
100
+ cd oats-scan
101
+ pip install pheo-oats
102
+ PYTHONPATH=src python3 -m oats_scan --files
103
+ ```
104
+
105
+ Or point `OATS_SCAN_BIN_DIR` at a directory holding `pheo-action-gateway`,
106
+ `oatsctl` and `pheo-mcp-github`.
107
+
108
+ ## What it looks at
109
+
110
+ With no arguments it reads the agent skills installed on this machine:
111
+ `~/.claude`, `~/.cursor`, `~/.codex`, `~/.openclaw`, `~/.aider`, `~/.windsurf`,
112
+ `~/.gemini`, `~/.continue`, plus the directory you are standing in.
113
+
114
+ Give it a path to read somewhere specific:
115
+
116
+ ```bash
117
+ oats-scan ~/my-project
118
+ oats-scan --json # machine readable
119
+ oats-scan --strict # exit 1 if anything needs review, for CI
120
+ ```
121
+
122
+ ## How it decides
123
+
124
+ Every command is sorted into a **consequence class**, each carrying a severity
125
+ from 0 to 100. Classes at or above 75 are the ones that can never become
126
+ routine, however well an agent has behaved. Those are what `never graduates`
127
+ marks.
128
+
129
+ Some things you can take back:
130
+
131
+ - Agent writes a bad document, you fix the document
132
+ - Agent opens a pull request, you close it
133
+
134
+ Some you can't:
135
+
136
+ - Agent deletes a folder, the files are gone
137
+ - Agent pushes to production, your customers already saw it
138
+ - Agent reads your password file, it has your password now
139
+ - Agent runs a script off the internet, whatever it did, it did
140
+
141
+ The classifier is deterministic. No model, no inference, no network call. It
142
+ reads the command string and nothing else, so the same command produces the
143
+ same class today, next year, and on your machine. Any result can be re-derived
144
+ without re-running anything.
145
+
146
+ The full list of classes ships with pheo-oats as
147
+ `pheo_oats/data/action-classes.json`, the same list the Pheo space and the
148
+ OATS gateway rule with.
149
+
150
+ ## What it tells you, and what it leaves to you
151
+
152
+ `oats-scan` reports the **class** of each action. It does not decide whether
153
+ that action is acceptable, because that answer is yours.
154
+
155
+ A startup will let an agent install packages all day. A bank will not let one
156
+ read a credential file, ever. Both are right. They are different companies with
157
+ different blast radii. So this gives you the view and leaves the limit to you.
158
+
159
+ Everything it reports is normal software doing normal things. Installers
160
+ download and run code, because that is what installers are for. Some of those
161
+ actions happen to be ones you cannot undo, and those are the ones worth a look.
162
+
163
+ ## How it works with your other tools
164
+
165
+ `oats-scan` reads actions. Registry scanners read artifacts. The two see
166
+ different things and both are worth having.
167
+
168
+ Scanners are the right tool for harm that never becomes an action: a hardcoded
169
+ recipient, an undisclosed scope, an instruction written to talk an agent into
170
+ misbehaving. `oats-scan` is the right tool for what the agent then goes and
171
+ does. Run both and you cover both.
172
+
173
+ ## Honest limits
174
+
175
+ **These counts are a floor.** Only fenced shell content is read. Prose is
176
+ ignored, because "this skill can delete your notes" is a sentence rather than
177
+ an action. A block is skipped unless its first command is a recognised one. The
178
+ real number is what you see here or higher.
179
+
180
+ **Precision is 92%**, with a 95% confidence interval of [84.8, 96.5] on a
181
+ hand adjudicated sample of 100. The known failure mode is content that looks
182
+ like a command inside a heredoc. When that happens the extra line is visible in
183
+ `--files`, so you can see it and judge for yourself.
184
+
185
+ **Unresolved blocks are counted separately.** A block the classifier could not
186
+ read is reported on its own line rather than folded in with the clean ones.
187
+
188
+ ## Privacy
189
+
190
+ Everything runs on your machine. The classifier is a local process listening on
191
+ 127.0.0.1, started for the length of the scan and shut down afterwards. It
192
+ writes to a temporary database that is deleted when the scan ends, so a scan
193
+ never touches state you rely on. No telemetry, no account, no network calls.
194
+
195
+ ## Where this comes from
196
+
197
+ The classifier is the resolver from the
198
+ [Open Agent Trust System](https://github.com/pheo-ai/open-agent-trust-system),
199
+ the same one that runs in front of live agents deciding whether an action
200
+ executes. The measurement study behind the class list covers 66,192 public
201
+ agent skills, and the classification of all of them is published as
202
+ [pheo-ai/clawhub-consequence-classes](https://huggingface.co/datasets/pheo-ai/clawhub-consequence-classes).
203
+
204
+ Once you can see what your agent is told to do, the next question is usually
205
+ whether something can hold an action while you look at it. That is
206
+ [Pheo OATS](https://pheo.ai), the gateway this classifier normally lives in,
207
+ and it is already installed: `oats quickstart claude` starts it. `oats-scan` is
208
+ the view. The gateway is the brake. Start with the view.
209
+
210
+ ## Licence
211
+
212
+ The Python source in this repository is MIT. The compiled classifier is part of
213
+ pheo-oats, proprietary software of Pheo Inc. installed as a dependency under
214
+ its own terms. See [LICENSE](LICENSE).
@@ -0,0 +1,193 @@
1
+ # oats-scan
2
+
3
+ **See what the agent skills on your machine tell an AI agent to run.**
4
+
5
+ ```bash
6
+ pip install oats-scan
7
+ oats-scan
8
+ ```
9
+
10
+ Thirty seconds. No account, no sign up, no configuration, and nothing leaves
11
+ your machine.
12
+
13
+ **oats-scan is now the same scanner as `oats scan` in
14
+ [pheo-oats](https://pypi.org/project/pheo-oats/).** `pip install oats-scan`
15
+ installs pheo-oats, and `oats-scan` runs `oats scan` with the flags, defaults
16
+ and exit codes it always had, so the two print the same numbers. If you
17
+ already use pheo-oats, `oats scan` is all you need.
18
+
19
+ ## You already installed these
20
+
21
+ Agent skills are markdown files that tell an AI what commands to run on your
22
+ computer. You install them the way people installed browser extensions in 2010:
23
+ on a recommendation, without reading them.
24
+
25
+ Your agent reads them. You usually don't.
26
+
27
+ `oats-scan` reads them for you. On a working laptop it found 1,528 commands
28
+ in 4,331 files, and 19 of them in classes that can never run unattended.
29
+ Nineteen. In plugins from well known vendors, all of them legitimate. Here is
30
+ what a run looks like, on a single skill file:
31
+
32
+ ```
33
+ Scanning /home/you/skills
34
+
35
+ 1 files, 5 shell blocks. Classifying ...
36
+
37
+ What these instruct an agent to do
38
+
39
+ Remote code execution 1 never graduates
40
+ Credential access 1 never graduates
41
+ Shell command 3
42
+
43
+ 2 of 5 actions, in 2 classes, can never run unattended.
44
+ Those are the ones a person has to approve, every time.
45
+ ```
46
+
47
+ One of the laptop's nineteen is this line, sitting in an installed skill,
48
+ waiting for the agent to decide to run it:
49
+
50
+ ```bash
51
+ curl -fsSL https://downloads.cursor.com/origin/install.sh | sh
52
+ ```
53
+
54
+ Nothing there is an attack. That is the point. **You still want to know.**
55
+
56
+ ```bash
57
+ oats-scan --files # which skill each one came from
58
+ ```
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install oats-scan
64
+ ```
65
+
66
+ Python 3.9 or newer. oats-scan itself is pure Python; it installs pheo-oats,
67
+ which carries the compiled classifier.
68
+
69
+ **Platform support** is pheo-oats': wheels for macOS (Apple silicon and
70
+ Intel), Linux x86-64, Linux aarch64 and Windows x86-64, and `pip` downloads
71
+ only yours.
72
+
73
+ **Working from source.** This repository holds the Python and the tests. To run
74
+ a source checkout, install pheo-oats for the classifier and put the checkout
75
+ ahead of it:
76
+
77
+ ```bash
78
+ git clone https://github.com/pheo-ai/oats-scan
79
+ cd oats-scan
80
+ pip install pheo-oats
81
+ PYTHONPATH=src python3 -m oats_scan --files
82
+ ```
83
+
84
+ Or point `OATS_SCAN_BIN_DIR` at a directory holding `pheo-action-gateway`,
85
+ `oatsctl` and `pheo-mcp-github`.
86
+
87
+ ## What it looks at
88
+
89
+ With no arguments it reads the agent skills installed on this machine:
90
+ `~/.claude`, `~/.cursor`, `~/.codex`, `~/.openclaw`, `~/.aider`, `~/.windsurf`,
91
+ `~/.gemini`, `~/.continue`, plus the directory you are standing in.
92
+
93
+ Give it a path to read somewhere specific:
94
+
95
+ ```bash
96
+ oats-scan ~/my-project
97
+ oats-scan --json # machine readable
98
+ oats-scan --strict # exit 1 if anything needs review, for CI
99
+ ```
100
+
101
+ ## How it decides
102
+
103
+ Every command is sorted into a **consequence class**, each carrying a severity
104
+ from 0 to 100. Classes at or above 75 are the ones that can never become
105
+ routine, however well an agent has behaved. Those are what `never graduates`
106
+ marks.
107
+
108
+ Some things you can take back:
109
+
110
+ - Agent writes a bad document, you fix the document
111
+ - Agent opens a pull request, you close it
112
+
113
+ Some you can't:
114
+
115
+ - Agent deletes a folder, the files are gone
116
+ - Agent pushes to production, your customers already saw it
117
+ - Agent reads your password file, it has your password now
118
+ - Agent runs a script off the internet, whatever it did, it did
119
+
120
+ The classifier is deterministic. No model, no inference, no network call. It
121
+ reads the command string and nothing else, so the same command produces the
122
+ same class today, next year, and on your machine. Any result can be re-derived
123
+ without re-running anything.
124
+
125
+ The full list of classes ships with pheo-oats as
126
+ `pheo_oats/data/action-classes.json`, the same list the Pheo space and the
127
+ OATS gateway rule with.
128
+
129
+ ## What it tells you, and what it leaves to you
130
+
131
+ `oats-scan` reports the **class** of each action. It does not decide whether
132
+ that action is acceptable, because that answer is yours.
133
+
134
+ A startup will let an agent install packages all day. A bank will not let one
135
+ read a credential file, ever. Both are right. They are different companies with
136
+ different blast radii. So this gives you the view and leaves the limit to you.
137
+
138
+ Everything it reports is normal software doing normal things. Installers
139
+ download and run code, because that is what installers are for. Some of those
140
+ actions happen to be ones you cannot undo, and those are the ones worth a look.
141
+
142
+ ## How it works with your other tools
143
+
144
+ `oats-scan` reads actions. Registry scanners read artifacts. The two see
145
+ different things and both are worth having.
146
+
147
+ Scanners are the right tool for harm that never becomes an action: a hardcoded
148
+ recipient, an undisclosed scope, an instruction written to talk an agent into
149
+ misbehaving. `oats-scan` is the right tool for what the agent then goes and
150
+ does. Run both and you cover both.
151
+
152
+ ## Honest limits
153
+
154
+ **These counts are a floor.** Only fenced shell content is read. Prose is
155
+ ignored, because "this skill can delete your notes" is a sentence rather than
156
+ an action. A block is skipped unless its first command is a recognised one. The
157
+ real number is what you see here or higher.
158
+
159
+ **Precision is 92%**, with a 95% confidence interval of [84.8, 96.5] on a
160
+ hand adjudicated sample of 100. The known failure mode is content that looks
161
+ like a command inside a heredoc. When that happens the extra line is visible in
162
+ `--files`, so you can see it and judge for yourself.
163
+
164
+ **Unresolved blocks are counted separately.** A block the classifier could not
165
+ read is reported on its own line rather than folded in with the clean ones.
166
+
167
+ ## Privacy
168
+
169
+ Everything runs on your machine. The classifier is a local process listening on
170
+ 127.0.0.1, started for the length of the scan and shut down afterwards. It
171
+ writes to a temporary database that is deleted when the scan ends, so a scan
172
+ never touches state you rely on. No telemetry, no account, no network calls.
173
+
174
+ ## Where this comes from
175
+
176
+ The classifier is the resolver from the
177
+ [Open Agent Trust System](https://github.com/pheo-ai/open-agent-trust-system),
178
+ the same one that runs in front of live agents deciding whether an action
179
+ executes. The measurement study behind the class list covers 66,192 public
180
+ agent skills, and the classification of all of them is published as
181
+ [pheo-ai/clawhub-consequence-classes](https://huggingface.co/datasets/pheo-ai/clawhub-consequence-classes).
182
+
183
+ Once you can see what your agent is told to do, the next question is usually
184
+ whether something can hold an action while you look at it. That is
185
+ [Pheo OATS](https://pheo.ai), the gateway this classifier normally lives in,
186
+ and it is already installed: `oats quickstart claude` starts it. `oats-scan` is
187
+ the view. The gateway is the brake. Start with the view.
188
+
189
+ ## Licence
190
+
191
+ The Python source in this repository is MIT. The compiled classifier is part of
192
+ pheo-oats, proprietary software of Pheo Inc. installed as a dependency under
193
+ its own terms. See [LICENSE](LICENSE).
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "oats-scan"
7
+ version = "0.2.0"
8
+ description = "See what the agent skills on your machine tell an AI agent to run (the `oats scan` of pheo-oats)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ authors = [{name = "Pheo", email = "hello@pheo.ai"}]
12
+ keywords = ["ai", "agents", "agent-skills", "security", "governance", "claude", "cursor"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Environment :: Console",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Topic :: Security",
19
+ "Topic :: Software Development :: Quality Assurance",
20
+ ]
21
+ # The scanner and its classifier are pheo-oats'. 0.7.1 is the first that
22
+ # does everything oats-scan did (see pheo-oats' CHANGELOG).
23
+ dependencies = ["pheo-oats>=0.7.1"]
24
+
25
+ [project.urls]
26
+ Homepage = "https://pheo.ai"
27
+ Source = "https://github.com/pheo-ai/oats-scan"
28
+ Issues = "https://github.com/pheo-ai/oats-scan/issues"
29
+
30
+ [project.scripts]
31
+ oats-scan = "oats_scan.cli:main"
32
+
33
+ [tool.setuptools]
34
+ package-dir = {"" = "src"}
35
+
36
+ [tool.setuptools.packages.find]
37
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,9 @@
1
+ try:
2
+ from importlib.metadata import PackageNotFoundError, version as _version
3
+
4
+ try:
5
+ __version__ = _version("oats-scan")
6
+ except PackageNotFoundError:
7
+ __version__ = "0.0.0+source"
8
+ except ImportError:
9
+ __version__ = "0.0.0+source"
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from oats_scan.cli import main
4
+
5
+ sys.exit(main())
@@ -0,0 +1,90 @@
1
+ """Command line entry point for oats-scan.
2
+
3
+ oats-scan is `oats scan` from pheo-oats under its old name: the same
4
+ scanner, so the two print the same numbers, with the flags, defaults and
5
+ exit codes oats-scan has always had (0 clean, 1 when --strict finds
6
+ something that needs review, 2 when the scan could not run, 130 on
7
+ Ctrl-C).
8
+ """
9
+ import argparse
10
+ import os
11
+ import sys
12
+
13
+ from oats_scan import __version__
14
+ from oats_scan.gateway import ScanError
15
+
16
+
17
+ def build_parser():
18
+ parser = argparse.ArgumentParser(
19
+ prog="oats-scan",
20
+ description=(
21
+ "Show what the agent skills on this machine instruct an AI agent "
22
+ "to run, sorted by what kind of effect each command has. The same "
23
+ "scanner as `oats scan` in pheo-oats."
24
+ ),
25
+ epilog=(
26
+ "examples:\n"
27
+ "\n"
28
+ " oats-scan # skills installed on this machine\n"
29
+ " oats-scan --files # and which file each one came from\n"
30
+ " oats-scan ~/my-project # a specific directory\n"
31
+ " oats-scan --json # machine readable\n"
32
+ "\n"
33
+ "Nothing is installed, nothing is changed, and nothing leaves\n"
34
+ "this machine.\n"
35
+ ),
36
+ formatter_class=argparse.RawDescriptionHelpFormatter,
37
+ )
38
+ parser.add_argument(
39
+ "path", nargs="?", default=None,
40
+ help="Directory to scan. Default: agent skills installed on this "
41
+ "machine, plus the current directory.",
42
+ )
43
+ parser.add_argument("--files", action="store_true",
44
+ help="List which files carry the actions worth reviewing")
45
+ parser.add_argument("--json", action="store_true", help="Machine readable output")
46
+ parser.add_argument("--strict", action="store_true",
47
+ help="Exit 1 if anything needs review, for CI")
48
+ parser.add_argument("--workers", type=int, default=8,
49
+ help="Parallel classifications (default 8)")
50
+ parser.add_argument("--version", action="version", version=__version__)
51
+ return parser
52
+
53
+
54
+ def main(argv=None):
55
+ try:
56
+ sys.stdout.reconfigure(line_buffering=True)
57
+ except (AttributeError, ValueError):
58
+ pass
59
+ args = build_parser().parse_args(argv)
60
+ # OATS_SCAN_BIN_DIR keeps pointing the scan at its binaries, now that
61
+ # they are pheo-oats' and pheo-oats looks for PHEO_OATS_BIN_DIR.
62
+ if os.environ.get("OATS_SCAN_BIN_DIR") and not os.environ.get("PHEO_OATS_BIN_DIR"):
63
+ os.environ["PHEO_OATS_BIN_DIR"] = os.environ["OATS_SCAN_BIN_DIR"]
64
+ try:
65
+ from pheo_oats.cli import OatsError
66
+ except ImportError: # no pheo-oats: importing the scan says so
67
+ OatsError = ScanError
68
+
69
+ try:
70
+ from oats_scan.scan import run
71
+
72
+ return run(args)
73
+ except (ScanError, OatsError) as error:
74
+ print("\n{}".format(error), file=sys.stderr)
75
+ return 2
76
+ except SystemExit as error:
77
+ # pheo-oats reports a missing binary, or a classifier that would not
78
+ # start, as SystemExit with a sentence. Here that is exit status 2,
79
+ # which --strict never uses, so CI can tell "could not scan" from
80
+ # "found something".
81
+ if isinstance(error.code, str):
82
+ print("\n{}".format(error.code), file=sys.stderr)
83
+ return 2
84
+ raise
85
+ except KeyboardInterrupt:
86
+ return 130
87
+
88
+
89
+ if __name__ == "__main__":
90
+ sys.exit(main())