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.
Files changed (28) hide show
  1. {codex_workspace_bootstrap-0.5.2/src/codex_workspace_bootstrap.egg-info → codex_workspace_bootstrap-0.6.0}/PKG-INFO +10 -8
  2. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/README.md +9 -7
  3. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/pyproject.toml +1 -1
  4. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/__init__.py +1 -1
  5. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/agents.py +93 -18
  6. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/cli.py +3 -0
  7. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/fixes.py +1 -1
  8. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/instructions.py +258 -54
  9. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0/src/codex_workspace_bootstrap.egg-info}/PKG-INFO +10 -8
  10. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap.egg-info/SOURCES.txt +1 -0
  11. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_cli.py +78 -3
  12. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_instruction_integrity.py +343 -0
  13. codex_workspace_bootstrap-0.6.0/tests/test_release_consistency.py +49 -0
  14. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/LICENSE +0 -0
  15. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/setup.cfg +0 -0
  16. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/__main__.py +0 -0
  17. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/audit.py +0 -0
  18. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/doctor.py +0 -0
  19. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/preflight.py +0 -0
  20. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap/sarif.py +0 -0
  21. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap.egg-info/dependency_links.txt +0 -0
  22. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap.egg-info/entry_points.txt +0 -0
  23. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/src/codex_workspace_bootstrap.egg-info/top_level.txt +0 -0
  24. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_audit.py +0 -0
  25. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_doctor.py +0 -0
  26. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_preflight.py +0 -0
  27. {codex_workspace_bootstrap-0.5.2 → codex_workspace_bootstrap-0.6.0}/tests/test_public_repo_patterns.py +0 -0
  28. {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.5.2
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@v7
259
- - uses: actions/setup-python@v7
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.5.2
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
- - Existing `AGENTS.md` files are protected unless overwrite is explicit.
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.5.2/codex_workspace_bootstrap-0.5.2-py3-none-any.whl"
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@v7
231
- - uses: actions/setup-python@v7
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.5.2
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
- - Existing `AGENTS.md` files are protected unless overwrite is explicit.
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.5.2/codex_workspace_bootstrap-0.5.2-py3-none-any.whl"
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.5.2"
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"
@@ -1,3 +1,3 @@
1
1
  """codex-workspace-bootstrap package."""
2
2
 
3
- __version__ = "0.5.2"
3
+ __version__ = "0.6.0"
@@ -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.exists():
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
- if "test" in scripts:
140
- plan.append(ValidationCommand("npm test", "package.json defines scripts.test"))
141
- if "lint" in scripts:
142
- plan.append(ValidationCommand("npm run lint", "package.json defines scripts.lint"))
143
- if "build" in scripts and "npm run build" in documented:
144
- plan.append(ValidationCommand("npm run build", "package.json defines scripts.build and README documents it"))
145
- if not {"test", "lint"} & scripts:
146
- plan.append(
147
- ValidationCommand(
148
- "npm install --ignore-scripts --package-lock-only --dry-run",
149
- "Node.js project detected; no test or lint script confirmed",
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))