codex-workspace-bootstrap 0.4.0__tar.gz → 0.5.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.
- {codex_workspace_bootstrap-0.4.0/src/codex_workspace_bootstrap.egg-info → codex_workspace_bootstrap-0.5.0}/PKG-INFO +42 -5
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/README.md +41 -4
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/pyproject.toml +1 -1
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/__init__.py +1 -1
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/cli.py +95 -4
- codex_workspace_bootstrap-0.5.0/src/codex_workspace_bootstrap/fixes.py +72 -0
- codex_workspace_bootstrap-0.5.0/src/codex_workspace_bootstrap/instructions.py +654 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/preflight.py +61 -53
- codex_workspace_bootstrap-0.5.0/src/codex_workspace_bootstrap/sarif.py +205 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0/src/codex_workspace_bootstrap.egg-info}/PKG-INFO +42 -5
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap.egg-info/SOURCES.txt +4 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/tests/test_cli.py +33 -1
- codex_workspace_bootstrap-0.5.0/tests/test_instruction_integrity.py +381 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/tests/test_preflight.py +60 -0
- codex_workspace_bootstrap-0.5.0/tests/test_public_repo_patterns.py +72 -0
- codex_workspace_bootstrap-0.5.0/tests/test_sarif.py +96 -0
- codex_workspace_bootstrap-0.4.0/src/codex_workspace_bootstrap/sarif.py +0 -83
- codex_workspace_bootstrap-0.4.0/tests/test_sarif.py +0 -44
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/LICENSE +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/setup.cfg +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/__main__.py +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/agents.py +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/audit.py +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/doctor.py +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap.egg-info/dependency_links.txt +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap.egg-info/entry_points.txt +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap.egg-info/top_level.txt +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/tests/test_audit.py +0 -0
- {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/tests/test_doctor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-workspace-bootstrap
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Preflight AI coding repositories before Codex, Copilot, Cline, Claude, Gemini, Continue, or Cursor touches them.
|
|
5
5
|
Author: kohli217
|
|
6
6
|
License: MIT
|
|
@@ -35,7 +35,7 @@ Dynamic: license-file
|
|
|
35
35
|
[](LICENSE)
|
|
36
36
|
[](pyproject.toml)
|
|
37
37
|
|
|
38
|
-
**Preflight your repository before an AI coding agent touches it.**
|
|
38
|
+
**Preflight your repository before an AI coding agent touches it — and catch instruction drift before different agents follow different rules.**
|
|
39
39
|
|
|
40
40
|
Windows-first. Local by default. CI-friendly. Designed for repositories used with Codex, Copilot, Cline, Claude Code, Gemini CLI, Continue, Cursor, and similar coding agents.
|
|
41
41
|
|
|
@@ -58,6 +58,7 @@ State: NEEDS ATTENTION
|
|
|
58
58
|
Project: Python
|
|
59
59
|
AI instructions: none detected
|
|
60
60
|
Audit: 10 passed, 4 warnings, 0 blocking
|
|
61
|
+
Instruction integrity: 0 findings, 0 drift, 0 invalid commands, 0 metadata
|
|
61
62
|
Next actions:
|
|
62
63
|
[P1] Add repository instructions for AI coding agents -> cwb init-agents .
|
|
63
64
|
[P2] Make the Codex CLI available when local Codex workflows are intended -> codex --version
|
|
@@ -101,6 +102,17 @@ The preflight detects repository instruction/config signals for:
|
|
|
101
102
|
|
|
102
103
|
It does not claim these files are correct merely because they exist. It tells you what was detected so a maintainer can review the actual instructions.
|
|
103
104
|
|
|
105
|
+
### Cross-agent instruction integrity
|
|
106
|
+
|
|
107
|
+
When multiple AI instruction files exist, `cwb` reads executable-looking commands and checks them against repository evidence. It understands nested `AGENTS.md` / `AGENTS.override.md`, nested Cursor `.cursor/rules`, and common path-specific frontmatter such as Copilot `applyTo` and rule `globs`. It can flag:
|
|
108
|
+
|
|
109
|
+
- package-manager mismatches against `packageManager` and lockfiles;
|
|
110
|
+
- cross-agent package-manager drift;
|
|
111
|
+
- missing `package.json` scripts referenced by instructions;
|
|
112
|
+
- conflicting test/lint/build validation commands when instruction files have no shared command for the same validation family.
|
|
113
|
+
|
|
114
|
+
The lint is intentionally conservative: different files may contain additional commands without being treated as conflicts when they share a compatible validation baseline. Commands from different scopes are not compared as if they were global rules. Path-specific rules are validated individually against repository evidence but are not cross-compared for drift unless their full selector semantics can be represented safely. A repository is not marked READY when it only has nested/path-specific instructions and no repository-wide instruction baseline.
|
|
115
|
+
|
|
104
116
|
### Risk signals
|
|
105
117
|
|
|
106
118
|
The audit warns about common secret-bearing filenames without printing their contents. When Git is available, it distinguishes **tracked**, **ignored**, and **untracked/unknown** candidates. Tracked risky filenames can become blocking findings in strict mode.
|
|
@@ -146,6 +158,7 @@ Write reports for automation or review:
|
|
|
146
158
|
```powershell
|
|
147
159
|
cwb preflight . --json preflight.json
|
|
148
160
|
cwb preflight . --markdown preflight.md
|
|
161
|
+
cwb preflight . --sarif preflight.sarif
|
|
149
162
|
```
|
|
150
163
|
|
|
151
164
|
Use strict mode when blocking findings should return a non-zero exit code:
|
|
@@ -154,6 +167,12 @@ Use strict mode when blocking findings should return a non-zero exit code:
|
|
|
154
167
|
cwb preflight . --strict
|
|
155
168
|
```
|
|
156
169
|
|
|
170
|
+
Fail CI on any instruction-integrity finding:
|
|
171
|
+
|
|
172
|
+
```powershell
|
|
173
|
+
cwb preflight . --fail-on-integrity
|
|
174
|
+
```
|
|
175
|
+
|
|
157
176
|
### Detailed audit
|
|
158
177
|
|
|
159
178
|
```powershell
|
|
@@ -163,6 +182,20 @@ cwb audit . --sarif audit.sarif
|
|
|
163
182
|
cwb audit . --strict
|
|
164
183
|
```
|
|
165
184
|
|
|
185
|
+
### Safe fix preview
|
|
186
|
+
|
|
187
|
+
```powershell
|
|
188
|
+
cwb fix .
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
This is preview-only by default. To apply only low-risk supported fixes:
|
|
192
|
+
|
|
193
|
+
```powershell
|
|
194
|
+
cwb fix . --apply
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Existing conflicting instruction files are never auto-rewritten. They remain human-review findings.
|
|
198
|
+
|
|
166
199
|
### Non-destructive doctor
|
|
167
200
|
|
|
168
201
|
```powershell
|
|
@@ -188,16 +221,19 @@ The reusable Action is published on GitHub Marketplace.
|
|
|
188
221
|
- uses: actions/setup-python@v7
|
|
189
222
|
with:
|
|
190
223
|
python-version: "3.13"
|
|
191
|
-
- uses: kohli217/codex-workspace-bootstrap@v0.
|
|
224
|
+
- uses: kohli217/codex-workspace-bootstrap@v0.5.0
|
|
192
225
|
with:
|
|
193
226
|
path: .
|
|
194
227
|
strict: "true"
|
|
228
|
+
fail_on_integrity: "true"
|
|
195
229
|
```
|
|
196
230
|
|
|
197
|
-
The Action adds the preflight Markdown report to the **GitHub Actions job summary**, so maintainers get a readable readiness snapshot without digging through raw logs. Optional SARIF output can be uploaded to GitHub Code Scanning.
|
|
231
|
+
The Action adds the preflight Markdown report to the **GitHub Actions job summary**, so maintainers get a readable readiness snapshot without digging through raw logs. Optional SARIF output contains both repository-audit and instruction-integrity findings and can be uploaded to GitHub Code Scanning.
|
|
198
232
|
|
|
199
233
|
See [docs/GITHUB_ACTION.md](docs/GITHUB_ACTION.md).
|
|
200
234
|
|
|
235
|
+
Read-only evaluations against real public repositories are documented in [docs/PUBLIC_REPO_EVALUATIONS.md](docs/PUBLIC_REPO_EVALUATIONS.md). They are reproducible technical evaluations, not claims of third-party adoption.
|
|
236
|
+
|
|
201
237
|
## Where it fits
|
|
202
238
|
|
|
203
239
|
This project is a **preflight layer**, not an AI coding agent and not a deep security scanner.
|
|
@@ -216,6 +252,7 @@ The intent is to complement those tools, not replace them.
|
|
|
216
252
|
- Core checks run locally.
|
|
217
253
|
- Suspected secret files are not opened or printed by the filename-risk check.
|
|
218
254
|
- Repository contents are not sent to a remote AI service by the core audit.
|
|
255
|
+
- Instruction files are read locally for deterministic linting; extracted commands are never executed by the integrity lint.
|
|
219
256
|
- Existing `AGENTS.md` files are protected unless overwrite is explicit.
|
|
220
257
|
- A passing preflight is evidence about the checks performed, **not a security guarantee**.
|
|
221
258
|
|
|
@@ -248,7 +285,7 @@ py -m pip install codex-workspace-bootstrap
|
|
|
248
285
|
Pinned GitHub release artifact:
|
|
249
286
|
|
|
250
287
|
```powershell
|
|
251
|
-
py -m pip install "https://github.com/kohli217/codex-workspace-bootstrap/releases/download/v0.
|
|
288
|
+
py -m pip install "https://github.com/kohli217/codex-workspace-bootstrap/releases/download/v0.5.0/codex_workspace_bootstrap-0.5.0-py3-none-any.whl"
|
|
252
289
|
```
|
|
253
290
|
|
|
254
291
|
## Development
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](pyproject.toml)
|
|
9
9
|
|
|
10
|
-
**Preflight your repository before an AI coding agent touches it.**
|
|
10
|
+
**Preflight your repository before an AI coding agent touches it — and catch instruction drift before different agents follow different rules.**
|
|
11
11
|
|
|
12
12
|
Windows-first. Local by default. CI-friendly. Designed for repositories used with Codex, Copilot, Cline, Claude Code, Gemini CLI, Continue, Cursor, and similar coding agents.
|
|
13
13
|
|
|
@@ -30,6 +30,7 @@ State: NEEDS ATTENTION
|
|
|
30
30
|
Project: Python
|
|
31
31
|
AI instructions: none detected
|
|
32
32
|
Audit: 10 passed, 4 warnings, 0 blocking
|
|
33
|
+
Instruction integrity: 0 findings, 0 drift, 0 invalid commands, 0 metadata
|
|
33
34
|
Next actions:
|
|
34
35
|
[P1] Add repository instructions for AI coding agents -> cwb init-agents .
|
|
35
36
|
[P2] Make the Codex CLI available when local Codex workflows are intended -> codex --version
|
|
@@ -73,6 +74,17 @@ The preflight detects repository instruction/config signals for:
|
|
|
73
74
|
|
|
74
75
|
It does not claim these files are correct merely because they exist. It tells you what was detected so a maintainer can review the actual instructions.
|
|
75
76
|
|
|
77
|
+
### Cross-agent instruction integrity
|
|
78
|
+
|
|
79
|
+
When multiple AI instruction files exist, `cwb` reads executable-looking commands and checks them against repository evidence. It understands nested `AGENTS.md` / `AGENTS.override.md`, nested Cursor `.cursor/rules`, and common path-specific frontmatter such as Copilot `applyTo` and rule `globs`. It can flag:
|
|
80
|
+
|
|
81
|
+
- package-manager mismatches against `packageManager` and lockfiles;
|
|
82
|
+
- cross-agent package-manager drift;
|
|
83
|
+
- missing `package.json` scripts referenced by instructions;
|
|
84
|
+
- conflicting test/lint/build validation commands when instruction files have no shared command for the same validation family.
|
|
85
|
+
|
|
86
|
+
The lint is intentionally conservative: different files may contain additional commands without being treated as conflicts when they share a compatible validation baseline. Commands from different scopes are not compared as if they were global rules. Path-specific rules are validated individually against repository evidence but are not cross-compared for drift unless their full selector semantics can be represented safely. A repository is not marked READY when it only has nested/path-specific instructions and no repository-wide instruction baseline.
|
|
87
|
+
|
|
76
88
|
### Risk signals
|
|
77
89
|
|
|
78
90
|
The audit warns about common secret-bearing filenames without printing their contents. When Git is available, it distinguishes **tracked**, **ignored**, and **untracked/unknown** candidates. Tracked risky filenames can become blocking findings in strict mode.
|
|
@@ -118,6 +130,7 @@ Write reports for automation or review:
|
|
|
118
130
|
```powershell
|
|
119
131
|
cwb preflight . --json preflight.json
|
|
120
132
|
cwb preflight . --markdown preflight.md
|
|
133
|
+
cwb preflight . --sarif preflight.sarif
|
|
121
134
|
```
|
|
122
135
|
|
|
123
136
|
Use strict mode when blocking findings should return a non-zero exit code:
|
|
@@ -126,6 +139,12 @@ Use strict mode when blocking findings should return a non-zero exit code:
|
|
|
126
139
|
cwb preflight . --strict
|
|
127
140
|
```
|
|
128
141
|
|
|
142
|
+
Fail CI on any instruction-integrity finding:
|
|
143
|
+
|
|
144
|
+
```powershell
|
|
145
|
+
cwb preflight . --fail-on-integrity
|
|
146
|
+
```
|
|
147
|
+
|
|
129
148
|
### Detailed audit
|
|
130
149
|
|
|
131
150
|
```powershell
|
|
@@ -135,6 +154,20 @@ cwb audit . --sarif audit.sarif
|
|
|
135
154
|
cwb audit . --strict
|
|
136
155
|
```
|
|
137
156
|
|
|
157
|
+
### Safe fix preview
|
|
158
|
+
|
|
159
|
+
```powershell
|
|
160
|
+
cwb fix .
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
This is preview-only by default. To apply only low-risk supported fixes:
|
|
164
|
+
|
|
165
|
+
```powershell
|
|
166
|
+
cwb fix . --apply
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Existing conflicting instruction files are never auto-rewritten. They remain human-review findings.
|
|
170
|
+
|
|
138
171
|
### Non-destructive doctor
|
|
139
172
|
|
|
140
173
|
```powershell
|
|
@@ -160,16 +193,19 @@ The reusable Action is published on GitHub Marketplace.
|
|
|
160
193
|
- uses: actions/setup-python@v7
|
|
161
194
|
with:
|
|
162
195
|
python-version: "3.13"
|
|
163
|
-
- uses: kohli217/codex-workspace-bootstrap@v0.
|
|
196
|
+
- uses: kohli217/codex-workspace-bootstrap@v0.5.0
|
|
164
197
|
with:
|
|
165
198
|
path: .
|
|
166
199
|
strict: "true"
|
|
200
|
+
fail_on_integrity: "true"
|
|
167
201
|
```
|
|
168
202
|
|
|
169
|
-
The Action adds the preflight Markdown report to the **GitHub Actions job summary**, so maintainers get a readable readiness snapshot without digging through raw logs. Optional SARIF output can be uploaded to GitHub Code Scanning.
|
|
203
|
+
The Action adds the preflight Markdown report to the **GitHub Actions job summary**, so maintainers get a readable readiness snapshot without digging through raw logs. Optional SARIF output contains both repository-audit and instruction-integrity findings and can be uploaded to GitHub Code Scanning.
|
|
170
204
|
|
|
171
205
|
See [docs/GITHUB_ACTION.md](docs/GITHUB_ACTION.md).
|
|
172
206
|
|
|
207
|
+
Read-only evaluations against real public repositories are documented in [docs/PUBLIC_REPO_EVALUATIONS.md](docs/PUBLIC_REPO_EVALUATIONS.md). They are reproducible technical evaluations, not claims of third-party adoption.
|
|
208
|
+
|
|
173
209
|
## Where it fits
|
|
174
210
|
|
|
175
211
|
This project is a **preflight layer**, not an AI coding agent and not a deep security scanner.
|
|
@@ -188,6 +224,7 @@ The intent is to complement those tools, not replace them.
|
|
|
188
224
|
- Core checks run locally.
|
|
189
225
|
- Suspected secret files are not opened or printed by the filename-risk check.
|
|
190
226
|
- Repository contents are not sent to a remote AI service by the core audit.
|
|
227
|
+
- Instruction files are read locally for deterministic linting; extracted commands are never executed by the integrity lint.
|
|
191
228
|
- Existing `AGENTS.md` files are protected unless overwrite is explicit.
|
|
192
229
|
- A passing preflight is evidence about the checks performed, **not a security guarantee**.
|
|
193
230
|
|
|
@@ -220,7 +257,7 @@ py -m pip install codex-workspace-bootstrap
|
|
|
220
257
|
Pinned GitHub release artifact:
|
|
221
258
|
|
|
222
259
|
```powershell
|
|
223
|
-
py -m pip install "https://github.com/kohli217/codex-workspace-bootstrap/releases/download/v0.
|
|
260
|
+
py -m pip install "https://github.com/kohli217/codex-workspace-bootstrap/releases/download/v0.5.0/codex_workspace_bootstrap-0.5.0-py3-none-any.whl"
|
|
224
261
|
```
|
|
225
262
|
|
|
226
263
|
## Development
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codex-workspace-bootstrap"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
description = "Preflight AI coding repositories before Codex, Copilot, Cline, Claude, Gemini, Continue, or Cursor touches them."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -9,14 +9,15 @@ from . import __version__
|
|
|
9
9
|
from .agents import generate_agents
|
|
10
10
|
from .audit import audit_repository, summary
|
|
11
11
|
from .doctor import doctor_findings
|
|
12
|
+
from .fixes import apply_fix_plan, build_fix_plan
|
|
12
13
|
from .preflight import build_preflight, render_markdown
|
|
13
|
-
from .sarif import checks_to_sarif
|
|
14
|
+
from .sarif import checks_to_sarif, preflight_report_to_sarif
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def _parser() -> argparse.ArgumentParser:
|
|
17
18
|
parser = argparse.ArgumentParser(
|
|
18
19
|
prog="codex-workspace-bootstrap",
|
|
19
|
-
description="
|
|
20
|
+
description="Preflight AI coding repositories for readiness, instruction integrity, and CI enforcement.",
|
|
20
21
|
)
|
|
21
22
|
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
22
23
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -25,11 +26,29 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
25
26
|
preflight.add_argument("path", nargs="?", default=".")
|
|
26
27
|
preflight.add_argument("--json", dest="json_path", help="Write the complete preflight report to JSON")
|
|
27
28
|
preflight.add_argument("--markdown", dest="markdown_path", help="Write a concise Markdown preflight report")
|
|
29
|
+
preflight.add_argument(
|
|
30
|
+
"--sarif",
|
|
31
|
+
dest="sarif_path",
|
|
32
|
+
help="Write audit and instruction-integrity findings as SARIF 2.1.0",
|
|
33
|
+
)
|
|
28
34
|
preflight.add_argument(
|
|
29
35
|
"--strict",
|
|
30
36
|
action="store_true",
|
|
31
37
|
help="Return a non-zero exit code when blocking findings are present",
|
|
32
38
|
)
|
|
39
|
+
preflight.add_argument(
|
|
40
|
+
"--fail-on-integrity",
|
|
41
|
+
action="store_true",
|
|
42
|
+
help="Return a non-zero exit code when AI instruction integrity findings are present",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
fix = sub.add_parser("fix", help="Preview safe repository-readiness fixes")
|
|
46
|
+
fix.add_argument("path", nargs="?", default=".")
|
|
47
|
+
fix.add_argument(
|
|
48
|
+
"--apply",
|
|
49
|
+
action="store_true",
|
|
50
|
+
help="Apply only low-risk supported fixes; conflicting instructions are never auto-rewritten",
|
|
51
|
+
)
|
|
33
52
|
|
|
34
53
|
audit = sub.add_parser("audit", help="Audit a repository and local toolchain")
|
|
35
54
|
audit.add_argument("path", nargs="?", default=".")
|
|
@@ -63,7 +82,9 @@ def _run_preflight(
|
|
|
63
82
|
path: str,
|
|
64
83
|
json_path: str | None,
|
|
65
84
|
markdown_path: str | None,
|
|
85
|
+
sarif_path: str | None,
|
|
66
86
|
strict: bool,
|
|
87
|
+
fail_on_integrity: bool,
|
|
67
88
|
) -> int:
|
|
68
89
|
root = Path(path).expanduser().resolve()
|
|
69
90
|
if not root.exists() or not root.is_dir():
|
|
@@ -84,7 +105,7 @@ def _run_preflight(
|
|
|
84
105
|
if instructions:
|
|
85
106
|
print("AI instructions:")
|
|
86
107
|
for item in instructions:
|
|
87
|
-
print(f" - {item['tool']}: {item['path']}")
|
|
108
|
+
print(f" - {item['tool']}: {item['path']} [scope={item.get('scope', '.')}]")
|
|
88
109
|
else:
|
|
89
110
|
print("AI instructions: none detected")
|
|
90
111
|
|
|
@@ -94,6 +115,23 @@ def _run_preflight(
|
|
|
94
115
|
f"{totals['warnings']} warnings, {totals['blocking']} blocking"
|
|
95
116
|
)
|
|
96
117
|
|
|
118
|
+
instruction_totals = report["instruction_summary"]
|
|
119
|
+
print(
|
|
120
|
+
f"Instruction integrity: {instruction_totals['findings']} findings, "
|
|
121
|
+
f"{instruction_totals['drift']} drift, "
|
|
122
|
+
f"{instruction_totals['invalid_commands']} invalid commands, "
|
|
123
|
+
f"{instruction_totals['metadata']} metadata"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
findings = report["instruction_findings"]
|
|
127
|
+
if findings:
|
|
128
|
+
print("Instruction findings:")
|
|
129
|
+
for item in findings:
|
|
130
|
+
print(
|
|
131
|
+
f" [{item['severity'].upper()}] {item['kind']}: {item['message']} "
|
|
132
|
+
f"[scope={item.get('scope', '.')}]"
|
|
133
|
+
)
|
|
134
|
+
|
|
97
135
|
actions = report["next_actions"]
|
|
98
136
|
if actions:
|
|
99
137
|
print("Next actions:")
|
|
@@ -112,8 +150,52 @@ def _run_preflight(
|
|
|
112
150
|
output.write_text(render_markdown(report), encoding="utf-8")
|
|
113
151
|
print(f"Preflight Markdown report written to: {output}")
|
|
114
152
|
|
|
153
|
+
if sarif_path:
|
|
154
|
+
_write_json(
|
|
155
|
+
sarif_path,
|
|
156
|
+
preflight_report_to_sarif(report),
|
|
157
|
+
"Preflight SARIF report",
|
|
158
|
+
)
|
|
159
|
+
|
|
115
160
|
if strict and report["state"] == "BLOCKED":
|
|
116
161
|
return 1
|
|
162
|
+
if fail_on_integrity and report["instruction_summary"]["findings"]:
|
|
163
|
+
return 1
|
|
164
|
+
return 0
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _run_fix(path: str, apply: bool) -> int:
|
|
168
|
+
root = Path(path).expanduser().resolve()
|
|
169
|
+
if not root.exists() or not root.is_dir():
|
|
170
|
+
print(f"error: repository path does not exist or is not a directory: {root}", file=sys.stderr)
|
|
171
|
+
return 2
|
|
172
|
+
|
|
173
|
+
plan = build_fix_plan(root)
|
|
174
|
+
print(f"Repository: {root}")
|
|
175
|
+
if not plan:
|
|
176
|
+
print("Fix plan: no supported fixes or instruction-integrity findings.")
|
|
177
|
+
return 0
|
|
178
|
+
|
|
179
|
+
print("Fix plan:")
|
|
180
|
+
for item in plan:
|
|
181
|
+
mode = "AUTO" if item.apply_supported else "REVIEW"
|
|
182
|
+
target = f" -> {item.target}" if item.target else ""
|
|
183
|
+
print(f" [{mode}] {item.description}{target}")
|
|
184
|
+
|
|
185
|
+
if not apply:
|
|
186
|
+
print("Preview only. Re-run with --apply to apply low-risk supported fixes.")
|
|
187
|
+
return 0
|
|
188
|
+
|
|
189
|
+
applied = apply_fix_plan(root, plan)
|
|
190
|
+
if applied:
|
|
191
|
+
print("Applied:")
|
|
192
|
+
for path_item in applied:
|
|
193
|
+
print(f" - {path_item}")
|
|
194
|
+
else:
|
|
195
|
+
print("No automatic changes were applied.")
|
|
196
|
+
manual = sum(not item.apply_supported for item in plan)
|
|
197
|
+
if manual:
|
|
198
|
+
print(f"{manual} finding(s) require human review and were left unchanged.")
|
|
117
199
|
return 0
|
|
118
200
|
|
|
119
201
|
def _run_audit(
|
|
@@ -200,7 +282,16 @@ def _run_init_agents(path: str, force: bool) -> int:
|
|
|
200
282
|
def main(argv: list[str] | None = None) -> int:
|
|
201
283
|
args = _parser().parse_args(argv)
|
|
202
284
|
if args.command == "preflight":
|
|
203
|
-
return _run_preflight(
|
|
285
|
+
return _run_preflight(
|
|
286
|
+
args.path,
|
|
287
|
+
args.json_path,
|
|
288
|
+
args.markdown_path,
|
|
289
|
+
args.sarif_path,
|
|
290
|
+
args.strict,
|
|
291
|
+
args.fail_on_integrity,
|
|
292
|
+
)
|
|
293
|
+
if args.command == "fix":
|
|
294
|
+
return _run_fix(args.path, args.apply)
|
|
204
295
|
if args.command == "audit":
|
|
205
296
|
return _run_audit(args.path, args.json_path, args.sarif_path, args.strict)
|
|
206
297
|
if args.command == "doctor":
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import asdict, dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from .agents import generate_agents
|
|
7
|
+
from .instructions import detect_instruction_signals, lint_instructions
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class FixPlanItem:
|
|
12
|
+
kind: str
|
|
13
|
+
description: str
|
|
14
|
+
apply_supported: bool
|
|
15
|
+
target: str | None = None
|
|
16
|
+
|
|
17
|
+
def to_dict(self) -> dict[str, object]:
|
|
18
|
+
return asdict(self)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_fix_plan(root: Path) -> list[FixPlanItem]:
|
|
22
|
+
root = root.resolve()
|
|
23
|
+
plan: list[FixPlanItem] = []
|
|
24
|
+
|
|
25
|
+
signals = detect_instruction_signals(root)
|
|
26
|
+
findings = lint_instructions(root, signals)
|
|
27
|
+
|
|
28
|
+
has_repository_wide = any(
|
|
29
|
+
signal.scope == "." and signal.kind in {"repository", "override"}
|
|
30
|
+
for signal in signals
|
|
31
|
+
)
|
|
32
|
+
if not has_repository_wide:
|
|
33
|
+
plan.append(
|
|
34
|
+
FixPlanItem(
|
|
35
|
+
"create-agents",
|
|
36
|
+
(
|
|
37
|
+
"Create an evidence-based AGENTS.md because no supported repository-wide "
|
|
38
|
+
"AI instruction baseline was detected."
|
|
39
|
+
),
|
|
40
|
+
True,
|
|
41
|
+
"AGENTS.md",
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
for finding in findings:
|
|
46
|
+
plan.append(
|
|
47
|
+
FixPlanItem(
|
|
48
|
+
f"manual-{finding.kind}",
|
|
49
|
+
finding.message,
|
|
50
|
+
False,
|
|
51
|
+
", ".join(finding.files) if finding.files else None,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return plan
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def apply_fix_plan(root: Path, plan: list[FixPlanItem]) -> list[str]:
|
|
59
|
+
root = root.resolve()
|
|
60
|
+
applied: list[str] = []
|
|
61
|
+
|
|
62
|
+
for item in plan:
|
|
63
|
+
if not item.apply_supported:
|
|
64
|
+
continue
|
|
65
|
+
if item.kind == "create-agents":
|
|
66
|
+
target = root / "AGENTS.md"
|
|
67
|
+
if target.exists():
|
|
68
|
+
continue
|
|
69
|
+
target.write_text(generate_agents(root), encoding="utf-8")
|
|
70
|
+
applied.append(str(target))
|
|
71
|
+
|
|
72
|
+
return applied
|