claude-dev-env 2.36.0 → 4.0.0

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 (66) hide show
  1. package/.agents/skills/_shared/advisor/scripts/README.md +1 -1
  2. package/.agents/skills/team-advisor/SKILL.md +11 -11
  3. package/_shared/advisor/advisor-protocol.md +51 -51
  4. package/_shared/advisor/reference/advisor-block.md +9 -9
  5. package/_shared/advisor/reference/astra-rung.md +39 -0
  6. package/_shared/advisor/reference/identity.md +4 -4
  7. package/_shared/advisor/reference/lifecycle.md +6 -6
  8. package/_shared/advisor/reference/spawn-walk-log.md +9 -9
  9. package/_shared/advisor/reference/third-party-bind.md +11 -11
  10. package/_shared/advisor/reference/warm-up.md +13 -13
  11. package/_shared/advisor/scripts/codex_astra_advisor.py +308 -0
  12. package/_shared/advisor/scripts/codex_astra_preflight.py +115 -0
  13. package/_shared/advisor/scripts/codex_astra_reply.py +159 -0
  14. package/_shared/advisor/scripts/config/advisor_scripts_constants/advisor_route_constants.py +3 -3
  15. package/_shared/advisor/scripts/config/advisor_scripts_constants/astra_advisor_constants.py +38 -0
  16. package/_shared/advisor/scripts/model_tier_run_validator.py +17 -18
  17. package/_shared/advisor/scripts/tests/test_codex_astra_advisor.py +271 -0
  18. package/_shared/advisor/scripts/tests/test_codex_astra_preflight.py +24 -0
  19. package/_shared/advisor/scripts/tests/test_codex_astra_reply.py +42 -0
  20. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +55 -30
  21. package/_shared/advisor/scripts/tests/test_tier_model_ids.py +14 -4
  22. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +14 -0
  23. package/_shared/pr-loop/scripts/terminology_sweep.py +138 -50
  24. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +13 -1
  25. package/docs/references/team-advisor-skill.md +3 -3
  26. package/hooks/validators/README.md +35 -1
  27. package/package.json +1 -1
  28. package/scripts/check.ps1 +18 -6
  29. package/scripts/policy_lint/adapter_detectors.py +42 -2
  30. package/scripts/policy_lint/adapters.py +24 -0
  31. package/scripts/policy_lint/config/constants.py +11 -0
  32. package/scripts/policy_lint/engine.py +1 -1
  33. package/scripts/policy_lint/model.py +1 -0
  34. package/scripts/policy_lint/registry.py +10 -1
  35. package/scripts/repository_checks/__init__.py +1 -0
  36. package/scripts/repository_checks/claude_md.py +108 -0
  37. package/scripts/repository_checks/config/__init__.py +1 -0
  38. package/scripts/repository_checks/config/constants.py +150 -0
  39. package/scripts/repository_checks/env_var_documentation.py +243 -0
  40. package/scripts/repository_checks/hook_modules.py +34 -0
  41. package/scripts/repository_checks/models.py +37 -0
  42. package/scripts/repository_checks/package_inventory.py +118 -0
  43. package/scripts/repository_checks/pytest_testpaths.py +84 -0
  44. package/scripts/repository_checks/reporting.py +39 -0
  45. package/scripts/repository_checks/runner.py +93 -0
  46. package/scripts/repository_checks/tracked_secrets.py +147 -0
  47. package/scripts/repository_checks/tracked_tree.py +28 -0
  48. package/scripts/repository_policy.py +120 -0
  49. package/scripts/tests/repository_policy_test_support.py +89 -0
  50. package/scripts/tests/test_cde_lint.py +25 -1
  51. package/scripts/tests/test_claude_md.py +68 -0
  52. package/scripts/tests/test_env_var_documentation.py +133 -0
  53. package/scripts/tests/test_hook_modules.py +17 -0
  54. package/scripts/tests/test_models.py +26 -0
  55. package/scripts/tests/test_package_inventory.py +96 -0
  56. package/scripts/tests/test_policy_lint_rules_registry.py +117 -0
  57. package/scripts/tests/test_pytest_testpaths.py +66 -0
  58. package/scripts/tests/test_reporting.py +54 -0
  59. package/scripts/tests/test_repository_policy.py +71 -0
  60. package/scripts/tests/test_runner.py +59 -0
  61. package/scripts/tests/test_tracked_secrets.py +175 -0
  62. package/scripts/tests/test_tracked_tree.py +28 -0
  63. package/_shared/advisor/reference/sol-rung.md +0 -39
  64. package/_shared/advisor/scripts/codex_sol_advisor.py +0 -581
  65. package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +0 -38
  66. package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +0 -740
