codex-workspace-bootstrap 0.5.2__tar.gz → 0.6.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.5.2/src/codex_workspace_bootstrap.egg-info → codex_workspace_bootstrap-0.6.0}/PKG-INFO +10 -8
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/README.md +9 -7
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/pyproject.toml +1 -1
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/__init__.py +1 -1
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/agents.py +93 -18
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/cli.py +3 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/fixes.py +1 -1
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/instructions.py +258 -54
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0/src/codex_workspace_bootstrap.egg-info}/PKG-INFO +10 -8
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap.egg-info/SOURCES.txt +1 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_cli.py +78 -3
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_instruction_integrity.py +343 -0
- codex_workspace_bootstrap-0.6.0/tests/test_release_consistency.py +49 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/LICENSE +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/setup.cfg +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/__main__.py +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/audit.py +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/doctor.py +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/preflight.py +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/sarif.py +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap.egg-info/dependency_links.txt +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap.egg-info/entry_points.txt +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap.egg-info/top_level.txt +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_audit.py +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_doctor.py +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_preflight.py +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_public_repo_patterns.py +0 -0
- {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_sarif.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.6.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
|
|
@@ -142,7 +142,7 @@ It does not claim these files are correct merely because they exist. It tells yo
|
|
|
142
142
|
|
|
143
143
|
### Cross-agent instruction integrity
|
|
144
144
|
|
|
145
|
-
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:
|
|
145
|
+
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 `CLAUDE.md`, Gemini CLI hierarchical context files (including project `context.fileName` overrides), nested Cursor `.cursor/rules`, and common path-specific frontmatter such as Copilot `applyTo` and rule `globs`. It can flag:
|
|
146
146
|
|
|
147
147
|
- package-manager mismatches against `packageManager` and lockfiles;
|
|
148
148
|
- cross-agent package-manager drift;
|
|
@@ -248,18 +248,18 @@ cwb doctor .
|
|
|
248
248
|
cwb init-agents .
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
-
Generation is evidence-based: the tool cross-checks project manifests, package scripts, pytest configuration, and README commands. Plausible but unconfirmed commands are separated for maintainer review. Existing `AGENTS.md` files are never overwritten unless `--force` is explicit.
|
|
251
|
+
Generation is evidence-based: the tool cross-checks project manifests, package-manager evidence, package scripts, pytest configuration, and README commands. Plausible but unconfirmed commands are separated for maintainer review. Existing `AGENTS.md` files are never overwritten unless `--force` is explicit.
|
|
252
252
|
|
|
253
253
|
## GitHub Action
|
|
254
254
|
|
|
255
255
|
The reusable Action is published on GitHub Marketplace.
|
|
256
256
|
|
|
257
257
|
```yaml
|
|
258
|
-
- uses: actions/checkout@
|
|
259
|
-
- uses: actions/setup-python@
|
|
258
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
259
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
|
|
260
260
|
with:
|
|
261
261
|
python-version: "3.13"
|
|
262
|
-
- uses: kohli217/codex-workspace-bootstrap@v0.
|
|
262
|
+
- uses: kohli217/codex-workspace-bootstrap@v0.6.0
|
|
263
263
|
with:
|
|
264
264
|
path: .
|
|
265
265
|
strict: "true"
|
|
@@ -291,7 +291,9 @@ The intent is to complement those tools, not replace them.
|
|
|
291
291
|
- Suspected secret files are not opened or printed by the filename-risk check.
|
|
292
292
|
- Repository contents are not sent to a remote AI service by the core audit.
|
|
293
293
|
- Instruction files are read locally for deterministic linting; extracted commands are never executed by the integrity lint.
|
|
294
|
-
-
|
|
294
|
+
- Repository instruction/configuration inputs are not read through symbolic links.
|
|
295
|
+
- `init-agents` and automatic fixes refuse symlinked `AGENTS.md` targets rather than writing through them.
|
|
296
|
+
- Existing regular `AGENTS.md` files are protected unless overwrite is explicit.
|
|
295
297
|
- A passing preflight is evidence about the checks performed, **not a security guarantee**.
|
|
296
298
|
|
|
297
299
|
See [SECURITY.md](SECURITY.md).
|
|
@@ -323,7 +325,7 @@ py -m pip install codex-workspace-bootstrap
|
|
|
323
325
|
Pinned GitHub release artifact:
|
|
324
326
|
|
|
325
327
|
```powershell
|
|
326
|
-
py -m pip install "https://github.com/kohli217/codex-workspace-bootstrap/releases/download/v0.
|
|
328
|
+
py -m pip install "https://github.com/kohli217/codex-workspace-bootstrap/releases/download/v0.6.0/codex_workspace_bootstrap-0.6.0-py3-none-any.whl"
|
|
327
329
|
```
|
|
328
330
|
|
|
329
331
|
## Development
|
|
@@ -114,7 +114,7 @@ It does not claim these files are correct merely because they exist. It tells yo
|
|
|
114
114
|
|
|
115
115
|
### Cross-agent instruction integrity
|
|
116
116
|
|
|
117
|
-
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:
|
|
117
|
+
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 `CLAUDE.md`, Gemini CLI hierarchical context files (including project `context.fileName` overrides), nested Cursor `.cursor/rules`, and common path-specific frontmatter such as Copilot `applyTo` and rule `globs`. It can flag:
|
|
118
118
|
|
|
119
119
|
- package-manager mismatches against `packageManager` and lockfiles;
|
|
120
120
|
- cross-agent package-manager drift;
|
|
@@ -220,18 +220,18 @@ cwb doctor .
|
|
|
220
220
|
cwb init-agents .
|
|
221
221
|
```
|
|
222
222
|
|
|
223
|
-
Generation is evidence-based: the tool cross-checks project manifests, package scripts, pytest configuration, and README commands. Plausible but unconfirmed commands are separated for maintainer review. Existing `AGENTS.md` files are never overwritten unless `--force` is explicit.
|
|
223
|
+
Generation is evidence-based: the tool cross-checks project manifests, package-manager evidence, package scripts, pytest configuration, and README commands. Plausible but unconfirmed commands are separated for maintainer review. Existing `AGENTS.md` files are never overwritten unless `--force` is explicit.
|
|
224
224
|
|
|
225
225
|
## GitHub Action
|
|
226
226
|
|
|
227
227
|
The reusable Action is published on GitHub Marketplace.
|
|
228
228
|
|
|
229
229
|
```yaml
|
|
230
|
-
- uses: actions/checkout@
|
|
231
|
-
- uses: actions/setup-python@
|
|
230
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
231
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
|
|
232
232
|
with:
|
|
233
233
|
python-version: "3.13"
|
|
234
|
-
- uses: kohli217/codex-workspace-bootstrap@v0.
|
|
234
|
+
- uses: kohli217/codex-workspace-bootstrap@v0.6.0
|
|
235
235
|
with:
|
|
236
236
|
path: .
|
|
237
237
|
strict: "true"
|
|
@@ -263,7 +263,9 @@ The intent is to complement those tools, not replace them.
|
|
|
263
263
|
- Suspected secret files are not opened or printed by the filename-risk check.
|
|
264
264
|
- Repository contents are not sent to a remote AI service by the core audit.
|
|
265
265
|
- Instruction files are read locally for deterministic linting; extracted commands are never executed by the integrity lint.
|
|
266
|
-
-
|
|
266
|
+
- Repository instruction/configuration inputs are not read through symbolic links.
|
|
267
|
+
- `init-agents` and automatic fixes refuse symlinked `AGENTS.md` targets rather than writing through them.
|
|
268
|
+
- Existing regular `AGENTS.md` files are protected unless overwrite is explicit.
|
|
267
269
|
- A passing preflight is evidence about the checks performed, **not a security guarantee**.
|
|
268
270
|
|
|
269
271
|
See [SECURITY.md](SECURITY.md).
|
|
@@ -295,7 +297,7 @@ py -m pip install codex-workspace-bootstrap
|
|
|
295
297
|
Pinned GitHub release artifact:
|
|
296
298
|
|
|
297
299
|
```powershell
|
|
298
|
-
py -m pip install "https://github.com/kohli217/codex-workspace-bootstrap/releases/download/v0.
|
|
300
|
+
py -m pip install "https://github.com/kohli217/codex-workspace-bootstrap/releases/download/v0.6.0/codex_workspace_bootstrap-0.6.0-py3-none-any.whl"
|
|
299
301
|
```
|
|
300
302
|
|
|
301
303
|
## 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.6.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"
|
|
@@ -29,7 +29,7 @@ def detect_project_signals(root: Path) -> list[str]:
|
|
|
29
29
|
|
|
30
30
|
def _node_script_names(root: Path) -> set[str]:
|
|
31
31
|
package_json = root / NODE_MARKER
|
|
32
|
-
if not package_json.
|
|
32
|
+
if not package_json.is_file() or package_json.is_symlink():
|
|
33
33
|
return set()
|
|
34
34
|
|
|
35
35
|
try:
|
|
@@ -44,10 +44,48 @@ def _node_script_names(root: Path) -> set[str]:
|
|
|
44
44
|
return {name for name, value in scripts.items() if isinstance(name, str) and isinstance(value, str)}
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def _node_package_managers(root: Path) -> set[str]:
|
|
48
|
+
managers: set[str] = set()
|
|
49
|
+
package_json = root / NODE_MARKER
|
|
50
|
+
|
|
51
|
+
if package_json.is_file() and not package_json.is_symlink():
|
|
52
|
+
try:
|
|
53
|
+
data = json.loads(package_json.read_text(encoding="utf-8"))
|
|
54
|
+
except (OSError, json.JSONDecodeError, UnicodeDecodeError):
|
|
55
|
+
data = {}
|
|
56
|
+
if isinstance(data, dict):
|
|
57
|
+
declared = data.get("packageManager")
|
|
58
|
+
if isinstance(declared, str) and declared:
|
|
59
|
+
manager = declared.split("@", 1)[0].strip().lower()
|
|
60
|
+
if manager in {"npm", "pnpm", "yarn", "bun"}:
|
|
61
|
+
managers.add(manager)
|
|
62
|
+
|
|
63
|
+
lockfiles = {
|
|
64
|
+
"package-lock.json": "npm",
|
|
65
|
+
"npm-shrinkwrap.json": "npm",
|
|
66
|
+
"pnpm-lock.yaml": "pnpm",
|
|
67
|
+
"yarn.lock": "yarn",
|
|
68
|
+
"bun.lock": "bun",
|
|
69
|
+
"bun.lockb": "bun",
|
|
70
|
+
}
|
|
71
|
+
for filename, manager in lockfiles.items():
|
|
72
|
+
candidate = root / filename
|
|
73
|
+
if candidate.is_file() and not candidate.is_symlink():
|
|
74
|
+
managers.add(manager)
|
|
75
|
+
|
|
76
|
+
return managers
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _package_script_command(manager: str, script: str) -> str:
|
|
80
|
+
if manager == "npm" and script == "test":
|
|
81
|
+
return "npm test"
|
|
82
|
+
return f"{manager} run {script}"
|
|
83
|
+
|
|
84
|
+
|
|
47
85
|
def _readme_text(root: Path) -> str:
|
|
48
86
|
for name in README_NAMES:
|
|
49
87
|
path = root / name
|
|
50
|
-
if path.is_file():
|
|
88
|
+
if path.is_file() and not path.is_symlink():
|
|
51
89
|
try:
|
|
52
90
|
return path.read_text(encoding="utf-8")
|
|
53
91
|
except (OSError, UnicodeDecodeError):
|
|
@@ -64,10 +102,10 @@ def _documented_commands(root: Path) -> set[str]:
|
|
|
64
102
|
"python -m pytest",
|
|
65
103
|
"pytest",
|
|
66
104
|
"python -m unittest",
|
|
67
|
-
"npm test",
|
|
68
|
-
"npm run lint",
|
|
69
|
-
"npm run build",
|
|
70
105
|
}
|
|
106
|
+
for manager in ("npm", "pnpm", "yarn", "bun"):
|
|
107
|
+
for script in ("test", "lint", "build"):
|
|
108
|
+
candidates.add(_package_script_command(manager, script))
|
|
71
109
|
found: set[str] = set()
|
|
72
110
|
for command in candidates:
|
|
73
111
|
pattern = rf"(?m)(^|[\s$>]){re.escape(command)}(?=$|\s)"
|
|
@@ -78,7 +116,7 @@ def _documented_commands(root: Path) -> set[str]:
|
|
|
78
116
|
|
|
79
117
|
def _pyproject_has_pytest(root: Path) -> bool:
|
|
80
118
|
path = root / "pyproject.toml"
|
|
81
|
-
if not path.is_file():
|
|
119
|
+
if not path.is_file() or path.is_symlink():
|
|
82
120
|
return False
|
|
83
121
|
|
|
84
122
|
try:
|
|
@@ -136,19 +174,56 @@ def validation_plan(root: Path) -> list[ValidationCommand]:
|
|
|
136
174
|
|
|
137
175
|
if "Node.js" in signals:
|
|
138
176
|
scripts = _node_script_names(root)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
177
|
+
managers = _node_package_managers(root)
|
|
178
|
+
manager = next(iter(managers)) if len(managers) == 1 else None
|
|
179
|
+
|
|
180
|
+
if manager:
|
|
181
|
+
if "test" in scripts:
|
|
182
|
+
plan.append(
|
|
183
|
+
ValidationCommand(
|
|
184
|
+
_package_script_command(manager, "test"),
|
|
185
|
+
f"package.json defines scripts.test and repository evidence selects {manager}",
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
if "lint" in scripts:
|
|
189
|
+
plan.append(
|
|
190
|
+
ValidationCommand(
|
|
191
|
+
_package_script_command(manager, "lint"),
|
|
192
|
+
f"package.json defines scripts.lint and repository evidence selects {manager}",
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
build_command = _package_script_command(manager, "build")
|
|
196
|
+
if "build" in scripts and build_command in documented:
|
|
197
|
+
plan.append(
|
|
198
|
+
ValidationCommand(
|
|
199
|
+
build_command,
|
|
200
|
+
f"package.json defines scripts.build, repository evidence selects {manager}, and README documents it",
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
elif len(managers) > 1:
|
|
204
|
+
# Conflicting package-manager evidence is handled by preflight.
|
|
205
|
+
# Do not manufacture a manager-specific validation command here.
|
|
206
|
+
pass
|
|
207
|
+
else:
|
|
208
|
+
# package.json alone does not prove npm. Preserve the historical
|
|
209
|
+
# npm suggestions only as review-required hints until the project
|
|
210
|
+
# supplies a lockfile, packageManager field, or documented command.
|
|
211
|
+
if "test" in scripts:
|
|
212
|
+
plan.append(
|
|
213
|
+
ValidationCommand(
|
|
214
|
+
"npm test",
|
|
215
|
+
"package.json defines scripts.test, but the package manager is not confirmed",
|
|
216
|
+
review_required=True,
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
if "lint" in scripts:
|
|
220
|
+
plan.append(
|
|
221
|
+
ValidationCommand(
|
|
222
|
+
"npm run lint",
|
|
223
|
+
"package.json defines scripts.lint, but the package manager is not confirmed",
|
|
224
|
+
review_required=True,
|
|
225
|
+
)
|
|
150
226
|
)
|
|
151
|
-
)
|
|
152
227
|
|
|
153
228
|
plan.extend(
|
|
154
229
|
[
|
|
@@ -270,6 +270,9 @@ def _run_init_agents(path: str, force: bool) -> int:
|
|
|
270
270
|
return 2
|
|
271
271
|
|
|
272
272
|
target = root / "AGENTS.md"
|
|
273
|
+
if target.is_symlink():
|
|
274
|
+
print(f"error: refusing to write through symlink: {target}", file=sys.stderr)
|
|
275
|
+
return 1
|
|
273
276
|
if target.exists() and not force:
|
|
274
277
|
print(f"error: {target} already exists; use --force to overwrite", file=sys.stderr)
|
|
275
278
|
return 1
|
|
@@ -64,7 +64,7 @@ def apply_fix_plan(root: Path, plan: list[FixPlanItem]) -> list[str]:
|
|
|
64
64
|
continue
|
|
65
65
|
if item.kind == "create-agents":
|
|
66
66
|
target = root / "AGENTS.md"
|
|
67
|
-
if target.exists():
|
|
67
|
+
if target.exists() or target.is_symlink():
|
|
68
68
|
continue
|
|
69
69
|
target.write_text(generate_agents(root), encoding="utf-8")
|
|
70
70
|
applied.append(str(target))
|