claude-dev-env 2.35.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.
- package/.agents/skills/_shared/advisor/scripts/README.md +1 -1
- package/.agents/skills/autoconverge/SKILL.md +19 -15
- package/.agents/skills/autoconverge/reference/gotchas.md +3 -3
- package/.agents/skills/autoconverge/reference/multi-pr.md +6 -2
- package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +11 -0
- package/.agents/skills/autoconverge/workflow/converge.mjs +26 -33
- package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +26 -2
- package/.agents/skills/autoconverge/workflow/converge_multi.mjs +12 -28
- package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +16 -0
- package/.agents/skills/team-advisor/SKILL.md +11 -11
- package/_shared/advisor/advisor-protocol.md +51 -51
- package/_shared/advisor/reference/advisor-block.md +9 -9
- package/_shared/advisor/reference/astra-rung.md +39 -0
- package/_shared/advisor/reference/identity.md +4 -4
- package/_shared/advisor/reference/lifecycle.md +6 -6
- package/_shared/advisor/reference/spawn-walk-log.md +9 -9
- package/_shared/advisor/reference/third-party-bind.md +11 -11
- package/_shared/advisor/reference/warm-up.md +13 -13
- package/_shared/advisor/scripts/codex_astra_advisor.py +308 -0
- package/_shared/advisor/scripts/codex_astra_preflight.py +115 -0
- package/_shared/advisor/scripts/codex_astra_reply.py +159 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/advisor_route_constants.py +3 -3
- package/_shared/advisor/scripts/config/advisor_scripts_constants/astra_advisor_constants.py +38 -0
- package/_shared/advisor/scripts/model_tier_run_validator.py +17 -18
- package/_shared/advisor/scripts/tests/test_codex_astra_advisor.py +271 -0
- package/_shared/advisor/scripts/tests/test_codex_astra_preflight.py +24 -0
- package/_shared/advisor/scripts/tests/test_codex_astra_reply.py +42 -0
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +55 -30
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +14 -4
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +14 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +138 -50
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +13 -1
- package/bin/cde.mjs +175 -0
- package/bin/cde.test.mjs +222 -0
- package/bin/install-constants.mjs +4 -0
- package/bin/install.cursor-rules.test.mjs +233 -2
- package/bin/install.mjs +49 -3
- package/bin/pstack-model-preferences.codex.json +23 -0
- package/bin/select-pstack-models.test.mjs +378 -0
- package/docs/references/team-advisor-skill.md +3 -3
- package/hooks/hooks_constants/tool_names.py +20 -0
- package/hooks/validators/README.md +35 -1
- package/hooks/validators/fast_save_validators.py +63 -89
- package/hooks/validators/run_all_validators.py +16 -8
- package/hooks/validators/test_fast_save_validators_policy_lint.py +25 -0
- package/hooks/validators/test_run_all_validators_policy_lint.py +52 -0
- package/package.json +3 -2
- package/rules/pstack-models.md +119 -0
- package/scripts/cde_lint.py +258 -0
- package/scripts/check.ps1 +18 -6
- package/scripts/policy_lint/__init__.py +41 -0
- package/scripts/policy_lint/adapter_configuration.py +91 -0
- package/scripts/policy_lint/adapter_detectors.py +368 -0
- package/scripts/policy_lint/adapter_pairing.py +145 -0
- package/scripts/policy_lint/adapter_support.py +97 -0
- package/scripts/policy_lint/adapters.py +376 -0
- package/scripts/policy_lint/config/__init__.py +1 -0
- package/scripts/policy_lint/config/constants.py +77 -0
- package/scripts/policy_lint/engine.py +224 -0
- package/scripts/policy_lint/model.py +388 -0
- package/scripts/policy_lint/registry.py +171 -0
- package/scripts/policy_lint/render.py +91 -0
- package/scripts/policy_lint/selection.py +372 -0
- package/scripts/policy_lint/selection_git.py +147 -0
- package/scripts/repository_checks/__init__.py +1 -0
- package/scripts/repository_checks/claude_md.py +108 -0
- package/scripts/repository_checks/config/__init__.py +1 -0
- package/scripts/repository_checks/config/constants.py +150 -0
- package/scripts/repository_checks/env_var_documentation.py +243 -0
- package/scripts/repository_checks/hook_modules.py +34 -0
- package/scripts/repository_checks/models.py +37 -0
- package/scripts/repository_checks/package_inventory.py +118 -0
- package/scripts/repository_checks/pytest_testpaths.py +84 -0
- package/scripts/repository_checks/reporting.py +39 -0
- package/scripts/repository_checks/runner.py +93 -0
- package/scripts/repository_checks/tracked_secrets.py +147 -0
- package/scripts/repository_checks/tracked_tree.py +28 -0
- package/scripts/repository_policy.py +120 -0
- package/scripts/select_pstack_models.mjs +304 -0
- package/scripts/tests/repository_policy_test_support.py +89 -0
- package/scripts/tests/test_cde_lint.py +398 -0
- package/scripts/tests/test_claude_md.py +68 -0
- package/scripts/tests/test_env_var_documentation.py +133 -0
- package/scripts/tests/test_hook_modules.py +17 -0
- package/scripts/tests/test_models.py +26 -0
- package/scripts/tests/test_package_inventory.py +96 -0
- package/scripts/tests/test_policy_lint_rules_engine.py +381 -0
- package/scripts/tests/test_policy_lint_rules_registry.py +369 -0
- package/scripts/tests/test_policy_lint_selection.py +373 -0
- package/scripts/tests/test_pytest_testpaths.py +66 -0
- package/scripts/tests/test_reporting.py +54 -0
- package/scripts/tests/test_repository_policy.py +71 -0
- package/scripts/tests/test_runner.py +59 -0
- package/scripts/tests/test_tracked_secrets.py +175 -0
- package/scripts/tests/test_tracked_tree.py +28 -0
- package/_shared/advisor/reference/sol-rung.md +0 -39
- package/_shared/advisor/scripts/codex_sol_advisor.py +0 -581
- package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +0 -38
- package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +0 -740
|
@@ -3,26 +3,26 @@
|
|
|
3
3
|
Detail behind the **Advisor block** section of [`advisor-protocol.md`](../advisor-protocol.md).
|
|
4
4
|
Open this when assembling the block for an executor spawn prompt.
|
|
5
5
|
|
|
6
|
-
Assembly order: one transport preamble picked by host profile, then the shared core, then
|
|
6
|
+
Assembly order: one transport preamble picked by host profile, then the shared core, then the weak-executor add-on for an executor at Sonnet or below.
|
|
7
7
|
Paste the assembled block at the **top** of the spawn prompt, ahead of any other sentence that mentions the advisor.
|
|
8
|
-
The assembled block is self-contained
|
|
8
|
+
The assembled block is self-contained. The executor receives this text alone, so every path it names is absolute.
|
|
9
9
|
The consult rules it restates are owned by [`consult-format.md`](consult-format.md); edit that file first and carry the change here.
|
|
10
10
|
|
|
11
|
-
## Transport preamble
|
|
11
|
+
## Transport preamble for a Claude host
|
|
12
12
|
|
|
13
13
|
> A shared session advisor named `<name>` is reachable via SendMessage; send each consult to it directly by that name.
|
|
14
14
|
|
|
15
|
-
## Transport preamble
|
|
15
|
+
## Transport preamble for a Codex host
|
|
16
16
|
|
|
17
|
-
> A shared session advisor named `<name>` is reachable as a native Codex
|
|
17
|
+
> A shared session advisor named `<name>` is reachable as a native Codex Astra subagent; send each consult to it in-session by that name.
|
|
18
18
|
|
|
19
|
-
## Transport preamble
|
|
19
|
+
## Transport preamble for a third-party host
|
|
20
20
|
|
|
21
21
|
> The orchestrating session owns a standing advisor for this run.
|
|
22
|
-
> The advisor chain: Claude Fable, then
|
|
22
|
+
> The advisor chain: Claude Fable, then Astra through the Codex CLI when Fable is out of usage and the Astra flag and its preflight open that rung. Both rungs use `ADVISOR_EFFORT` (default low). When neither binds, there is no advisor.
|
|
23
23
|
> The orchestrating session is your one path to it: send each consult as a report to the session that assigned you, and it relays the advisor's reply.
|
|
24
24
|
|
|
25
|
-
## Shared core
|
|
25
|
+
## Shared core for every host
|
|
26
26
|
|
|
27
27
|
> Consult before locking in a nontrivial approach, once you believe your assignment is done, before any hard-to-reverse action, when the same failure repeats or progress has stalled, and when the chosen approach is being reconsidered.
|
|
28
28
|
> Build the first consult with the complete format in `$HOME/.claude/_shared/advisor/reference/consult-format.md`: assignment, desired outcome, constraints and exclusions, actions taken in order, real output and current state, live decision or blocker, validation evidence, unresolved risks, and load-bearing paths or excerpts. Later consults carry only changed evidence.
|
|
@@ -31,7 +31,7 @@ The consult rules it restates are owned by [`consult-format.md`](consult-format.
|
|
|
31
31
|
> Replies open with one of ENDORSE, CORRECTION, PLAN, or STOP — treat CORRECTION and PLAN as actions to take.
|
|
32
32
|
> On STOP, or when the advisor is unreachable, stop and report that back to whoever assigned you; advisor binding and the four signals stay with the session that owns the advisor.
|
|
33
33
|
|
|
34
|
-
## Weak-executor add-on
|
|
34
|
+
## Weak-executor add-on for Sonnet or below, either host
|
|
35
35
|
|
|
36
36
|
> Send your first consult right after orientation and before your first write.
|
|
37
37
|
> Send a completion consult once your writes and test output exist — that consult asks the advisor to hunt for missing requirements, untested behavior, wrong assumptions, unhandled edge cases, evidence gaps, and early completion claims.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Astra rung
|
|
2
|
+
|
|
3
|
+
Detail behind the **Host profiles → Astra rung** section of [`advisor-protocol.md`](../advisor-protocol.md).
|
|
4
|
+
Open this when Fable is out of usage on a Claude or ThirdParty host and an Astra fallback bind is starting.
|
|
5
|
+
On a Codex host, Astra is the in-session default. See [`identity.md`](identity.md).
|
|
6
|
+
|
|
7
|
+
## Flag
|
|
8
|
+
|
|
9
|
+
`ADVISOR_ASTRA=1` (or `true` / `yes` / `on`) opens the Astra fallback after Fable is out of usage. Set the variable in the helper's process environment, or pass `--enable-astra` on the helper invocation. The CLI flag opens the rung for that run without touching the environment.
|
|
10
|
+
Flag off both ways: fail closed when Fable did not bind.
|
|
11
|
+
A Windows `setx` write only updates the persisted user environment; only a process started after that write inherits the new value, so an already-running session either sets the flag in its own invoking process environment or passes `--enable-astra`.
|
|
12
|
+
|
|
13
|
+
## Effort
|
|
14
|
+
|
|
15
|
+
`ADVISOR_EFFORT` selects Codex `model_reasoning_effort` for Astra and `--effort` for Fable: `low`, `medium`, `high`, `xhigh`, or `max`. The default is `low`, which sends `model_reasoning_effort="low"`. Pass `--effort <level>` on the helper to set effort for that Astra run without changing the environment. An unset or unrecognized value uses `low`.
|
|
16
|
+
|
|
17
|
+
Every fallback reply carries a `fallback_kind` field. `declined` means policy closed the rung (flag off, usage meter at or below the gate). `broken` means the Astra path itself failed (missing executable, spawn error, timeout, malformed reply). A `broken` fallback is a defect to report.
|
|
18
|
+
|
|
19
|
+
## Preflight
|
|
20
|
+
|
|
21
|
+
Flag on: run the Codex preflight first.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
python ~/.claude/_shared/pr-loop/scripts/codex_usage_probe.py
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**GOTCHA. Probe path.** That installed path is the only one. `codex_astra_advisor.resolve_usage_probe_path` builds `~/.claude/_shared/pr-loop/scripts/codex_usage_probe.py` and stops. Do not hunt `skills/codex-review/`, Codex worktrees under `C:\dev\.codex\worktrees\`, or other copies. Repo home: `packages/claude-dev-env/_shared/pr-loop/scripts/`.
|
|
28
|
+
|
|
29
|
+
The shared entry point is `~/.claude/_shared/advisor/scripts/codex_astra_advisor.py`. It calls the installed probe and owns Astra bind or resume parsing. Bind with `python ~/.claude/_shared/advisor/scripts/codex_astra_advisor.py --bind --enable-astra --cwd <repo-root>` and pipe the charter on stdin. Resume with `--resume <session_id>` and pipe the delta consult on stdin.
|
|
30
|
+
|
|
31
|
+
The gate passes only when the probe exits 0, `percent_left` is finite numeric data, and `percent_left` is strictly greater than `WEEKLY_USAGE_GATE_THRESHOLD_PERCENT` from the existing probe. The exact-threshold case fails closed when Fable did not bind.
|
|
32
|
+
|
|
33
|
+
## Branches
|
|
34
|
+
|
|
35
|
+
**Preflight pass.** Bind one Codex CLI session at `gpt-6-astra` with `model_reasoning_effort` set from `ADVISOR_EFFORT` (default `low`), `--sandbox read-only`, and JSON output. The helper receives the standing-reviewer charter on stdin and returns only parsed ENDORSE / CORRECTION / PLAN / STOP guidance with a session ID.
|
|
36
|
+
|
|
37
|
+
**Preflight fail.** Probe failure, non-zero exit, timeout, missing or malformed usage, `null`, non-finite usage, or usage at or below the threshold fails closed when Fable did not bind.
|
|
38
|
+
|
|
39
|
+
The helper owns the Astra attempt and returns an explicit fallback result. The consuming advisor path owns the Fable bind. Apply the same gate to every Astra attempt, including resume.
|
|
@@ -9,7 +9,7 @@ Call `resolve_session_identity` from `$HOME/.claude/_shared/advisor/scripts/tier
|
|
|
9
9
|
|
|
10
10
|
| Identity text | Host profile | Bind path |
|
|
11
11
|
|---|---|---|
|
|
12
|
-
| a `codex` token | Codex | In-session
|
|
12
|
+
| a `codex` token | Codex | In-session Astra spawn |
|
|
13
13
|
| a `claude` token | Claude | In-session Fable spawn of `session-advisor` |
|
|
14
14
|
| any other identity | ThirdParty | Headless CLI chain |
|
|
15
15
|
|
|
@@ -17,11 +17,11 @@ When both `codex` and `claude` tokens appear, Codex wins. Empty text is ThirdPar
|
|
|
17
17
|
|
|
18
18
|
## Bind path
|
|
19
19
|
|
|
20
|
-
**Claude.** Spawn `subagent_type: session-advisor` at Fable through the Agent tool. When Fable is out of usage, bind
|
|
20
|
+
**Claude.** Spawn `subagent_type: session-advisor` at Fable through the Agent tool. When Fable is out of usage, bind Astra through the Codex helper if `ADVISOR_ASTRA` is on. Fail closed when neither binds.
|
|
21
21
|
|
|
22
|
-
**Codex.** Spawn a native in-session
|
|
22
|
+
**Codex.** Spawn a native in-session Astra subagent at `resolve_codex_model_id("Astra")` (`gpt-6-astra`). Walk `candidate_tiers = ["Astra"]`. Record `{tier: "Astra", result: "spawned"}` on success. The `ADVISOR_ASTRA` flag is not required. Fail closed when Astra does not bind. Do not walk Fable on a Codex host.
|
|
23
23
|
|
|
24
|
-
**ThirdParty.** Bind Fable through the CLI Claude-chain. When Fable is out of usage, bind
|
|
24
|
+
**ThirdParty.** Bind Fable through the CLI Claude-chain. When Fable is out of usage, bind Astra through the Codex helper if `ADVISOR_ASTRA` is on. Fail closed when neither binds.
|
|
25
25
|
|
|
26
26
|
## Mechanical override
|
|
27
27
|
|
|
@@ -5,7 +5,7 @@ Open this when the advisor drifts, dies, or the task pivots.
|
|
|
5
5
|
|
|
6
6
|
## Claude host
|
|
7
7
|
|
|
8
|
-
The session that spawns the shared advisor owns its whole lifecycle
|
|
8
|
+
The session that spawns the shared advisor owns its whole lifecycle: spawn, drift-respawn, and shutdown.
|
|
9
9
|
Every other consumer (executors, or any other consulting session) reaches it by message alone; spawn, respawn, and shutdown belong to the owning session.
|
|
10
10
|
One shared advisor exists per orchestrated session, owned by the session that spawned it.
|
|
11
11
|
|
|
@@ -14,15 +14,15 @@ A **Fable**-tier re-spawn carries the exact token `FABLE-SPAWN-AUTHORIZED` in th
|
|
|
14
14
|
|
|
15
15
|
## Codex host
|
|
16
16
|
|
|
17
|
-
The session that spawns the shared
|
|
17
|
+
The session that spawns the shared Astra subagent owns its whole lifecycle: spawn, drift-respawn, and shutdown.
|
|
18
18
|
Every other consumer reaches it by message alone; spawn, respawn, and shutdown belong to the owning session.
|
|
19
19
|
|
|
20
|
-
**Re-spawn on drift.** If a reply shows the agent working from a stale picture, or the session pivots to an unrelated task, the owning session ends that agent and spawns a fresh native
|
|
20
|
+
**Re-spawn on drift.** If a reply shows the agent working from a stale picture, or the session pivots to an unrelated task, the owning session ends that agent and spawns a fresh native Astra subagent with a new charter.
|
|
21
21
|
|
|
22
22
|
## Third-party host
|
|
23
23
|
|
|
24
|
-
The orchestrating session owns the
|
|
24
|
+
The orchestrating session owns the CLI advisor bind for the whole run: first bind, re-bind on drift or lost `session_id`, and fail-closed report when the bound path cannot serve.
|
|
25
25
|
|
|
26
|
-
**Re-bind on drift.** If a reply shows a stale picture, the task pivots, or `--resume` fails after a usage-limit failover (session stores are per binary/account), re-bind through `claude_chain_runner.py` with the charter plus a compact recap of consults so far.
|
|
27
|
-
Capture the new `session_id`, and log a fresh Fable walk with `result: "cli"` on success, then
|
|
26
|
+
**Re-bind on drift.** If a reply shows a stale picture, the task pivots, or `--resume` fails after a usage-limit failover (session stores are per binary/account), re-bind Fable through `claude_chain_runner.py` with the charter plus a compact recap of consults so far.
|
|
27
|
+
Capture the new `session_id`, and log a fresh Fable walk with `result: "cli"` on success, then Astra when that rung is open. An Astra re-bind uses `codex_astra_advisor.py --bind --enable-astra` with the same charter and recap, and records `result: "codex"` on success.
|
|
28
28
|
Executors keep reporting to the orchestrating session; advisor binding stays with that session alone.
|
|
@@ -6,16 +6,16 @@ Detail behind the **Model floor** section of [`advisor-protocol.md`](../advisor-
|
|
|
6
6
|
|
|
7
7
|
Write the log as JSON with these field names:
|
|
8
8
|
|
|
9
|
-
- `own_tier
|
|
10
|
-
- `host_profile
|
|
11
|
-
- `candidate_tiers
|
|
12
|
-
- `
|
|
13
|
-
- `attempts
|
|
14
|
-
- `selected_tier
|
|
9
|
+
- `own_tier`: the consuming session's tier (recorded; it does not add Opus to the advisor walk).
|
|
10
|
+
- `host_profile`: `Claude`, `Codex`, or `ThirdParty`. Omit to default to `Claude`.
|
|
11
|
+
- `candidate_tiers`: on Claude and ThirdParty, Fable, plus Astra when `astra_enabled` is true. On Codex, Astra only.
|
|
12
|
+
- `astra_enabled`: a boolean recorded before candidate selection. On Claude and ThirdParty, `true` adds Astra after Fable and `false` walks Fable alone. On Codex this flag does not change the walk.
|
|
13
|
+
- `attempts`: one `{tier, result}` entry appended as each bind try happens. `result` is `codex` for an Astra helper bind, `spawned` for an in-session spawn (Claude Agent or Codex native Astra), `cli` for a CLI Claude-chain bind, or a failure reason such as `unavailable`.
|
|
14
|
+
- `selected_tier`: the tier of the first successful bind (first `codex`, `spawned`, or `cli` entry), or `null` paired with a `fallback_reason` string when none bound.
|
|
15
15
|
|
|
16
16
|
## Log path
|
|
17
17
|
|
|
18
|
-
Write to a path the session controls
|
|
18
|
+
Write to a path the session controls, typically `<job-temp-dir>/model-tier-run.json`, or the OS temp directory when no job directory exists.
|
|
19
19
|
|
|
20
20
|
## Validator
|
|
21
21
|
|
|
@@ -28,5 +28,5 @@ Exit code `1` means a ladder invariant failed.
|
|
|
28
28
|
Exit code `2` means the path or JSON was unusable.
|
|
29
29
|
The same checks are available in-process via `validate_model_tier_run(run)`.
|
|
30
30
|
|
|
31
|
-
The validator checks ladder shape only: the candidate slice, the order of bind tries, and the success-token rules per tier and host. On Claude and ThirdParty,
|
|
32
|
-
Host policy sits on top of it
|
|
31
|
+
The validator checks ladder shape only: the candidate slice, the order of bind tries, and the success-token rules per tier and host. On Claude and ThirdParty, Astra is attempted after Fable when `astra_enabled` is true, and `selected_tier: "Astra"` requires `result: "codex"`. On Codex, the walk is Astra only, and `result: "spawned"` or `result: "codex"` counts as success.
|
|
32
|
+
Host policy sits on top of it. See the Model floor section of the protocol.
|
|
@@ -3,23 +3,23 @@
|
|
|
3
3
|
Detail behind the **Host profiles → Third-party host** section of [`advisor-protocol.md`](../advisor-protocol.md).
|
|
4
4
|
Open this when binding or re-binding the advisor from a third-party (non-Claude, non-Codex) harness.
|
|
5
5
|
|
|
6
|
-
## GOTCHA
|
|
6
|
+
## GOTCHA: Cursor Astra first shot
|
|
7
7
|
|
|
8
|
-
Cursor is a third-party host. When the walk reaches
|
|
8
|
+
Cursor is a third-party host. When the walk reaches Astra (`ADVISOR_ASTRA=1` / `--enable-astra` after Fable is out of usage, or the user asks for Astra), bind Astra on the **first** tool call through the headless Codex helper. Do not use the Agent or Task tool, do not spawn Grok as a substitute, and do not search the filesystem for `codex_usage_probe.py`.
|
|
9
9
|
|
|
10
10
|
```
|
|
11
|
-
python "$HOME/.claude/_shared/advisor/scripts/
|
|
11
|
+
python "$HOME/.claude/_shared/advisor/scripts/codex_astra_advisor.py" --bind --enable-astra --cwd <repo-root>
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Pipe the standing-reviewer charter on stdin. Persist `session_id` from the JSON reply. Later consults: `--resume <session_id>` with the delta on stdin. Capture helper / `codex` stdout as UTF-8 on Windows (`encoding="utf-8"`, `errors="replace"`). Probe path and preflight: [`
|
|
14
|
+
Pipe the standing-reviewer charter on stdin. Persist `session_id` from the JSON reply. Later consults: `--resume <session_id>` with the delta on stdin. Capture helper / `codex` stdout as UTF-8 on Windows (`encoding="utf-8"`, `errors="replace"`). Probe path and preflight: [`astra-rung.md`](astra-rung.md).
|
|
15
15
|
|
|
16
|
-
When the
|
|
16
|
+
When the Astra flag is off, follow the Claude-chain steps below.
|
|
17
17
|
|
|
18
18
|
## Bind steps
|
|
19
19
|
|
|
20
20
|
1. Name the session identity first (protocol **Host profiles**). This path is for a ThirdParty profile.
|
|
21
|
-
2. Walk `candidate_tiers = ["Fable"]`. When Fable is out of usage, the
|
|
22
|
-
3. **CLI bind (primary path):** for
|
|
21
|
+
2. Walk `candidate_tiers = ["Fable"]`. When Fable is out of usage, the Astra rung ([`astra-rung.md`](astra-rung.md)) binds Astra after Fable (`candidate_tiers = ["Fable", "Astra"]`).
|
|
22
|
+
3. **CLI bind (primary path):** for Fable, pipe a charter file into:
|
|
23
23
|
|
|
24
24
|
```
|
|
25
25
|
python "$HOME/.claude/scripts/claude_chain_runner.py" --routing-mode ordered_account -- -p --model <alias> --effort <effort> --output-format json
|
|
@@ -27,15 +27,15 @@ When the Sol flag is off, follow the Claude-chain steps below.
|
|
|
27
27
|
|
|
28
28
|
Use `--model fable --effort` with the value of `ADVISOR_EFFORT` (default `low`) on Fable.
|
|
29
29
|
User-facing wording follows [`rules/asd-ste100-language.md`](../../../rules/asd-ste100-language.md).
|
|
30
|
-
A root advisor bind uses `--routing-mode ordered_account
|
|
30
|
+
A root advisor bind uses `--routing-mode ordered_account`. Walk order, failover, and the `advisor_blocked` terminal status are in [`cli-chain.md`](cli-chain.md).
|
|
31
31
|
4. Stop at the first successful bind.
|
|
32
|
-
Record `{tier, result: "cli"}` and set `selected_tier` to that tier.
|
|
32
|
+
Record `{tier, result: "cli"}` for Fable or `{tier: "Astra", result: "codex"}` for the Astra helper, and set `selected_tier` to that tier.
|
|
33
33
|
Persist `session_id` from the JSON events (any event carries it; the runner also surfaces it on `ChainInvocationOutcome.session_id`; reply text is the `type == "result"` event's `.result` field).
|
|
34
|
-
Run every bind and every later consult with cwd set to the repo root the work is for
|
|
34
|
+
Run every bind and every later consult with cwd set to the repo root the work is for. Claude sessions are project-scoped by working directory.
|
|
35
35
|
5. **Fail closed:** when every candidate fails (chain exhausted, `advisor_blocked`, or model unavailable), set `selected_tier = null` and a `fallback_reason`, report that the advisor is unreachable, and **stop**. ENDORSE / CORRECTION / PLAN / STOP come only from a bound advisor.
|
|
36
36
|
6. Assemble and paste each executor's Advisor block from [`advisor-block.md`](advisor-block.md). Executors report to the orchestrating session; that session consults the bound advisor and relays the four-signal reply.
|
|
37
37
|
|
|
38
38
|
## Session model field
|
|
39
39
|
|
|
40
40
|
Resolve a third-party session's own model field with `resolve_cli_model_id("ThirdParty")` → `third-party` when a host model alias is required.
|
|
41
|
-
The **advisor** bind uses the Fable alias.
|
|
41
|
+
The **advisor** bind uses the Fable alias. Astra binds through the Codex helper.
|
|
@@ -3,39 +3,39 @@
|
|
|
3
3
|
Detail behind the **Warm-up** section of [`advisor-protocol.md`](../advisor-protocol.md).
|
|
4
4
|
Open this when binding the warm advisor on a Claude host or a Codex host, and for the charter text on every host.
|
|
5
5
|
|
|
6
|
-
## Spawn fields
|
|
6
|
+
## Spawn fields for a Claude host
|
|
7
7
|
|
|
8
|
-
The consuming skill's session
|
|
8
|
+
The consuming skill's session tries Fable first. Spawn with:
|
|
9
9
|
|
|
10
10
|
- `subagent_type: session-advisor` (see [`agents/session-advisor.md`](../../../agents/session-advisor.md) for the full signal contract).
|
|
11
|
-
- `model`: the short alias
|
|
11
|
+
- `model`: the short alias via `resolve_cli_model_id("Fable")` (alias table: [`cli-chain.md`](cli-chain.md)): `fable`. When Fable is out of usage and the Astra rung is open, continue with the Codex helper in [`astra-rung.md`](astra-rung.md) as the Astra fallback.
|
|
12
12
|
- `name`: a name the session and every consumer will use to reach it (e.g. `team-advisor-agent`).
|
|
13
13
|
- `run_in_background: true`.
|
|
14
|
-
- `prompt`: the charter below. A **Fable**-tier try carries the exact token `FABLE-SPAWN-AUTHORIZED` in that prompt
|
|
14
|
+
- `prompt`: the charter below. A **Fable**-tier try carries the exact token `FABLE-SPAWN-AUTHORIZED` in that prompt. `hooks/blocking/fable_spawn_gate.py` denies every `Agent` or `Task` spawn at `model: fable` whose prompt lacks that token. A try at any other tier needs no token.
|
|
15
15
|
|
|
16
|
-
Stop at the first successful
|
|
16
|
+
Stop at the first successful bind. That try's tier is `selected_tier`; the warm advisor lives at that tier for the rest of the session.
|
|
17
17
|
|
|
18
18
|
## Charter (the spawn prompt)
|
|
19
19
|
|
|
20
|
-
The charter states
|
|
20
|
+
The charter states the agent's role, the repo path, and the session's current goal in two or three sentences. The agent is a standing reviewer. It answers only via SendMessage. File edits and commands are out of its scope.
|
|
21
21
|
On a Fable-tier try, include the exact token `FABLE-SPAWN-AUTHORIZED` as plain text in this prompt (substring match; the gate reads the token alone, wherever it came from).
|
|
22
22
|
State plainly:
|
|
23
23
|
|
|
24
24
|
- Every consult carries: who is asking (name and assignment), what changed since their last consult, the live decision or question, and any load-bearing paths or excerpts.
|
|
25
|
-
- Reply via SendMessage to whoever sent the consult, by name
|
|
26
|
-
- Treat each consult on its own terms, keyed to the sender's stated assignment. Different consumers' consults will interleave in this one transcript
|
|
25
|
+
- Reply via SendMessage to whoever sent the consult, by name. Each reply goes back to its own sender, and many different consumers may reach this one agent.
|
|
26
|
+
- Treat each consult on its own terms, keyed to the sender's stated assignment. Different consumers' consults will interleave in this one transcript. Keep each consumer's context separate, and blend only when a consult explicitly asks for that.
|
|
27
27
|
- If a consult re-raises a question already answered, with nothing new attached, reply by restating the prior answer and naming it as a restatement.
|
|
28
|
-
- Every reply, including this first bind turn, opens its first line with exactly one of the four uppercase signal words
|
|
28
|
+
- Every reply, including this first bind turn, opens its first line with exactly one of the four uppercase signal words: `ENDORSE`, `CORRECTION`, `PLAN`, `STOP`. Nothing else on that line. Standing by after the bind is itself an `ENDORSE` of the charter, stated as that first line.
|
|
29
29
|
|
|
30
30
|
The agent finishes its first turn standing by. `SendMessage` alone resumes it; between consults it waits quietly.
|
|
31
31
|
|
|
32
32
|
## Codex host
|
|
33
33
|
|
|
34
|
-
Spawn a native in-session
|
|
35
|
-
Record `{tier: "
|
|
36
|
-
The `
|
|
34
|
+
Spawn a native in-session Astra subagent at `resolve_codex_model_id("Astra")` (`gpt-6-astra`) with the charter as its prompt.
|
|
35
|
+
Record `{tier: "Astra", result: "spawned"}` on success. Fail closed when that spawn does not bind.
|
|
36
|
+
The `ADVISOR_ASTRA` flag is not required. Do not walk Fable. Consults stay in-session with that Astra subagent.
|
|
37
37
|
Identity routing: [`identity.md`](identity.md).
|
|
38
38
|
|
|
39
39
|
## Third-party host
|
|
40
40
|
|
|
41
|
-
Bind per [`third-party-bind.md`](third-party-bind.md) and charter the CLI session with the same charter
|
|
41
|
+
Bind per [`third-party-bind.md`](third-party-bind.md) and charter the CLI session with the same charter. The reply contract is the same, and consults travel through the CLI runner.
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"""Bind and consult a read-only Codex CLI session through Astra."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
12
|
+
from dataclasses import asdict
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
_scripts_directory = Path(__file__).resolve().parent
|
|
16
|
+
_config_directory = _scripts_directory / "config"
|
|
17
|
+
for each_import_directory in (_scripts_directory, _config_directory):
|
|
18
|
+
each_import_directory_text = str(each_import_directory)
|
|
19
|
+
if each_import_directory_text not in sys.path:
|
|
20
|
+
sys.path[:0] = [each_import_directory_text]
|
|
21
|
+
|
|
22
|
+
from advisor_scripts_constants.astra_advisor_constants import (
|
|
23
|
+
ADVISOR_CODEX_EXECUTABLE_ENV_VAR,
|
|
24
|
+
ALL_ASTRA_TRUTHY_VALUES,
|
|
25
|
+
ASTRA_BIND_FAILURE_REASON,
|
|
26
|
+
ASTRA_CODEX_TIMEOUT_REASON,
|
|
27
|
+
ASTRA_CODEX_TIMEOUT_SECONDS,
|
|
28
|
+
ASTRA_EFFORT_FLAG,
|
|
29
|
+
ASTRA_ENABLE_FLAG,
|
|
30
|
+
ASTRA_ENV_VAR,
|
|
31
|
+
ASTRA_EXECUTABLE_NOT_FOUND_REASON,
|
|
32
|
+
ASTRA_FALLBACK_KIND_BROKEN,
|
|
33
|
+
ASTRA_FALLBACK_KIND_DECLINED,
|
|
34
|
+
ASTRA_SESSION_ID_METAVAR,
|
|
35
|
+
CLAUDE_CONFIG_DIRECTORY_NAME,
|
|
36
|
+
CODEX_CONFIG_FLAG,
|
|
37
|
+
CODEX_EXECUTABLE,
|
|
38
|
+
CODEX_EXEC_SUBCOMMAND,
|
|
39
|
+
CODEX_JSON_FLAG,
|
|
40
|
+
CODEX_MODEL_FLAG,
|
|
41
|
+
CODEX_PROMPT_FROM_STDIN,
|
|
42
|
+
CODEX_READ_ONLY_SANDBOX,
|
|
43
|
+
CODEX_REASONING_CONFIG_TEMPLATE,
|
|
44
|
+
CODEX_RESUME_SUBCOMMAND,
|
|
45
|
+
CODEX_SANDBOX_FLAG,
|
|
46
|
+
USAGE_PROBE_FILENAME,
|
|
47
|
+
USAGE_PROBE_PACKAGE_DIRECTORY_NAME,
|
|
48
|
+
USAGE_PROBE_SCRIPTS_DIRECTORY_NAME,
|
|
49
|
+
USAGE_PROBE_SHARED_DIRECTORY_NAME,
|
|
50
|
+
)
|
|
51
|
+
from advisor_scripts_constants.advisor_route_constants import (
|
|
52
|
+
ADVISOR_CODEX_MODEL_ID,
|
|
53
|
+
ADVISOR_EFFORT_DEFAULT,
|
|
54
|
+
ADVISOR_EFFORT_ENV_VAR,
|
|
55
|
+
ALL_ADVISOR_EFFORT_LEVELS,
|
|
56
|
+
SPAWN_OUTCOME_KEY,
|
|
57
|
+
)
|
|
58
|
+
from codex_astra_reply import (
|
|
59
|
+
CodexAstraAdvisorReply,
|
|
60
|
+
build_fallback_reply,
|
|
61
|
+
parse_codex_jsonl_reply,
|
|
62
|
+
)
|
|
63
|
+
from codex_astra_preflight import AstraPreflight, run_astra_preflight
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _resolved_settings(
|
|
67
|
+
all_settings: Mapping[str, str] | None,
|
|
68
|
+
) -> Mapping[str, str]:
|
|
69
|
+
return os.environ if all_settings is None else all_settings
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def is_astra_advisor_enabled(all_settings: Mapping[str, str] | None) -> bool:
|
|
73
|
+
"""Return whether the Astra advisor flag is on.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
all_settings: Environment mapping, or None to read os.environ.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
True when the flag holds a truthy value.
|
|
80
|
+
"""
|
|
81
|
+
raw_setting = _resolved_settings(all_settings).get(ASTRA_ENV_VAR, "")
|
|
82
|
+
return raw_setting.strip().lower() in ALL_ASTRA_TRUTHY_VALUES
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def resolve_advisor_effort(all_settings: Mapping[str, str] | None) -> str:
|
|
86
|
+
"""Return ADVISOR_EFFORT, or low when the value is missing or unknown.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
all_settings: Environment mapping, or None to read os.environ.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
The requested effort, or low.
|
|
93
|
+
"""
|
|
94
|
+
requested = _resolved_settings(all_settings).get(ADVISOR_EFFORT_ENV_VAR, "")
|
|
95
|
+
normalized = requested.strip().lower()
|
|
96
|
+
return normalized if normalized in ALL_ADVISOR_EFFORT_LEVELS else ADVISOR_EFFORT_DEFAULT
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def resolve_usage_probe_path(home_directory: Path) -> Path:
|
|
100
|
+
"""Return the Codex usage-probe path under home_directory.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
home_directory: Home directory that holds the installed probe.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Path to the usage probe under that home.
|
|
107
|
+
"""
|
|
108
|
+
return (
|
|
109
|
+
home_directory
|
|
110
|
+
/ CLAUDE_CONFIG_DIRECTORY_NAME
|
|
111
|
+
/ USAGE_PROBE_SHARED_DIRECTORY_NAME
|
|
112
|
+
/ USAGE_PROBE_PACKAGE_DIRECTORY_NAME
|
|
113
|
+
/ USAGE_PROBE_SCRIPTS_DIRECTORY_NAME
|
|
114
|
+
/ USAGE_PROBE_FILENAME
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def resolve_codex_executable(all_settings: Mapping[str, str] | None) -> str | None:
|
|
119
|
+
"""Return the Codex executable from the override or PATH.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
all_settings: Environment mapping, or None to read os.environ.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Executable path or name, or None if neither is set.
|
|
126
|
+
"""
|
|
127
|
+
override = _resolved_settings(all_settings).get(ADVISOR_CODEX_EXECUTABLE_ENV_VAR, "").strip()
|
|
128
|
+
return override or shutil.which(CODEX_EXECUTABLE)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def build_codex_arguments(
|
|
132
|
+
codex_executable: str,
|
|
133
|
+
session_id: str | None = None,
|
|
134
|
+
reasoning_effort: str = ADVISOR_EFFORT_DEFAULT,
|
|
135
|
+
) -> list[str]:
|
|
136
|
+
"""Build the Codex command list for bind or resume.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
codex_executable: Codex executable path or name.
|
|
140
|
+
session_id: Session to resume, or None for a new bind.
|
|
141
|
+
reasoning_effort: Codex reasoning effort.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Command arguments for Codex exec.
|
|
145
|
+
"""
|
|
146
|
+
arguments = [
|
|
147
|
+
codex_executable,
|
|
148
|
+
CODEX_EXEC_SUBCOMMAND,
|
|
149
|
+
CODEX_MODEL_FLAG,
|
|
150
|
+
ADVISOR_CODEX_MODEL_ID,
|
|
151
|
+
CODEX_CONFIG_FLAG,
|
|
152
|
+
CODEX_REASONING_CONFIG_TEMPLATE.format(effort=reasoning_effort),
|
|
153
|
+
CODEX_SANDBOX_FLAG,
|
|
154
|
+
CODEX_READ_ONLY_SANDBOX,
|
|
155
|
+
CODEX_JSON_FLAG,
|
|
156
|
+
]
|
|
157
|
+
if session_id is not None:
|
|
158
|
+
arguments.extend([CODEX_RESUME_SUBCOMMAND, session_id])
|
|
159
|
+
arguments.append(CODEX_PROMPT_FROM_STDIN)
|
|
160
|
+
return arguments
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _resolve_preflight(
|
|
164
|
+
preflight: AstraPreflight | None,
|
|
165
|
+
probe_path: Path | None,
|
|
166
|
+
process_runner: Callable[..., subprocess.CompletedProcess[str]],
|
|
167
|
+
) -> AstraPreflight:
|
|
168
|
+
if preflight is not None:
|
|
169
|
+
return preflight
|
|
170
|
+
resolved_path = resolve_usage_probe_path(Path.home()) if probe_path is None else probe_path
|
|
171
|
+
return run_astra_preflight(resolved_path, process_runner)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _run_codex(
|
|
175
|
+
prompt: str,
|
|
176
|
+
working_directory: Path,
|
|
177
|
+
session_id: str | None,
|
|
178
|
+
all_settings: Mapping[str, str] | None,
|
|
179
|
+
executable: str,
|
|
180
|
+
process_runner: Callable[..., subprocess.CompletedProcess[str]],
|
|
181
|
+
) -> subprocess.CompletedProcess[str]:
|
|
182
|
+
return process_runner(
|
|
183
|
+
build_codex_arguments(
|
|
184
|
+
executable,
|
|
185
|
+
session_id=session_id,
|
|
186
|
+
reasoning_effort=resolve_advisor_effort(all_settings),
|
|
187
|
+
),
|
|
188
|
+
cwd=str(working_directory),
|
|
189
|
+
input=prompt,
|
|
190
|
+
capture_output=True,
|
|
191
|
+
text=True,
|
|
192
|
+
check=False,
|
|
193
|
+
shell=False,
|
|
194
|
+
timeout=ASTRA_CODEX_TIMEOUT_SECONDS,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _run_enabled_advisor(
|
|
199
|
+
prompt: str,
|
|
200
|
+
working_directory: Path,
|
|
201
|
+
setting_by_name: Mapping[str, str] | None,
|
|
202
|
+
session_id: str | None,
|
|
203
|
+
executable: str,
|
|
204
|
+
resolved_preflight: AstraPreflight,
|
|
205
|
+
process_runner: Callable[..., subprocess.CompletedProcess[str]],
|
|
206
|
+
) -> CodexAstraAdvisorReply:
|
|
207
|
+
if not resolved_preflight.eligible:
|
|
208
|
+
return build_fallback_reply(resolved_preflight.reason, True, resolved_preflight.fallback_kind)
|
|
209
|
+
try:
|
|
210
|
+
completed = _run_codex(
|
|
211
|
+
prompt, working_directory, session_id, setting_by_name, executable, process_runner
|
|
212
|
+
)
|
|
213
|
+
except subprocess.TimeoutExpired as error:
|
|
214
|
+
return build_fallback_reply(f"{ASTRA_CODEX_TIMEOUT_REASON}: {error}", True)
|
|
215
|
+
except (OSError, subprocess.SubprocessError) as error:
|
|
216
|
+
return build_fallback_reply(f"{ASTRA_BIND_FAILURE_REASON}: {error}", True)
|
|
217
|
+
if completed.returncode != 0:
|
|
218
|
+
return build_fallback_reply(
|
|
219
|
+
f"{ASTRA_BIND_FAILURE_REASON}: process exit {completed.returncode}", True
|
|
220
|
+
)
|
|
221
|
+
return parse_codex_jsonl_reply(completed.stdout, session_id, True)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def run_codex_astra_advisor(
|
|
225
|
+
prompt: str,
|
|
226
|
+
working_directory: Path,
|
|
227
|
+
preflight: AstraPreflight | None,
|
|
228
|
+
probe_path: Path | None,
|
|
229
|
+
setting_by_name: Mapping[str, str] | None,
|
|
230
|
+
session_id: str | None,
|
|
231
|
+
process_runner: Callable[..., subprocess.CompletedProcess[str]],
|
|
232
|
+
) -> CodexAstraAdvisorReply:
|
|
233
|
+
"""Bind or resume a read-only Codex Astra advisor.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
prompt: Advisor prompt text.
|
|
237
|
+
working_directory: Working directory for Codex.
|
|
238
|
+
preflight: Usage probe result, or None to run the probe.
|
|
239
|
+
probe_path: Usage-probe path, or None for the installed path.
|
|
240
|
+
setting_by_name: Environment mapping, or None to read os.environ.
|
|
241
|
+
session_id: Session to resume, or None for a new bind.
|
|
242
|
+
process_runner: Callable that runs the probe and Codex.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Advisor reply, or a fallback.
|
|
246
|
+
"""
|
|
247
|
+
if not is_astra_advisor_enabled(setting_by_name):
|
|
248
|
+
return build_fallback_reply("Astra advisor flag is disabled", False, ASTRA_FALLBACK_KIND_DECLINED)
|
|
249
|
+
executable = resolve_codex_executable(setting_by_name)
|
|
250
|
+
if executable is None:
|
|
251
|
+
return build_fallback_reply(ASTRA_EXECUTABLE_NOT_FOUND_REASON, True)
|
|
252
|
+
resolved_preflight = _resolve_preflight(preflight, probe_path, process_runner)
|
|
253
|
+
return _run_enabled_advisor(prompt, working_directory, setting_by_name, session_id, executable, resolved_preflight, process_runner)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def build_argument_parser() -> argparse.ArgumentParser:
|
|
257
|
+
"""Build the command-line parser for bind and resume.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Argument parser for this helper.
|
|
261
|
+
"""
|
|
262
|
+
parser = argparse.ArgumentParser(description="Bind or consult a read-only Codex Astra advisor.")
|
|
263
|
+
mode_group = parser.add_mutually_exclusive_group(required=True)
|
|
264
|
+
mode_group.add_argument("--bind", action="store_true")
|
|
265
|
+
mode_group.add_argument("--resume", metavar=ASTRA_SESSION_ID_METAVAR)
|
|
266
|
+
parser.add_argument("--cwd", required=True, type=Path)
|
|
267
|
+
parser.add_argument(ASTRA_ENABLE_FLAG, dest="is_astra_requested", action="store_true")
|
|
268
|
+
parser.add_argument(
|
|
269
|
+
ASTRA_EFFORT_FLAG,
|
|
270
|
+
dest="astra_effort",
|
|
271
|
+
choices=ALL_ADVISOR_EFFORT_LEVELS,
|
|
272
|
+
default=None,
|
|
273
|
+
)
|
|
274
|
+
return parser
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def main(all_cli_arguments: Sequence[str]) -> int:
|
|
278
|
+
"""Run one bind or resume request from stdin.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
all_cli_arguments: CLI arguments after the program name.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
0 when the advisor reply succeeds, 1 on fallback.
|
|
285
|
+
"""
|
|
286
|
+
parsed = build_argument_parser().parse_args(list(all_cli_arguments))
|
|
287
|
+
all_settings = dict(os.environ)
|
|
288
|
+
if parsed.is_astra_requested:
|
|
289
|
+
all_settings[ASTRA_ENV_VAR] = "1"
|
|
290
|
+
if parsed.astra_effort is not None:
|
|
291
|
+
all_settings[ADVISOR_EFFORT_ENV_VAR] = parsed.astra_effort
|
|
292
|
+
reply = run_codex_astra_advisor(
|
|
293
|
+
sys.stdin.read(),
|
|
294
|
+
parsed.cwd,
|
|
295
|
+
None,
|
|
296
|
+
None,
|
|
297
|
+
all_settings,
|
|
298
|
+
parsed.resume if not parsed.bind else None,
|
|
299
|
+
subprocess.run,
|
|
300
|
+
)
|
|
301
|
+
payload = asdict(reply)
|
|
302
|
+
payload[SPAWN_OUTCOME_KEY] = payload.pop("outcome")
|
|
303
|
+
sys.stdout.write(f"{json.dumps(payload, sort_keys=True)}\n")
|
|
304
|
+
return 0 if reply.successful else 1
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
if __name__ == "__main__":
|
|
308
|
+
raise SystemExit(main(sys.argv[1:]))
|