claude-dev-env 2.4.0 → 2.7.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/CLAUDE.md +26 -59
- package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +6 -3
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +12 -2
- package/_shared/pr-loop/scripts/code_rules_gate_parts/baseline_import_isolation.py +309 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_regression.py +540 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +206 -70
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/__init__.py +1 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/_repo_test_helpers.py +76 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_baseline_import_isolation.py +248 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_regression.py +309 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +91 -58
- package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +202 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
- package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
- package/_shared/pr-loop/worker-spawn.md +1 -1
- package/agents/CLAUDE.md +3 -2
- package/agents/caveman.md +0 -1
- package/agents/clasp-deployment-orchestrator.md +0 -1
- package/agents/clean-coder.md +0 -1
- package/agents/code-advisor.md +0 -1
- package/agents/code-quality-agent.md +1 -2
- package/agents/code-verifier.md +36 -8
- package/agents/deep-research.md +0 -1
- package/agents/docs-agent.md +0 -1
- package/agents/git-commit-crafter.md +0 -1
- package/agents/issue-tracker.md +42 -0
- package/agents/plan-packet-validator.md +0 -1
- package/agents/pr-description-writer.md +0 -1
- package/agents/test_agent_frontmatter.py +67 -18
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +143 -141
- package/bin/CLAUDE.md +68 -5
- package/bin/codex-compat.mjs +104 -0
- package/bin/codex-compat.test.mjs +51 -0
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-constants.mjs +88 -0
- package/bin/install.mjs +1138 -114
- package/bin/install.prune.test.mjs +869 -19
- package/bin/install.test.mjs +906 -2
- package/codex-capability-map.json +13 -0
- package/commands/implement.md +1 -1
- package/commands/right-size.md +1 -1
- package/docs/CLAUDE.md +1 -0
- package/docs/CODE_RULES.md +2 -0
- package/docs/codex-compatibility.md +25 -0
- package/docs/host-pool-health-monitor.md +102 -0
- package/docs/nas-ssh-invocation.md +96 -12
- package/docs/references/CLAUDE.md +4 -2
- package/docs/references/advisor-tool.md +13 -0
- package/docs/references/code-review-enforcement.md +35 -0
- package/docs/references/team-advisor-skill.md +14 -0
- package/hooks/blocking/CLAUDE.md +4 -0
- package/hooks/blocking/code_review_pr_create_gate.py +7 -3
- package/hooks/blocking/code_review_push_gate.py +9 -4
- package/hooks/blocking/code_review_stamp_directory_write_blocker.py +8 -0
- package/hooks/blocking/config/__init__.py +5 -5
- package/hooks/blocking/config/code_review_enforcement_constants.py +40 -7
- package/hooks/blocking/config/test_code_review_enforcement_constants.py +58 -0
- package/hooks/blocking/config/verified_commit_constants.py +160 -159
- package/hooks/blocking/eli11_reply_enforcer.py +479 -0
- package/hooks/blocking/gh_body_arg_blocker.py +1 -1
- package/hooks/blocking/nas_ssh_binary_enforcer.py +8 -46
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
- package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
- package/hooks/blocking/shell_substitution_blocker.py +129 -0
- package/hooks/blocking/state_description_blocker.py +1 -1
- package/hooks/blocking/stop_dispatcher.py +1 -1
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +2 -3
- package/hooks/blocking/test_code_review_pr_create_gate.py +14 -0
- package/hooks/blocking/test_code_review_push_gate.py +16 -0
- package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +19 -0
- package/hooks/blocking/test_eli11_reply_enforcer.py +457 -0
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -1
- package/hooks/blocking/test_shell_substitution_blocker.py +124 -0
- package/hooks/blocking/test_stop_dispatcher.py +23 -0
- package/hooks/blocking/test_unscoped_search_blocker.py +102 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +804 -808
- package/hooks/blocking/test_verification_verdict_store.py +54 -0
- package/hooks/blocking/test_verified_commit_gate.py +581 -581
- package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
- package/hooks/blocking/unscoped_search_blocker.py +391 -0
- package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
- package/hooks/blocking/verification_verdict_store.py +1039 -1036
- package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
- package/hooks/blocking/verifier_verdict_minter.py +280 -280
- package/hooks/git-hooks/CLAUDE.md +3 -0
- package/hooks/git-hooks/conftest.py +30 -0
- package/hooks/git-hooks/gate_utils.py +2 -2
- package/hooks/git-hooks/git_hooks_constants/__init__.py +41 -2
- package/hooks/git-hooks/pre_push.py +75 -4
- package/hooks/git-hooks/pre_push_base_reference.py +166 -0
- package/hooks/git-hooks/test_config.py +0 -15
- package/hooks/git-hooks/test_gate_utils.py +3 -15
- package/hooks/git-hooks/test_pre_commit.py +1 -15
- package/hooks/git-hooks/test_pre_push.py +257 -23
- package/hooks/git-hooks/test_pre_push_base_reference.py +339 -0
- package/hooks/hooks.json +10 -12
- package/hooks/hooks_constants/CLAUDE.md +7 -2
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +4 -4
- package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +101 -0
- package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +2 -8
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
- package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
- package/hooks/hooks_constants/shell_command_segments.py +82 -0
- package/hooks/hooks_constants/shell_substitution_blocker_constants.py +67 -0
- package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -0
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +5 -6
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +1 -0
- package/hooks/hooks_constants/unscoped_search_blocker_constants.py +153 -0
- package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
- package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
- package/hooks/pyproject.toml +1 -0
- package/hooks/validators/CLAUDE.md +1 -0
- package/hooks/validators/config/__init__.py +0 -0
- package/hooks/validators/config/directory_exemption_constants.py +183 -0
- package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
- package/hooks/validators/conftest.py +4 -0
- package/hooks/validators/ruff_integration.py +49 -5
- package/hooks/validators/run_all_validators.py +206 -9
- package/hooks/validators/test_directory_exemption_constants.py +185 -0
- package/hooks/validators/test_python_antipattern_checks.py +110 -5
- package/hooks/validators/test_ruff_integration.py +92 -1
- package/hooks/validators/test_run_all_validators.py +115 -68
- package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
- package/package.json +13 -3
- package/rules/CLAUDE.md +17 -22
- package/rules/agent-spawn-protocol.md +6 -6
- package/rules/anti-corollary-tests.md +1 -1
- package/rules/bdd.md +1 -1
- package/rules/cleanup-temp-files.md +10 -4
- package/rules/code-standards.md +7 -0
- package/rules/conservative-action.md +1 -5
- package/rules/context7.md +0 -4
- package/rules/destructive-commands.md +47 -0
- package/rules/doc-inventory-integrity.md +48 -0
- package/rules/doc-prose-cuts.md +58 -0
- package/rules/docstring-prose-matches-implementation.md +53 -44
- package/rules/durable-post-artifacts.md +0 -4
- package/rules/eli11-replies.md +31 -0
- package/rules/explore-thoroughly.md +4 -4
- package/rules/falsify-before-green.md +68 -0
- package/rules/file-global-constants.md +1 -1
- package/rules/filesystem-search.md +51 -0
- package/rules/gh-cli-conventions.md +27 -0
- package/rules/git-workflow.md +26 -0
- package/rules/hedging-claims.md +9 -0
- package/rules/long-horizon-autonomy.md +0 -4
- package/rules/measurement-denominators.md +48 -0
- package/rules/nas-ssh-invocation.md +23 -5
- package/rules/parallel-tools.md +2 -2
- package/rules/plain-illustrative-docstrings.md +3 -7
- package/rules/plain-language.md +2 -0
- package/rules/proof-of-work-pr-comments.md +0 -4
- package/rules/re-stage-before-commit.md +2 -0
- package/rules/research-mode.md +10 -0
- package/rules/shell-invocation.md +21 -0
- package/rules/testing.md +4 -0
- package/rules/verified-commit-gate-skip.md +3 -27
- package/rules/verify-before-asking.md +5 -0
- package/rules/windows-filesystem-safe.md +1 -1
- package/rules/workers-done-before-complete.md +4 -0
- package/scripts/CLAUDE.md +1 -0
- package/scripts/Capture-PoolHealth.ps1 +410 -0
- package/scripts/Migrate-ShellPolicy.ps1 +1 -1
- package/scripts/_code_review_test_support.py +404 -0
- package/scripts/claude_chain_runner.py +141 -1
- package/scripts/codex_capability_bridge.py +171 -0
- package/scripts/codex_compat_materializer.py +1087 -0
- package/scripts/codex_compat_watcher.py +502 -0
- package/scripts/conftest.py +16 -1
- package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +9 -0
- package/scripts/dev_env_scripts_constants/code_review_constants.py +37 -0
- package/scripts/invoke_code_review.py +11 -4
- package/scripts/resolve_worker_spawn.py +626 -626
- package/scripts/spawn_grok_batch.py +672 -672
- package/scripts/sync_to_cursor/rules.py +0 -10
- package/scripts/test_claude_chain_runner.py +131 -0
- package/scripts/test_invoke_code_review.py +85 -908
- package/scripts/test_invoke_code_review_chain.py +70 -0
- package/scripts/test_invoke_code_review_cli.py +192 -0
- package/scripts/test_invoke_code_review_contract.py +256 -0
- package/scripts/test_invoke_code_review_git.py +123 -0
- package/scripts/test_invoke_code_review_mode.py +99 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -1014
- package/scripts/tests/test_code_review_constants.py +80 -0
- package/scripts/tests/test_codex_capability_bridge.py +91 -0
- package/scripts/tests/test_codex_compat_materializer.py +632 -0
- package/scripts/tests/test_codex_compat_watcher.py +599 -0
- package/scripts/tests/test_sync_to_cursor.py +0 -1
- package/skills/CLAUDE.md +2 -0
- package/skills/auditing-claude-config/SKILL.md +114 -114
- package/skills/autoconverge/SKILL.md +427 -427
- package/skills/autoconverge/reference/convergence.md +24 -3
- package/skills/autoconverge/workflow/CLAUDE.md +1 -0
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +3 -3
- package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1263
- package/skills/autoconverge/workflow/converge.mjs +168 -1
- package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
- package/skills/bugteam/reference/copilot-gap-analysis.md +1 -1
- package/skills/closeout/SKILL.md +33 -50
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
- package/skills/codex-review/scripts/run_codex_review.py +233 -1
- package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
- package/skills/condensing-instructions/SKILL.md +72 -0
- package/skills/copilot-review/SKILL.md +119 -119
- package/skills/e-code-review/SKILL.md +52 -0
- package/skills/e-code-review/reference/fix.md +54 -0
- package/skills/e-code-review/reference/loop.md +43 -0
- package/skills/e-code-review/reference/low.md +57 -0
- package/skills/e-code-review/reference/medium.md +153 -0
- package/skills/e-code-review/reference/xhigh.md +182 -0
- package/skills/e-simplify/SKILL.md +97 -0
- package/skills/fresh-branch/CLAUDE.md +1 -1
- package/skills/fresh-branch/SKILL.md +5 -6
- package/skills/fresh-branch/scripts/create_fresh_branch.py +42 -24
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -3
- package/skills/fresh-branch/scripts/test_create_fresh_branch.py +30 -126
- package/skills/issue-tracker/SKILL.md +92 -0
- package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
- package/skills/issue-tracker/reference/handoff-schema.md +64 -0
- package/skills/issue-tracker/reference/operation-matrix.md +41 -0
- package/skills/orchestrator/SKILL.md +177 -22
- package/skills/orchestrator/scripts/status_gate.py +625 -0
- package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
- package/skills/orchestrator/scripts/test_status_gate.py +439 -0
- package/skills/orchestrator-refresh/SKILL.md +129 -35
- package/skills/plan-to-pr/SKILL.md +155 -0
- package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
- package/skills/plan-to-pr/reference/model-routing.md +36 -0
- package/skills/plan-to-pr/reference/packet-contract.md +43 -0
- package/skills/plan-to-pr/reference/packet-schema.json +57 -0
- package/skills/plan-to-pr/reference/process-inventory.md +22 -0
- package/skills/plan-to-pr/reference/review-loop.md +33 -0
- package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
- package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
- package/skills/plan-to-pr/reference/task-seeds.md +14 -0
- package/skills/plan-to-pr/reference/task-ticket.md +38 -0
- package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
- package/skills/plan-to-pr/scripts/config/constants.py +193 -0
- package/skills/plan-to-pr/scripts/create_packet.py +173 -0
- package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
- package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
- package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
- package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
- package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
- package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
- package/skills/plan-to-pr/scripts/validate_run.py +173 -0
- package/skills/plan-to-pr/test_skill_contract.py +207 -0
- package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
- package/skills/pr-converge/SKILL.md +472 -469
- package/skills/pr-converge/reference/examples.md +3 -3
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +7 -4
- package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
- package/skills/pr-converge/reference/per-tick.md +5 -5
- package/skills/pr-converge/reference/progress-checklist.md +1 -1
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
- package/skills/pr-converge/test_step5_host_branch.py +1 -1
- package/skills/pr-fix-protocol/SKILL.md +1 -1
- package/skills/privacy-hygiene/SKILL.md +68 -68
- package/skills/privacy-hygiene/reference/sweep-procedure.md +1 -1
- package/skills/prototype/workflows/promotion.md +1 -1
- package/skills/release-notes-html/SKILL.md +164 -0
- package/skills/session-log/SKILL.md +1 -1
- package/skills/task-build/CLAUDE.md +8 -7
- package/skills/task-build/SKILL.md +16 -8
- package/skills/task-build/reference/tool-routing.md +19 -0
- package/rules/claude-md-orphan-file.md +0 -28
- package/rules/cleanup-command-forms.md +0 -23
- package/rules/code-reviews.md +0 -11
- package/rules/env-var-table-code-drift.md +0 -10
- package/rules/gh-body-file.md +0 -5
- package/rules/gh-paginate.md +0 -3
- package/rules/hook-prose-matches-detector.md +0 -15
- package/rules/no-historical-clutter.md +0 -26
- package/rules/no-inline-destructive-literals.md +0 -9
- package/rules/no-justification-noise.md +0 -61
- package/rules/package-inventory-stale-entry.md +0 -25
- package/rules/right-sized-engineering.md +0 -28
- package/rules/self-contained-docs.md +0 -17
- package/rules/shell-invocation-policy.md +0 -5
- package/rules/tdd.md +0 -7
- package/skills/closeout/reference/issue-body-templates.md +0 -108
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Translate Claude capability requests into declarative Codex records."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CapabilityArgumentError(ValueError):
|
|
11
|
+
"""Raised when bridge command-line input cannot be translated."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CapabilityArgumentParser(argparse.ArgumentParser):
|
|
15
|
+
"""Parse bridge arguments while keeping failures inside the JSON contract."""
|
|
16
|
+
|
|
17
|
+
def error(self, message: str) -> None:
|
|
18
|
+
raise CapabilityArgumentError(f"invalid bridge arguments: {message}")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _load_capability_map() -> dict[str, object]:
|
|
22
|
+
capability_map_path = Path(__file__).resolve().parents[1] / "codex-capability-map.json"
|
|
23
|
+
with capability_map_path.open(encoding="utf-8") as map_file:
|
|
24
|
+
return json.load(map_file)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _build_error_record(source_surface: str, message: str) -> dict[str, object]:
|
|
28
|
+
return {"source": source_surface, "status": "error", "error": message}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _parse_command_arguments() -> argparse.Namespace:
|
|
32
|
+
argument_parser = CapabilityArgumentParser()
|
|
33
|
+
argument_parser.add_argument("source_surface")
|
|
34
|
+
argument_parser.add_argument("payload_json", nargs="?")
|
|
35
|
+
return argument_parser.parse_args()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _read_payload(payload_json: str) -> object:
|
|
39
|
+
try:
|
|
40
|
+
return json.loads(payload_json)
|
|
41
|
+
except json.JSONDecodeError as payload_error:
|
|
42
|
+
raise CapabilityArgumentError("payload must be valid JSON") from payload_error
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _capability_by_surface() -> dict[str, dict[str, object]]:
|
|
46
|
+
all_capabilities = _load_capability_map()["capabilities"]
|
|
47
|
+
capability_by_surface: dict[str, dict[str, object]] = {}
|
|
48
|
+
for each_capability in all_capabilities:
|
|
49
|
+
capability_by_surface[each_capability["source"].casefold()] = each_capability
|
|
50
|
+
for each_alias in each_capability["aliases"]:
|
|
51
|
+
capability_by_surface[each_alias.casefold()] = each_capability
|
|
52
|
+
return capability_by_surface
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _contains_unsafe_path(all_capability_payload: object) -> bool:
|
|
56
|
+
path_pattern = re.compile(
|
|
57
|
+
r"(?:^[A-Za-z]:[\\/]|^\\\\|^/|^~(?:[\\/]|$)|^(?:%[^%]+%|\$\{?[A-Za-z_][A-Za-z0-9_]*\}?)(?:[\\/]|$)|(?:^|[\\/])(?:Users|home|\.ssh|\.codex)(?:[\\/]|$)|(?:^|[\\/])\.\.(?:[\\/]|$))",
|
|
58
|
+
re.IGNORECASE,
|
|
59
|
+
)
|
|
60
|
+
if isinstance(all_capability_payload, str):
|
|
61
|
+
return bool(path_pattern.search(all_capability_payload))
|
|
62
|
+
if isinstance(all_capability_payload, dict):
|
|
63
|
+
return any(_contains_unsafe_path(each_payload_entry) for each_payload_entry in all_capability_payload.values())
|
|
64
|
+
if isinstance(all_capability_payload, list):
|
|
65
|
+
return any(_contains_unsafe_path(each_payload_entry) for each_payload_entry in all_capability_payload)
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _status_by_source() -> dict[str, str]:
|
|
70
|
+
return {"pending": "pending", "queued": "pending", "in_progress": "in_progress", "in-progress": "in_progress", "active": "in_progress", "completed": "completed", "complete": "completed", "done": "completed"}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _task_plan_entry(all_capability_payload: dict[str, object]) -> dict[str, str] | None:
|
|
74
|
+
task_name = all_capability_payload.get("subject", all_capability_payload.get("name", all_capability_payload.get("title")))
|
|
75
|
+
task_status = all_capability_payload.get("status", "pending")
|
|
76
|
+
status_by_source = _status_by_source()
|
|
77
|
+
if not isinstance(task_name, str) or not task_name.strip():
|
|
78
|
+
return None
|
|
79
|
+
if not isinstance(task_status, str) or task_status.casefold() not in status_by_source:
|
|
80
|
+
return None
|
|
81
|
+
return {"step": task_name, "status": status_by_source[task_status.casefold()]}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _has_required_fields(all_capability_payload: dict[str, object], all_required_field_definitions: dict[str, str]) -> bool:
|
|
85
|
+
status_by_source = _status_by_source()
|
|
86
|
+
for each_required_field_name, each_required_field_schema in all_required_field_definitions.items():
|
|
87
|
+
required_field_content = all_capability_payload.get(each_required_field_name)
|
|
88
|
+
if each_required_field_schema == "string" and (not isinstance(required_field_content, str) or not required_field_content.strip()):
|
|
89
|
+
return False
|
|
90
|
+
if each_required_field_schema == "status" and (not isinstance(required_field_content, str) or required_field_content.casefold() not in status_by_source):
|
|
91
|
+
return False
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _has_valid_plan(all_capability_payload: dict[str, object]) -> bool:
|
|
96
|
+
all_task_entries = all_capability_payload.get("plan")
|
|
97
|
+
return isinstance(all_task_entries, list) and all(isinstance(each_task_entry, dict) and _task_plan_entry(each_task_entry) is not None for each_task_entry in all_task_entries)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def translate_capability(source_surface: str, payload: object) -> dict[str, object]:
|
|
101
|
+
"""Translate one capability request into a deterministic declarative record.
|
|
102
|
+
|
|
103
|
+
The dispatcher selects a mapped target, rejects unsafe or malformed payloads,
|
|
104
|
+
conditionally dispatches TaskList plans through ``_has_valid_plan``, and emits
|
|
105
|
+
manual-review records for unsupported scheduling requests.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
source_surface: Claude capability name or configured alias.
|
|
109
|
+
payload: JSON-compatible request payload.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
A declarative translation record that never executes the target action.
|
|
113
|
+
"""
|
|
114
|
+
capability = _capability_by_surface().get(source_surface.casefold())
|
|
115
|
+
if capability is None:
|
|
116
|
+
return {"source": source_surface, "status": "unknown", "error": "unknown source surface"}
|
|
117
|
+
if _contains_unsafe_path(payload):
|
|
118
|
+
return {"source": capability["source"], "status": "rejected", "error": "path-bearing payload is not allowed"}
|
|
119
|
+
if not isinstance(payload, dict):
|
|
120
|
+
return {"source": capability["source"], "status": "malformed", "error": "payload must be a JSON object"}
|
|
121
|
+
if capability["status"] == "unsupported":
|
|
122
|
+
return {"source": capability["source"], "target": None, "status": "unsupported", "manual_review": True, "error": "scheduling is unavailable"}
|
|
123
|
+
if capability["target"] == "update_plan":
|
|
124
|
+
if capability["source"] == "TaskList":
|
|
125
|
+
if not _has_valid_plan(payload):
|
|
126
|
+
return {"source": capability["source"], "status": "malformed", "error": "TaskList requires a plan of named tasks"}
|
|
127
|
+
return {"source": capability["source"], "target": capability["target"], "transformation": "snapshot", "payload": {"plan": payload["plan"]}, "manual_review": True, "status": "mapped"}
|
|
128
|
+
task_plan_entry = _task_plan_entry(payload)
|
|
129
|
+
if task_plan_entry is None:
|
|
130
|
+
return {"source": capability["source"], "status": "malformed", "error": "task requires a name and supported status"}
|
|
131
|
+
operation = "status mutation" if capability["source"] == "TaskUpdate" else "replace-plan-item" if payload.get("operation") == "replace" else "append"
|
|
132
|
+
return {"source": capability["source"], "target": capability["target"], "transformation": operation, "payload": {"plan": [task_plan_entry]}, "status": "mapped"}
|
|
133
|
+
all_required_field_definitions = capability.get("required_fields", {})
|
|
134
|
+
if not isinstance(all_required_field_definitions, dict) or not _has_required_fields(payload, all_required_field_definitions):
|
|
135
|
+
return {"source": capability["source"], "status": "malformed", "error": "payload is missing required fields"}
|
|
136
|
+
return {"source": capability["source"], "target": capability["target"], "transformation": capability["transformation"], "payload": payload, "manual_review": capability["manual_review"], "status": "mapped"}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def main() -> int:
|
|
140
|
+
"""Dispatch a command-line request and print its JSON translation record.
|
|
141
|
+
|
|
142
|
+
The dispatcher reads an optional payload argument or stdin, translates it
|
|
143
|
+
through the public capability dispatcher, and emits JSON.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
None. Command-line arguments are read from the process argument vector.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Zero after the translation record is written.
|
|
150
|
+
"""
|
|
151
|
+
try:
|
|
152
|
+
command_arguments = _parse_command_arguments()
|
|
153
|
+
payload_json = command_arguments.payload_json or sys.stdin.read()
|
|
154
|
+
capability_payload = _read_payload(payload_json)
|
|
155
|
+
translated_record = translate_capability(command_arguments.source_surface, capability_payload)
|
|
156
|
+
all_translation_error_statuses = ("unknown", "rejected", "malformed")
|
|
157
|
+
if translated_record["status"] in all_translation_error_statuses:
|
|
158
|
+
print(json.dumps(translated_record, sort_keys=True, separators=(",", ":")))
|
|
159
|
+
return 2
|
|
160
|
+
print(json.dumps(translated_record, sort_keys=True, separators=(",", ":")))
|
|
161
|
+
return 0
|
|
162
|
+
except CapabilityArgumentError as argument_error:
|
|
163
|
+
print(json.dumps(_build_error_record("unknown", str(argument_error)), separators=(",", ":")))
|
|
164
|
+
return 2
|
|
165
|
+
except (OSError, KeyError, TypeError, json.JSONDecodeError) as configuration_error:
|
|
166
|
+
print(json.dumps(_build_error_record("unknown", f"bridge configuration error: {configuration_error}"), separators=(",", ":")))
|
|
167
|
+
return 2
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == "__main__":
|
|
171
|
+
raise SystemExit(main())
|