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.
Files changed (29) hide show
  1. {codex_workspace_bootstrap-0.4.0/src/codex_workspace_bootstrap.egg-info → codex_workspace_bootstrap-0.5.0}/PKG-INFO +42 -5
  2. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/README.md +41 -4
  3. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/pyproject.toml +1 -1
  4. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/__init__.py +1 -1
  5. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/cli.py +95 -4
  6. codex_workspace_bootstrap-0.5.0/src/codex_workspace_bootstrap/fixes.py +72 -0
  7. codex_workspace_bootstrap-0.5.0/src/codex_workspace_bootstrap/instructions.py +654 -0
  8. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/preflight.py +61 -53
  9. codex_workspace_bootstrap-0.5.0/src/codex_workspace_bootstrap/sarif.py +205 -0
  10. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0/src/codex_workspace_bootstrap.egg-info}/PKG-INFO +42 -5
  11. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap.egg-info/SOURCES.txt +4 -0
  12. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/tests/test_cli.py +33 -1
  13. codex_workspace_bootstrap-0.5.0/tests/test_instruction_integrity.py +381 -0
  14. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/tests/test_preflight.py +60 -0
  15. codex_workspace_bootstrap-0.5.0/tests/test_public_repo_patterns.py +72 -0
  16. codex_workspace_bootstrap-0.5.0/tests/test_sarif.py +96 -0
  17. codex_workspace_bootstrap-0.4.0/src/codex_workspace_bootstrap/sarif.py +0 -83
  18. codex_workspace_bootstrap-0.4.0/tests/test_sarif.py +0 -44
  19. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/LICENSE +0 -0
  20. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/setup.cfg +0 -0
  21. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/__main__.py +0 -0
  22. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/agents.py +0 -0
  23. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/audit.py +0 -0
  24. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap/doctor.py +0 -0
  25. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap.egg-info/dependency_links.txt +0 -0
  26. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap.egg-info/entry_points.txt +0 -0
  27. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/src/codex_workspace_bootstrap.egg-info/top_level.txt +0 -0
  28. {codex_workspace_bootstrap-0.4.0 → codex_workspace_bootstrap-0.5.0}/tests/test_audit.py +0 -0
  29. {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.4.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](https://img.shields.io/github/license/kohli217/codex-workspace-bootstrap)](LICENSE)
36
36
  [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](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.4.0
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.4.0/codex_workspace_bootstrap-0.4.0-py3-none-any.whl"
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](https://img.shields.io/github/license/kohli217/codex-workspace-bootstrap)](LICENSE)
8
8
  [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](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.4.0
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.4.0/codex_workspace_bootstrap-0.4.0-py3-none-any.whl"
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.4.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"
@@ -1,3 +1,3 @@
1
1
  """codex-workspace-bootstrap package."""
2
2
 
3
- __version__ = "0.4.0"
3
+ __version__ = "0.5.0"
@@ -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="Audit and bootstrap repositories for reliable Codex workflows.",
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(args.path, args.json_path, args.markdown_path, args.strict)
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