propre-cli 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.
- propre_cli-0.1.0/LICENSE +21 -0
- propre_cli-0.1.0/PKG-INFO +211 -0
- propre_cli-0.1.0/README.md +173 -0
- propre_cli-0.1.0/propre/__init__.py +5 -0
- propre_cli-0.1.0/propre/__main__.py +4 -0
- propre_cli-0.1.0/propre/cli.py +202 -0
- propre_cli-0.1.0/propre/config.py +106 -0
- propre_cli-0.1.0/propre/constants.py +45 -0
- propre_cli-0.1.0/propre/context.py +55 -0
- propre_cli-0.1.0/propre/engine.py +40 -0
- propre_cli-0.1.0/propre/models.py +159 -0
- propre_cli-0.1.0/propre/phases/__init__.py +18 -0
- propre_cli-0.1.0/propre/phases/base.py +14 -0
- propre_cli-0.1.0/propre/phases/harden.py +466 -0
- propre_cli-0.1.0/propre/phases/restructure.py +310 -0
- propre_cli-0.1.0/propre/phases/scan.py +194 -0
- propre_cli-0.1.0/propre/phases/secrets.py +273 -0
- propre_cli-0.1.0/propre/phases/ship.py +205 -0
- propre_cli-0.1.0/propre/reporters/__init__.py +1 -0
- propre_cli-0.1.0/propre/reporters/renderers.py +183 -0
- propre_cli-0.1.0/propre/utils/__init__.py +1 -0
- propre_cli-0.1.0/propre/utils/entropy.py +26 -0
- propre_cli-0.1.0/propre/utils/fs.py +66 -0
- propre_cli-0.1.0/propre/utils/imports.py +34 -0
- propre_cli-0.1.0/propre/utils/process.py +22 -0
- propre_cli-0.1.0/propre/utils/transaction.py +48 -0
- propre_cli-0.1.0/propre/utils/tree_sitter.py +21 -0
- propre_cli-0.1.0/propre_cli.egg-info/PKG-INFO +211 -0
- propre_cli-0.1.0/propre_cli.egg-info/SOURCES.txt +33 -0
- propre_cli-0.1.0/propre_cli.egg-info/dependency_links.txt +1 -0
- propre_cli-0.1.0/propre_cli.egg-info/entry_points.txt +2 -0
- propre_cli-0.1.0/propre_cli.egg-info/requires.txt +7 -0
- propre_cli-0.1.0/propre_cli.egg-info/top_level.txt +1 -0
- propre_cli-0.1.0/pyproject.toml +75 -0
- propre_cli-0.1.0/setup.cfg +4 -0
propre_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 immerSIR
|
|
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,211 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: propre-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Analyze, restructure, and harden vibe-coded projects for production. Architecture-level linting, secret detection, and code quality enforcement in one CLI.
|
|
5
|
+
Author-email: immerSIR <immersir223@gmail.com>
|
|
6
|
+
Maintainer-email: immerSIR <immersir223@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/immerSIR/propre-cli
|
|
9
|
+
Project-URL: Documentation, https://github.com/immerSIR/propre-cli#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/immerSIR/propre-cli
|
|
11
|
+
Project-URL: Issues, https://github.com/immerSIR/propre-cli/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/immerSIR/propre-cli/blob/main/CHANGELOG.md
|
|
13
|
+
Keywords: cli,code-quality,security,secret-detection,refactoring,restructuring,developer-tools,architecture,linting,production-readiness,vibe-coding
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
26
|
+
Classifier: Topic :: Software Development :: Testing
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
32
|
+
Requires-Dist: rich>=13.7.1
|
|
33
|
+
Requires-Dist: typer>=0.12.5
|
|
34
|
+
Provides-Extra: parsers
|
|
35
|
+
Requires-Dist: tree-sitter>=0.22.3; extra == "parsers"
|
|
36
|
+
Requires-Dist: tree-sitter-languages>=1.10.2; extra == "parsers"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# Propre
|
|
40
|
+
|
|
41
|
+
**Propre** is a post-vibe-coding cleanup and hardening CLI.
|
|
42
|
+
|
|
43
|
+
It analyzes AI-generated or rapidly prototyped codebases and prepares them for real-world use by detecting structural issues, unsafe patterns, and production blockers — then optionally fixing them.
|
|
44
|
+
|
|
45
|
+
> From messy prototype → production-ready project.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install propre
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
Analyze a project:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
propre scan .
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Auto-fix safe issues:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
propre fix .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Check production readiness:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
propre ship .
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Generate a report:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
propre report . -o report.md
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Commands
|
|
86
|
+
|
|
87
|
+
| Command | Description |
|
|
88
|
+
| ------------------------------ | ------------------------------ |
|
|
89
|
+
| `propre scan [path]` | Full analysis (no changes) |
|
|
90
|
+
| `propre fix [path]` | Auto-fix safe issues |
|
|
91
|
+
| `propre res [path]` | Project restructuring only |
|
|
92
|
+
| `propre sec [path]` | Secret scanning only |
|
|
93
|
+
| `propre ship [path]` | Production readiness checklist |
|
|
94
|
+
| `propre report [path] -o file` | Export full report |
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Common Workflows
|
|
100
|
+
|
|
101
|
+
### Before committing AI-generated code
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
propre scan .
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Before opening a pull request
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
propre fix .
|
|
111
|
+
propre ship .
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### CI safety check
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
propre scan . --ci
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Security audit
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
propre sec . --deep-scan
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Global Flags
|
|
129
|
+
|
|
130
|
+
| Flag | Description | | | |
|
|
131
|
+
| --------------------- | -------------------------------------- | ---- | ------ | ------------- |
|
|
132
|
+
| `--dry-run` | Preview changes without applying | | | |
|
|
133
|
+
| `--verbose` / `-v` | Detailed output | | | |
|
|
134
|
+
| `--config propre.yml` | Custom rules | | | |
|
|
135
|
+
| `--fix` | Apply safe fixes outside `fix` command | | | |
|
|
136
|
+
| `--ignore <pattern>` | Exclude paths | | | |
|
|
137
|
+
| `--ci` | Exit non-zero if blockers found | | | |
|
|
138
|
+
| `--format terminal | md | json | sarif` | Output format |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Configuration
|
|
143
|
+
|
|
144
|
+
Create a `propre.yml` at your project root:
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
stack: auto
|
|
148
|
+
|
|
149
|
+
restructure:
|
|
150
|
+
enabled: true
|
|
151
|
+
confirm: true
|
|
152
|
+
|
|
153
|
+
secrets:
|
|
154
|
+
deep_scan: false
|
|
155
|
+
severity_threshold: medium
|
|
156
|
+
|
|
157
|
+
rules:
|
|
158
|
+
dead_code: warn
|
|
159
|
+
console_logs: error
|
|
160
|
+
missing_types: warn
|
|
161
|
+
hardcoded_config: error
|
|
162
|
+
|
|
163
|
+
ignore:
|
|
164
|
+
- node_modules/
|
|
165
|
+
- .git/
|
|
166
|
+
- dist/
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## What Propre Detects
|
|
172
|
+
|
|
173
|
+
- Dead code & unused files
|
|
174
|
+
- Debug logs & leftover prints
|
|
175
|
+
- Missing typing
|
|
176
|
+
- Hardcoded configuration
|
|
177
|
+
- Project structure issues
|
|
178
|
+
- Secrets & credentials
|
|
179
|
+
- Production blockers
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## CI Integration
|
|
184
|
+
|
|
185
|
+
Example GitHub Action step:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
propre scan . --ci
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The command exits with a non-zero code if blocking issues are found.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Philosophy
|
|
196
|
+
|
|
197
|
+
Modern coding workflows generate code faster than they validate it.
|
|
198
|
+
|
|
199
|
+
Propre acts as the **final safety layer** between experimentation and deployment:
|
|
200
|
+
|
|
201
|
+
- AI coding assistants create
|
|
202
|
+
- Developers iterate
|
|
203
|
+
- **Propre hardens**
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT
|
|
210
|
+
|
|
211
|
+
---
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Propre
|
|
2
|
+
|
|
3
|
+
**Propre** is a post-vibe-coding cleanup and hardening CLI.
|
|
4
|
+
|
|
5
|
+
It analyzes AI-generated or rapidly prototyped codebases and prepares them for real-world use by detecting structural issues, unsafe patterns, and production blockers — then optionally fixing them.
|
|
6
|
+
|
|
7
|
+
> From messy prototype → production-ready project.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install propre
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
Analyze a project:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
propre scan .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Auto-fix safe issues:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
propre fix .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Check production readiness:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
propre ship .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Generate a report:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
propre report . -o report.md
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Commands
|
|
48
|
+
|
|
49
|
+
| Command | Description |
|
|
50
|
+
| ------------------------------ | ------------------------------ |
|
|
51
|
+
| `propre scan [path]` | Full analysis (no changes) |
|
|
52
|
+
| `propre fix [path]` | Auto-fix safe issues |
|
|
53
|
+
| `propre res [path]` | Project restructuring only |
|
|
54
|
+
| `propre sec [path]` | Secret scanning only |
|
|
55
|
+
| `propre ship [path]` | Production readiness checklist |
|
|
56
|
+
| `propre report [path] -o file` | Export full report |
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Common Workflows
|
|
62
|
+
|
|
63
|
+
### Before committing AI-generated code
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
propre scan .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Before opening a pull request
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
propre fix .
|
|
73
|
+
propre ship .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### CI safety check
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
propre scan . --ci
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Security audit
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
propre sec . --deep-scan
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Global Flags
|
|
91
|
+
|
|
92
|
+
| Flag | Description | | | |
|
|
93
|
+
| --------------------- | -------------------------------------- | ---- | ------ | ------------- |
|
|
94
|
+
| `--dry-run` | Preview changes without applying | | | |
|
|
95
|
+
| `--verbose` / `-v` | Detailed output | | | |
|
|
96
|
+
| `--config propre.yml` | Custom rules | | | |
|
|
97
|
+
| `--fix` | Apply safe fixes outside `fix` command | | | |
|
|
98
|
+
| `--ignore <pattern>` | Exclude paths | | | |
|
|
99
|
+
| `--ci` | Exit non-zero if blockers found | | | |
|
|
100
|
+
| `--format terminal | md | json | sarif` | Output format |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Configuration
|
|
105
|
+
|
|
106
|
+
Create a `propre.yml` at your project root:
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
stack: auto
|
|
110
|
+
|
|
111
|
+
restructure:
|
|
112
|
+
enabled: true
|
|
113
|
+
confirm: true
|
|
114
|
+
|
|
115
|
+
secrets:
|
|
116
|
+
deep_scan: false
|
|
117
|
+
severity_threshold: medium
|
|
118
|
+
|
|
119
|
+
rules:
|
|
120
|
+
dead_code: warn
|
|
121
|
+
console_logs: error
|
|
122
|
+
missing_types: warn
|
|
123
|
+
hardcoded_config: error
|
|
124
|
+
|
|
125
|
+
ignore:
|
|
126
|
+
- node_modules/
|
|
127
|
+
- .git/
|
|
128
|
+
- dist/
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## What Propre Detects
|
|
134
|
+
|
|
135
|
+
- Dead code & unused files
|
|
136
|
+
- Debug logs & leftover prints
|
|
137
|
+
- Missing typing
|
|
138
|
+
- Hardcoded configuration
|
|
139
|
+
- Project structure issues
|
|
140
|
+
- Secrets & credentials
|
|
141
|
+
- Production blockers
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## CI Integration
|
|
146
|
+
|
|
147
|
+
Example GitHub Action step:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
propre scan . --ci
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The command exits with a non-zero code if blocking issues are found.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Philosophy
|
|
158
|
+
|
|
159
|
+
Modern coding workflows generate code faster than they validate it.
|
|
160
|
+
|
|
161
|
+
Propre acts as the **final safety layer** between experimentation and deployment:
|
|
162
|
+
|
|
163
|
+
- AI coding assistants create
|
|
164
|
+
- Developers iterate
|
|
165
|
+
- **Propre hardens**
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
|
172
|
+
|
|
173
|
+
---
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from propre.config import load_config
|
|
9
|
+
from propre.context import RunContext, RuntimeOptions
|
|
10
|
+
from propre.engine import PropreEngine
|
|
11
|
+
from propre.models import OutputFormat
|
|
12
|
+
from propre.reporters.renderers import emit_report
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(
|
|
15
|
+
add_completion=False,
|
|
16
|
+
help="Propre: post-vibe-coding architecture cleanup and hardening CLI.",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _resolve_phases(command: str, restructure_enabled: bool) -> list[str]:
|
|
21
|
+
if command in {"scan", "report"}:
|
|
22
|
+
phases = ["scan"]
|
|
23
|
+
if restructure_enabled:
|
|
24
|
+
phases.append("restructure")
|
|
25
|
+
phases.extend(["secrets", "harden", "ship"])
|
|
26
|
+
return phases
|
|
27
|
+
if command == "fix":
|
|
28
|
+
phases = ["scan"]
|
|
29
|
+
if restructure_enabled:
|
|
30
|
+
phases.append("restructure")
|
|
31
|
+
phases.extend(["secrets", "harden", "ship"])
|
|
32
|
+
return phases
|
|
33
|
+
if command == "restructure":
|
|
34
|
+
return ["scan", "restructure"]
|
|
35
|
+
if command == "secrets":
|
|
36
|
+
return ["secrets"]
|
|
37
|
+
if command == "ship":
|
|
38
|
+
return ["ship"]
|
|
39
|
+
raise ValueError(f"Unknown command: {command}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _build_context(
|
|
43
|
+
command: str,
|
|
44
|
+
path: Path,
|
|
45
|
+
global_options: dict[str, Any],
|
|
46
|
+
output_path: Path | None,
|
|
47
|
+
deep_scan: bool | None,
|
|
48
|
+
) -> RunContext:
|
|
49
|
+
project_path = path.resolve()
|
|
50
|
+
config_path = Path(global_options["config"]).resolve() if global_options.get("config") else None
|
|
51
|
+
loaded = load_config(project_path, config_path)
|
|
52
|
+
|
|
53
|
+
auto_fix = bool(global_options["fix"] or command in {"fix", "restructure"})
|
|
54
|
+
|
|
55
|
+
runtime = RuntimeOptions(
|
|
56
|
+
command=command,
|
|
57
|
+
dry_run=bool(global_options["dry_run"]),
|
|
58
|
+
verbose=bool(global_options["verbose"]),
|
|
59
|
+
auto_fix=auto_fix,
|
|
60
|
+
ci=bool(global_options["ci"]),
|
|
61
|
+
output_format=OutputFormat(global_options["format"]),
|
|
62
|
+
output_path=output_path,
|
|
63
|
+
ignore=list(global_options["ignore"]),
|
|
64
|
+
deep_scan=deep_scan,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return RunContext(project_path=project_path, loaded_config=loaded, options=runtime)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _run(
|
|
71
|
+
ctx: typer.Context,
|
|
72
|
+
command: str,
|
|
73
|
+
path: Path,
|
|
74
|
+
output_path: Path | None = None,
|
|
75
|
+
deep_scan: bool | None = None,
|
|
76
|
+
) -> None:
|
|
77
|
+
runtime_ctx = _build_context(command, path, ctx.obj, output_path, deep_scan)
|
|
78
|
+
|
|
79
|
+
if runtime_ctx.options.verbose:
|
|
80
|
+
config_source = runtime_ctx.loaded_config.source or "defaults"
|
|
81
|
+
runtime_ctx.console.print(f"[cyan]Config:[/cyan] {config_source}")
|
|
82
|
+
|
|
83
|
+
engine = PropreEngine(runtime_ctx)
|
|
84
|
+
phases = _resolve_phases(command, runtime_ctx.config.restructure.enabled)
|
|
85
|
+
report = engine.run(phases)
|
|
86
|
+
|
|
87
|
+
emit_report(
|
|
88
|
+
report,
|
|
89
|
+
output_format=runtime_ctx.options.output_format.value,
|
|
90
|
+
console=runtime_ctx.console,
|
|
91
|
+
output_path=runtime_ctx.options.output_path,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if runtime_ctx.options.ci:
|
|
95
|
+
failed_checks = any(not check.passed for phase in report.phases for check in phase.checks)
|
|
96
|
+
if report.has_blockers() or failed_checks:
|
|
97
|
+
raise typer.Exit(code=1)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@app.callback()
|
|
101
|
+
def common_options(
|
|
102
|
+
ctx: typer.Context,
|
|
103
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Preview changes without applying them."),
|
|
104
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output."),
|
|
105
|
+
config: Path | None = typer.Option(None, "--config", help="Path to propre.yml config file."),
|
|
106
|
+
fix: bool = typer.Option(False, "--fix", help="Auto-apply safe fixes."),
|
|
107
|
+
ignore: list[str] = typer.Option([], "--ignore", help="Ignore glob/prefix pattern."),
|
|
108
|
+
ci: bool = typer.Option(False, "--ci", help="Exit non-zero on failures/blockers."),
|
|
109
|
+
format: OutputFormat = typer.Option(OutputFormat.TERMINAL, "--format", help="Output format."),
|
|
110
|
+
) -> None:
|
|
111
|
+
ctx.obj = {
|
|
112
|
+
"dry_run": dry_run,
|
|
113
|
+
"verbose": verbose,
|
|
114
|
+
"config": str(config) if config else None,
|
|
115
|
+
"fix": fix,
|
|
116
|
+
"ignore": ignore,
|
|
117
|
+
"ci": ci,
|
|
118
|
+
"format": format.value,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@app.command()
|
|
123
|
+
def scan(
|
|
124
|
+
ctx: typer.Context,
|
|
125
|
+
path: Path = typer.Argument(Path("."), exists=True, file_okay=False, dir_okay=True),
|
|
126
|
+
deep_scan: bool | None = typer.Option(
|
|
127
|
+
None,
|
|
128
|
+
"--deep-scan/--no-deep-scan",
|
|
129
|
+
help="Also scan git history for secrets.",
|
|
130
|
+
),
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Full analysis, no changes unless --fix is set."""
|
|
133
|
+
_run(ctx, "scan", path, deep_scan=deep_scan)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@app.command()
|
|
137
|
+
def fix(
|
|
138
|
+
ctx: typer.Context,
|
|
139
|
+
path: Path = typer.Argument(Path("."), exists=True, file_okay=False, dir_okay=True),
|
|
140
|
+
deep_scan: bool | None = typer.Option(
|
|
141
|
+
None,
|
|
142
|
+
"--deep-scan/--no-deep-scan",
|
|
143
|
+
help="Also scan git history for secrets.",
|
|
144
|
+
),
|
|
145
|
+
) -> None:
|
|
146
|
+
"""Auto-fix safe issues where possible."""
|
|
147
|
+
_run(ctx, "fix", path, deep_scan=deep_scan)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@app.command("res")
|
|
151
|
+
def restructure(
|
|
152
|
+
ctx: typer.Context,
|
|
153
|
+
path: Path = typer.Argument(Path("."), exists=True, file_okay=False, dir_okay=True),
|
|
154
|
+
) -> None:
|
|
155
|
+
"""Folder restructuring only."""
|
|
156
|
+
_run(ctx, "restructure", path)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@app.command("sec")
|
|
160
|
+
def secrets(
|
|
161
|
+
ctx: typer.Context,
|
|
162
|
+
path: Path = typer.Argument(Path("."), exists=True, file_okay=False, dir_okay=True),
|
|
163
|
+
deep_scan: bool | None = typer.Option(
|
|
164
|
+
None,
|
|
165
|
+
"--deep-scan/--no-deep-scan",
|
|
166
|
+
help="Also scan git history for secrets.",
|
|
167
|
+
),
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Secret scanning only."""
|
|
170
|
+
_run(ctx, "secrets", path, deep_scan=deep_scan)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@app.command()
|
|
174
|
+
def ship(
|
|
175
|
+
ctx: typer.Context,
|
|
176
|
+
path: Path = typer.Argument(Path("."), exists=True, file_okay=False, dir_okay=True),
|
|
177
|
+
) -> None:
|
|
178
|
+
"""Production readiness checklist."""
|
|
179
|
+
_run(ctx, "ship", path)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@app.command()
|
|
183
|
+
def report(
|
|
184
|
+
ctx: typer.Context,
|
|
185
|
+
path: Path = typer.Argument(Path("."), exists=True, file_okay=False, dir_okay=True),
|
|
186
|
+
output: Path = typer.Option(Path("report.md"), "-o", "--output", help="Write report to this file."),
|
|
187
|
+
deep_scan: bool | None = typer.Option(
|
|
188
|
+
None,
|
|
189
|
+
"--deep-scan/--no-deep-scan",
|
|
190
|
+
help="Also scan git history for secrets.",
|
|
191
|
+
),
|
|
192
|
+
) -> None:
|
|
193
|
+
"""Export a full report file."""
|
|
194
|
+
_run(ctx, "report", path, output_path=output, deep_scan=deep_scan)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def run() -> None:
|
|
198
|
+
app()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
run()
|