@@ -1,581 +0,0 @@
1
- """Bind and consult a read-only Codex CLI session at Sol low effort."""
2
-
3
- from __future__ import annotations
4
-
5
- import argparse
6
- import importlib
7
- import json
8
- import math
9
- import os
10
- import shutil
11
- import subprocess
12
- import sys
13
- from collections.abc import Callable, Mapping, Sequence
14
- from dataclasses import asdict, dataclass
15
- from pathlib import Path
16
-
17
- _scripts_directory = Path(__file__).resolve().parent
18
- _config_directory = _scripts_directory / "config"
19
- _config_directory_text = str(_config_directory)
20
- if _config_directory_text not in sys.path:
21
- sys.path.insert(0, _config_directory_text)
22
-
23
- from advisor_scripts_constants.sol_advisor_constants import ( # noqa: E402
24
- ADVISOR_CODEX_EXECUTABLE_ENV_VAR,
25
- ALL_SOL_TRUTHY_VALUES,
26
- CLAUDE_CONFIG_DIRECTORY_NAME,
27
- CODEX_CONFIG_FLAG,
28
- CODEX_EXECUTABLE,
29
- CODEX_EXEC_SUBCOMMAND,
30
- CODEX_JSON_FLAG,
31
- CODEX_MODEL_FLAG,
32
- CODEX_PROMPT_FROM_STDIN,
33
- CODEX_READ_ONLY_SANDBOX,
34
- CODEX_REASONING_CONFIG_TEMPLATE,
35
- CODEX_RESUME_SUBCOMMAND,
36
- CODEX_SANDBOX_FLAG,
37
- USAGE_PROBE_FILENAME,
38
- USAGE_PROBE_PACKAGE_DIRECTORY_NAME,
39
- USAGE_PROBE_SCRIPTS_DIRECTORY_NAME,
40
- USAGE_PROBE_SHARED_DIRECTORY_NAME,
41
- SOL_BIND_FAILURE_REASON,
42
- SOL_CODEX_TIMEOUT_REASON,
43
- SOL_CODEX_TIMEOUT_SECONDS,
44
- SOL_EFFORT_FLAG,
45
- SOL_ENABLE_FLAG,
46
- SOL_ENV_VAR,
47
- SOL_EXECUTABLE_NOT_FOUND_REASON,
48
- SOL_FALLBACK_KIND_BROKEN,
49
- SOL_FALLBACK_KIND_DECLINED,
50
- SOL_INVALID_SIGNAL_REASON,
51
- SOL_MALFORMED_JSONL_REASON,
52
- SOL_MISSING_SESSION_REASON,
53
- SOL_PREFLIGHT_FAILURE_REASON,
54
- SOL_PROBE_TIMEOUT_REASON,
55
- SOL_REPLY_FAILURE_REASON,
56
- SOL_SESSION_ID_METAVAR,
57
- SOL_USAGE_PROBE_TIMEOUT_SECONDS,
58
- )
59
- from advisor_scripts_constants.advisor_route_constants import ( # noqa: E402
60
- ADVISOR_CODEX_MODEL_ID,
61
- ADVISOR_EFFORT_DEFAULT,
62
- ADVISOR_EFFORT_ENV_VAR,
63
- ADVISOR_FALLBACK_RESULT,
64
- ADVISOR_FALLBACK_TIER,
65
- ADVISOR_MODEL_TIER,
66
- ALL_ADVISOR_EFFORT_LEVELS,
67
- ALL_ADVISOR_GUIDANCE_SIGNALS,
68
- CODEX_BIND_SUCCESS_TOKEN,
69
- SPAWN_OUTCOME_KEY,
70
- )
71
-
72
-
73
- @dataclass(frozen=True)
74
- class SolPreflight:
75
- """Record the weekly-meter decision made before a Sol attempt."""
76
-
77
- eligible: bool
78
- percent_left: float | None
79
- reason: str
80
- fallback_kind: str | None = None
81
-
82
-
83
- @dataclass(frozen=True)
84
- class CodexSolAdvisorReply:
85
- """Record a parsed Codex advisor response or an explicit fallback."""
86
-
87
- session_id: str | None
88
- guidance: str | None
89
- successful: bool
90
- reason: str | None
91
- is_fallback: bool
92
- signal: str | None
93
- sol_enabled: bool
94
- selected_tier: str
95
- outcome: str
96
- fallback_kind: str | None
97
-
98
-
99
- def _preflight_fallback(
100
- reason: str,
101
- percent_left: float | None,
102
- fallback_kind: str = SOL_FALLBACK_KIND_BROKEN,
103
- ) -> SolPreflight:
104
- return SolPreflight(False, percent_left, reason, fallback_kind)
105
-
106
-
107
- def _reply_fallback(
108
- reason: str,
109
- is_sol_enabled: bool,
110
- fallback_kind: str | None = SOL_FALLBACK_KIND_BROKEN,
111
- ) -> CodexSolAdvisorReply:
112
- return CodexSolAdvisorReply(
113
- session_id=None,
114
- guidance=None,
115
- successful=False,
116
- reason=reason,
117
- is_fallback=True,
118
- signal=None,
119
- sol_enabled=is_sol_enabled,
120
- selected_tier=ADVISOR_FALLBACK_TIER,
121
- outcome=ADVISOR_FALLBACK_RESULT,
122
- fallback_kind=fallback_kind,
123
- )
124
-
125
-
126
- def _reply_success(
127
- session_id: str,
128
- guidance: str,
129
- signal: str,
130
- ) -> CodexSolAdvisorReply:
131
- return CodexSolAdvisorReply(
132
- session_id=session_id,
133
- guidance=guidance,
134
- successful=True,
135
- reason=None,
136
- is_fallback=False,
137
- signal=signal,
138
- sol_enabled=True,
139
- selected_tier=ADVISOR_MODEL_TIER,
140
- outcome=CODEX_BIND_SUCCESS_TOKEN,
141
- fallback_kind=None,
142
- )
143
-
144
-
145
- def _resolved_setting_by_name(
146
- setting_by_name: Mapping[str, str] | None,
147
- ) -> Mapping[str, str]:
148
- return os.environ if setting_by_name is None else setting_by_name
149
-
150
-
151
- def is_sol_advisor_enabled(
152
- setting_by_name: Mapping[str, str] | None,
153
- ) -> bool:
154
- """Return whether the optional Sol rung is enabled.
155
-
156
- Args:
157
- setting_by_name: Optional environment-like settings mapping.
158
-
159
- Returns:
160
- Whether the Sol feature flag contains a recognized truthy value.
161
- """
162
- resolved_setting_by_name = _resolved_setting_by_name(setting_by_name)
163
- return (
164
- resolved_setting_by_name.get(SOL_ENV_VAR, "").strip().lower()
165
- in ALL_SOL_TRUTHY_VALUES
166
- )
167
-
168
-
169
- def resolve_advisor_effort(
170
- setting_by_name: Mapping[str, str] | None,
171
- ) -> str:
172
- """Return the shared advisor effort from settings, or the default.
173
-
174
- ::
175
-
176
- resolve_advisor_effort({"ADVISOR_EFFORT": "medium"})
177
- # ok: "medium"
178
- resolve_advisor_effort({"ADVISOR_EFFORT": "MAX"})
179
- # ok: "max"
180
- resolve_advisor_effort({})
181
- # ok: default low
182
- resolve_advisor_effort({"ADVISOR_EFFORT": "nope"})
183
- # ok: default low
184
- resolve_advisor_effort({"ADVISOR_SOL_EFFORT": "high"})
185
- # ok: default low
186
-
187
- Fable and Sol both read this value. Unset and unrecognized values use
188
- low. The Sol-only name does not set effort.
189
-
190
- Args:
191
- setting_by_name: Optional environment-like settings mapping.
192
-
193
- Returns:
194
- One of low, medium, high, xhigh, or max.
195
- """
196
- resolved_setting_by_name = _resolved_setting_by_name(setting_by_name)
197
- requested_effort = (
198
- resolved_setting_by_name.get(ADVISOR_EFFORT_ENV_VAR, "").strip().lower()
199
- )
200
- if requested_effort in ALL_ADVISOR_EFFORT_LEVELS:
201
- return requested_effort
202
- return ADVISOR_EFFORT_DEFAULT
203
-
204
-
205
- def resolve_usage_probe_path(home_directory: Path) -> Path:
206
- """Return the installed Codex weekly usage probe path.
207
-
208
- ::
209
-
210
- resolve_usage_probe_path(Path.home())
211
- -> ~/.claude/_shared/pr-loop/scripts/codex_usage_probe.py
212
-
213
- This is the only probe path the Sol helper uses. Do not search worktrees
214
- or archived ``skills/codex-review`` trees for a second copy.
215
-
216
- Args:
217
- home_directory: Home directory used to construct the path.
218
-
219
- Returns:
220
- The path to the installed weekly usage probe.
221
- """
222
- return (
223
- home_directory
224
- / CLAUDE_CONFIG_DIRECTORY_NAME
225
- / USAGE_PROBE_SHARED_DIRECTORY_NAME
226
- / USAGE_PROBE_PACKAGE_DIRECTORY_NAME
227
- / USAGE_PROBE_SCRIPTS_DIRECTORY_NAME
228
- / USAGE_PROBE_FILENAME
229
- )
230
-
231
-
232
- def _load_usage_gate(probe_path: Path) -> Callable[[float], bool]:
233
- probe_directory = str(probe_path.parent)
234
- if probe_directory not in sys.path:
235
- sys.path.insert(0, probe_directory)
236
- usage_probe_module = importlib.import_module("codex_usage_probe")
237
- return usage_probe_module.is_codex_review_required
238
-
239
-
240
- def _parse_probe_percent(stdout_text: str) -> tuple[float | None, str | None]:
241
- try:
242
- usage_report = json.loads(stdout_text)
243
- except (TypeError, json.JSONDecodeError):
244
- return None, "usage report is malformed"
245
- if not isinstance(usage_report, dict):
246
- return None, "usage report is malformed"
247
- raw_percent_left = usage_report.get("percent_left")
248
- if raw_percent_left is None:
249
- return None, "usage meter is unknown"
250
- if isinstance(raw_percent_left, bool) or not isinstance(
251
- raw_percent_left, (int, float)
252
- ):
253
- return None, "usage meter is malformed"
254
- percent_left = float(raw_percent_left)
255
- if not math.isfinite(percent_left):
256
- return None, "usage meter is malformed"
257
- return percent_left, None
258
-
259
-
260
- def run_sol_preflight(
261
- probe_path: Path,
262
- process_runner: Callable[..., subprocess.CompletedProcess[str]],
263
- ) -> SolPreflight:
264
- """Run the existing usage probe and require a finite meter above its gate.
265
-
266
- Args:
267
- probe_path: Path to the installed usage probe.
268
- process_runner: Callable used to execute the probe.
269
-
270
- Returns:
271
- The usage-meter eligibility decision and any fallback reason.
272
- """
273
- try:
274
- completed_process = process_runner(
275
- [sys.executable, str(probe_path)],
276
- capture_output=True,
277
- text=True,
278
- check=False,
279
- shell=False,
280
- timeout=SOL_USAGE_PROBE_TIMEOUT_SECONDS,
281
- )
282
- if completed_process.returncode != 0:
283
- return _preflight_fallback(
284
- f"{SOL_PREFLIGHT_FAILURE_REASON}: probe exit {completed_process.returncode}",
285
- None,
286
- )
287
- percent_left, parse_reason = _parse_probe_percent(completed_process.stdout)
288
- if parse_reason is not None:
289
- return _preflight_fallback(
290
- f"{SOL_PREFLIGHT_FAILURE_REASON}: {parse_reason}", None
291
- )
292
- usage_gate = _load_usage_gate(probe_path)
293
- if not callable(usage_gate) or percent_left is None:
294
- return _preflight_fallback(
295
- f"{SOL_PREFLIGHT_FAILURE_REASON}: usage meter is unknown", None
296
- )
297
- if not usage_gate(percent_left):
298
- return _preflight_fallback(
299
- f"{SOL_PREFLIGHT_FAILURE_REASON}: usage meter is at or below the gate",
300
- percent_left,
301
- fallback_kind=SOL_FALLBACK_KIND_DECLINED,
302
- )
303
- except subprocess.TimeoutExpired as probe_error:
304
- return _preflight_fallback(f"{SOL_PROBE_TIMEOUT_REASON}: {probe_error}", None)
305
- except (
306
- OSError,
307
- subprocess.SubprocessError,
308
- ImportError,
309
- AttributeError,
310
- TypeError,
311
- ValueError,
312
- ) as probe_error:
313
- return _preflight_fallback(
314
- f"{SOL_PREFLIGHT_FAILURE_REASON}: {probe_error}", None
315
- )
316
- return SolPreflight(True, percent_left, "usage meter is above the Sol gate")
317
-
318
-
319
- def resolve_codex_executable(
320
- setting_by_name: Mapping[str, str] | None,
321
- ) -> str | None:
322
- """Resolve the Codex CLI executable to an invocable name or path.
323
-
324
- A bare "codex" name fails Windows `CreateProcess`, since the npm shim
325
- directory holds only `codex` (a sh script), `codex.cmd`, and `codex.ps1`.
326
- `shutil.which` finds `codex.cmd` via `PATHEXT`. An explicit override
327
- always wins and is trusted without a `which` check.
328
-
329
- Args:
330
- setting_by_name: Optional environment-like settings mapping.
331
-
332
- Returns:
333
- An invocable executable name or path, or None when unresolved.
334
- """
335
- resolved_setting_by_name = _resolved_setting_by_name(setting_by_name)
336
- executable_override = resolved_setting_by_name.get(ADVISOR_CODEX_EXECUTABLE_ENV_VAR, "").strip()
337
- if executable_override:
338
- return executable_override
339
- return shutil.which(CODEX_EXECUTABLE)
340
-
341
-
342
- def build_codex_arguments(
343
- codex_executable: str,
344
- session_id: str | None = None,
345
- reasoning_effort: str = ADVISOR_EFFORT_DEFAULT,
346
- ) -> list[str]:
347
- """Build the installed CLI's shell-free bind or resume argv.
348
-
349
- Args:
350
- codex_executable: Resolved executable name or path to invoke.
351
- session_id: Optional existing session to resume.
352
- reasoning_effort: Codex model_reasoning_effort token.
353
-
354
- Returns:
355
- The shell-free Codex command argument vector.
356
- """
357
- command_arguments = [
358
- codex_executable,
359
- CODEX_EXEC_SUBCOMMAND,
360
- CODEX_MODEL_FLAG,
361
- ADVISOR_CODEX_MODEL_ID,
362
- CODEX_CONFIG_FLAG,
363
- CODEX_REASONING_CONFIG_TEMPLATE.format(effort=reasoning_effort),
364
- CODEX_SANDBOX_FLAG,
365
- CODEX_READ_ONLY_SANDBOX,
366
- CODEX_JSON_FLAG,
367
- ]
368
- if session_id is not None:
369
- command_arguments.extend([CODEX_RESUME_SUBCOMMAND, session_id])
370
- command_arguments.append(CODEX_PROMPT_FROM_STDIN)
371
- return command_arguments
372
-
373
-
374
- def _guidance_signal(guidance: str) -> str | None:
375
- for each_line in guidance.splitlines():
376
- stripped_line = each_line.strip()
377
- if not stripped_line:
378
- continue
379
- return stripped_line if stripped_line in ALL_ADVISOR_GUIDANCE_SIGNALS else None
380
- return None
381
-
382
-
383
- def parse_codex_jsonl_reply(
384
- jsonl_text: str,
385
- existing_session_id: str | None,
386
- is_sol_enabled: bool,
387
- ) -> CodexSolAdvisorReply:
388
- """Parse strict Codex JSONL into a session id and final guidance.
389
-
390
- Args:
391
- jsonl_text: JSONL emitted by the Codex CLI.
392
- existing_session_id: Optional session id required on resume.
393
- is_sol_enabled: Whether the attempted route had Sol enabled.
394
-
395
- Returns:
396
- The parsed guidance or an explicit Fable fallback reply.
397
- """
398
- discovered_session_id: str | None = None
399
- final_guidance: str | None = None
400
- try:
401
- for each_line in jsonl_text.splitlines():
402
- if not each_line.strip():
403
- continue
404
- event = json.loads(each_line)
405
- if not isinstance(event, dict):
406
- return _reply_fallback(SOL_MALFORMED_JSONL_REASON, is_sol_enabled)
407
- if event.get("type") == "thread.started":
408
- thread_id = event.get("thread_id")
409
- if isinstance(thread_id, str) and thread_id.strip():
410
- discovered_session_id = thread_id.strip()
411
- completed_event = event.get("item")
412
- if (
413
- event.get("type") == "item.completed"
414
- and isinstance(completed_event, dict)
415
- and completed_event.get("type") == "agent_message"
416
- and isinstance(completed_event.get("text"), str)
417
- ):
418
- final_guidance = completed_event["text"].strip()
419
- except (TypeError, json.JSONDecodeError):
420
- return _reply_fallback(SOL_MALFORMED_JSONL_REASON, is_sol_enabled)
421
- if discovered_session_id is None:
422
- return _reply_fallback(SOL_MISSING_SESSION_REASON, is_sol_enabled)
423
- if (
424
- existing_session_id is not None
425
- and discovered_session_id != existing_session_id
426
- ):
427
- return _reply_fallback(SOL_MISSING_SESSION_REASON, is_sol_enabled)
428
- if not final_guidance:
429
- return _reply_fallback(SOL_REPLY_FAILURE_REASON, is_sol_enabled)
430
- guidance_signal = _guidance_signal(final_guidance)
431
- if guidance_signal is None:
432
- return _reply_fallback(SOL_INVALID_SIGNAL_REASON, is_sol_enabled)
433
- return _reply_success(discovered_session_id, final_guidance, guidance_signal)
434
-
435
-
436
- def _resolve_sol_preflight(
437
- preflight: SolPreflight | None,
438
- probe_path: Path | None,
439
- process_runner: Callable[..., subprocess.CompletedProcess[str]],
440
- ) -> SolPreflight:
441
- if preflight is not None:
442
- return preflight
443
- resolved_probe_path = (
444
- resolve_usage_probe_path(Path.home()) if probe_path is None else probe_path
445
- )
446
- return run_sol_preflight(
447
- probe_path=resolved_probe_path, process_runner=process_runner
448
- )
449
-
450
-
451
- def run_codex_sol_advisor(
452
- prompt: str,
453
- working_directory: Path,
454
- preflight: SolPreflight | None,
455
- probe_path: Path | None,
456
- setting_by_name: Mapping[str, str] | None,
457
- session_id: str | None,
458
- process_runner: Callable[..., subprocess.CompletedProcess[str]],
459
- ) -> CodexSolAdvisorReply:
460
- """Run one usage-gated read-only Sol bind or resume attempt.
461
-
462
- Args:
463
- prompt: Advisor charter or delta consult sent to Codex.
464
- working_directory: Repository directory supplied to the CLI.
465
- preflight: Optional precomputed usage-meter decision.
466
- probe_path: Optional installed usage-probe path.
467
- setting_by_name: Optional environment-like settings mapping.
468
- session_id: Existing session id for a resume attempt.
469
- process_runner: Callable used to execute the probe and Codex.
470
-
471
- Returns:
472
- The parsed Sol guidance or an explicit Fable fallback reply.
473
- """
474
- if not is_sol_advisor_enabled(setting_by_name):
475
- return _reply_fallback(
476
- "Sol advisor flag is disabled",
477
- False,
478
- fallback_kind=SOL_FALLBACK_KIND_DECLINED,
479
- )
480
- codex_executable = resolve_codex_executable(setting_by_name)
481
- if codex_executable is None:
482
- return _reply_fallback(SOL_EXECUTABLE_NOT_FOUND_REASON, True)
483
- resolved_preflight = _resolve_sol_preflight(preflight, probe_path, process_runner)
484
- if not resolved_preflight.eligible:
485
- return _reply_fallback(
486
- resolved_preflight.reason,
487
- True,
488
- fallback_kind=resolved_preflight.fallback_kind,
489
- )
490
- try:
491
- completed_process = process_runner(
492
- build_codex_arguments(
493
- codex_executable,
494
- session_id=session_id,
495
- reasoning_effort=resolve_advisor_effort(setting_by_name),
496
- ),
497
- cwd=str(working_directory),
498
- input=prompt,
499
- capture_output=True,
500
- text=True,
501
- check=False,
502
- shell=False,
503
- timeout=SOL_CODEX_TIMEOUT_SECONDS,
504
- )
505
- except subprocess.TimeoutExpired as bind_error:
506
- return _reply_fallback(f"{SOL_CODEX_TIMEOUT_REASON}: {bind_error}", True)
507
- except (OSError, subprocess.SubprocessError) as bind_error:
508
- return _reply_fallback(f"{SOL_BIND_FAILURE_REASON}: {bind_error}", True)
509
- if completed_process.returncode != 0:
510
- return _reply_fallback(
511
- f"{SOL_BIND_FAILURE_REASON}: process exit {completed_process.returncode}",
512
- True,
513
- )
514
- return parse_codex_jsonl_reply(
515
- completed_process.stdout,
516
- existing_session_id=session_id,
517
- is_sol_enabled=True,
518
- )
519
-
520
-
521
- def build_argument_parser() -> argparse.ArgumentParser:
522
- """Build the command-line parser for Sol bind and resume.
523
-
524
- Returns:
525
- The parser for the helper's bind and resume modes.
526
- """
527
- argument_parser = argparse.ArgumentParser(
528
- description="Bind or consult a read-only Codex Sol advisor."
529
- )
530
- mode_group = argument_parser.add_mutually_exclusive_group(required=True)
531
- mode_group.add_argument("--bind", action="store_true")
532
- mode_group.add_argument("--resume", metavar=SOL_SESSION_ID_METAVAR)
533
- argument_parser.add_argument("--cwd", required=True, type=Path)
534
- argument_parser.add_argument(
535
- SOL_ENABLE_FLAG,
536
- dest="is_sol_requested",
537
- action="store_true",
538
- help="Open the Sol rung for this invocation without an environment flag.",
539
- )
540
- argument_parser.add_argument(
541
- SOL_EFFORT_FLAG,
542
- dest="sol_effort",
543
- choices=ALL_ADVISOR_EFFORT_LEVELS,
544
- default=None,
545
- help="Shared advisor effort for this invocation.",
546
- )
547
- return argument_parser
548
-
549
-
550
- def main(all_cli_arguments: Sequence[str]) -> int:
551
- """Run one bind or resume from stdin and print a JSON response.
552
-
553
- Args:
554
- all_cli_arguments: Command-line arguments without the program name.
555
-
556
- Returns:
557
- Zero for a successful Sol response, or one for an explicit fallback.
558
- """
559
- parsed_arguments = build_argument_parser().parse_args(list(all_cli_arguments))
560
- setting_by_name: dict[str, str] = dict(os.environ)
561
- if parsed_arguments.is_sol_requested:
562
- setting_by_name[SOL_ENV_VAR] = "1"
563
- if parsed_arguments.sol_effort is not None:
564
- setting_by_name[ADVISOR_EFFORT_ENV_VAR] = parsed_arguments.sol_effort
565
- advisor_reply = run_codex_sol_advisor(
566
- prompt=sys.stdin.read(),
567
- working_directory=parsed_arguments.cwd,
568
- preflight=None,
569
- probe_path=None,
570
- setting_by_name=setting_by_name,
571
- session_id=parsed_arguments.resume if not parsed_arguments.bind else None,
572
- process_runner=subprocess.run,
573
- )
574
- reply_payload = asdict(advisor_reply)
575
- reply_payload[SPAWN_OUTCOME_KEY] = reply_payload.pop("outcome")
576
- print(json.dumps(reply_payload, sort_keys=True))
577
- return 0 if advisor_reply.successful else 1
578
-
579
-
580
- if __name__ == "__main__":
581
- raise SystemExit(main(sys.argv[1:]))
@@ -1,38 +0,0 @@
1
- """Constants for the optional Codex Sol advisor bind."""
2
-
3
- from __future__ import annotations
4
-
5
- CODEX_EXECUTABLE: str = "codex"
6
- ADVISOR_CODEX_EXECUTABLE_ENV_VAR: str = "ADVISOR_CODEX_EXECUTABLE"
7
- CODEX_READ_ONLY_SANDBOX: str = "read-only"
8
- CODEX_JSON_FLAG: str = "--json"
9
- CODEX_MODEL_FLAG: str = "--model"
10
- CODEX_CONFIG_FLAG: str = "--config"
11
- CODEX_REASONING_CONFIG_TEMPLATE: str = 'model_reasoning_effort="{effort}"'
12
- CODEX_PROMPT_FROM_STDIN: str = "-"
13
- CODEX_EXEC_SUBCOMMAND: str = "exec"
14
- CODEX_RESUME_SUBCOMMAND: str = "resume"
15
- CODEX_SANDBOX_FLAG: str = "--sandbox"
16
- CLAUDE_CONFIG_DIRECTORY_NAME: str = ".claude"
17
- USAGE_PROBE_SHARED_DIRECTORY_NAME: str = "_shared"
18
- USAGE_PROBE_PACKAGE_DIRECTORY_NAME: str = "pr-loop"
19
- USAGE_PROBE_SCRIPTS_DIRECTORY_NAME: str = "scripts"
20
- USAGE_PROBE_FILENAME: str = "codex_usage_probe.py"
21
- SOL_SESSION_ID_METAVAR: str = "SESSION_ID"
22
- SOL_CODEX_TIMEOUT_SECONDS: float = 120.0
23
- SOL_USAGE_PROBE_TIMEOUT_SECONDS: float = 30.0
24
- SOL_ENV_VAR: str = "ADVISOR_SOL"
25
- ALL_SOL_TRUTHY_VALUES: frozenset[str] = frozenset({"1", "true", "yes", "on"})
26
- SOL_PREFLIGHT_FAILURE_REASON: str = "sol preflight did not establish an eligible Codex meter"
27
- SOL_BIND_FAILURE_REASON: str = "Codex Sol bind failed"
28
- SOL_REPLY_FAILURE_REASON: str = "Codex Sol returned no advisor guidance"
29
- SOL_PROBE_TIMEOUT_REASON: str = "Codex Sol meter check timed out"
30
- SOL_CODEX_TIMEOUT_REASON: str = "Codex Sol request timed out"
31
- SOL_MALFORMED_JSONL_REASON: str = "Codex Sol returned malformed JSONL"
32
- SOL_MISSING_SESSION_REASON: str = "Codex Sol returned no session id"
33
- SOL_INVALID_SIGNAL_REASON: str = "Codex Sol returned an invalid guidance signal"
34
- SOL_EXECUTABLE_NOT_FOUND_REASON: str = "Codex Sol could not find the codex executable on PATH"
35
- SOL_FALLBACK_KIND_DECLINED: str = "declined"
36
- SOL_FALLBACK_KIND_BROKEN: str = "broken"
37
- SOL_ENABLE_FLAG: str = "--enable-sol"
38
- SOL_EFFORT_FLAG: str = "--effort"