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,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
7
|
+
const materializerPath = path.join(packageRoot, "scripts", "codex_compat_materializer.py");
|
|
8
|
+
const bridgePath = path.join(packageRoot, "scripts", "codex_capability_bridge.py");
|
|
9
|
+
const pythonNames = process.platform === "win32" ? ["py", "python"] : ["python3", "python"];
|
|
10
|
+
|
|
11
|
+
export function createHelpText() {
|
|
12
|
+
return [
|
|
13
|
+
"Usage: codex-compat <command> [options]",
|
|
14
|
+
"",
|
|
15
|
+
"Commands:",
|
|
16
|
+
" materialize Plan or apply Claude agents into an explicit Codex root",
|
|
17
|
+
" bridge Translate a Claude capability request to a declarative record",
|
|
18
|
+
"",
|
|
19
|
+
"Materialize options:",
|
|
20
|
+
" --source-root <path> Claude source root (required)",
|
|
21
|
+
" --target-root <path> Codex target root (required)",
|
|
22
|
+
" --apply Write changes; default is dry-run",
|
|
23
|
+
" --allow-prune-all Permit an empty plan to delete every managed file",
|
|
24
|
+
" --python <command> Python interpreter override",
|
|
25
|
+
"",
|
|
26
|
+
"Bridge options:",
|
|
27
|
+
" --surface <name> Capability surface (required)",
|
|
28
|
+
" --payload <json> JSON object payload (required)",
|
|
29
|
+
" --python <command> Python interpreter override",
|
|
30
|
+
"",
|
|
31
|
+
"Python may also be selected with CODEX_COMPAT_PYTHON. Roots are never inferred or written automatically.",
|
|
32
|
+
].join("\n");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readOption(argumentsList, optionName) {
|
|
36
|
+
const optionIndex = argumentsList.indexOf(optionName);
|
|
37
|
+
if (optionIndex < 0) return undefined;
|
|
38
|
+
return argumentsList[optionIndex + 1];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function requireOption(argumentsList, optionName) {
|
|
42
|
+
const optionValue = readOption(argumentsList, optionName);
|
|
43
|
+
if (!optionValue || optionValue.startsWith("--")) throw new Error(`Missing required option: ${optionName}`);
|
|
44
|
+
return optionValue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function writeErrorRecord(errorCode, errorMessage) {
|
|
48
|
+
process.stderr.write(`${JSON.stringify({ error: { code: errorCode, message: errorMessage } })}\n`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function buildCommand(commandName, argumentsList, interpreter) {
|
|
52
|
+
if (commandName === "materialize") {
|
|
53
|
+
const sourceRoot = requireOption(argumentsList, "--source-root");
|
|
54
|
+
const targetRoot = requireOption(argumentsList, "--target-root");
|
|
55
|
+
const materializerArguments = [materializerPath, sourceRoot, targetRoot];
|
|
56
|
+
if (argumentsList.includes("--apply")) materializerArguments.push("--apply");
|
|
57
|
+
if (argumentsList.includes("--allow-prune-all")) materializerArguments.push("--allow-prune-all");
|
|
58
|
+
return { executable: interpreter, arguments: materializerArguments };
|
|
59
|
+
}
|
|
60
|
+
if (commandName === "bridge") {
|
|
61
|
+
const surface = requireOption(argumentsList, "--surface");
|
|
62
|
+
const payload = requireOption(argumentsList, "--payload");
|
|
63
|
+
return { executable: interpreter, arguments: [bridgePath, surface, payload] };
|
|
64
|
+
}
|
|
65
|
+
throw new Error(`Unknown command: ${commandName}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function findPython(commandNames = pythonNames, canRun = (command) => spawnSync(command, ["--version"], { stdio: "ignore" }).status === 0) {
|
|
69
|
+
for (const eachCommand of commandNames) {
|
|
70
|
+
if (canRun(eachCommand)) return eachCommand;
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function runCommand(command, runChild = spawn) {
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
const childProcess = runChild(command.executable, command.arguments, { stdio: "inherit" });
|
|
78
|
+
childProcess.on("close", (exitCode, signal) => resolve(exitCode ?? 1 + (signal ? 1 : 0)));
|
|
79
|
+
childProcess.on("error", () => resolve(1));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function main(argumentsList = process.argv.slice(2), dependencies = {}) {
|
|
84
|
+
if (argumentsList.length === 0 || argumentsList.includes("--help") || argumentsList.includes("-h")) {
|
|
85
|
+
process.stdout.write(`${createHelpText()}\n`);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
const [commandName, ...commandArguments] = argumentsList;
|
|
89
|
+
try {
|
|
90
|
+
const interpreter = readOption(commandArguments, "--python") ?? process.env.CODEX_COMPAT_PYTHON ?? await (dependencies.findPython ?? findPython)();
|
|
91
|
+
if (!interpreter) {
|
|
92
|
+
writeErrorRecord("python_unavailable", "No usable Python interpreter found. Use --python or CODEX_COMPAT_PYTHON.");
|
|
93
|
+
return 1;
|
|
94
|
+
}
|
|
95
|
+
const command = buildCommand(commandName, commandArguments, interpreter);
|
|
96
|
+
return await (dependencies.runCommand ?? runCommand)(command, dependencies.runChild);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown compatibility command error";
|
|
99
|
+
writeErrorRecord("invalid_command", errorMessage);
|
|
100
|
+
return 2;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) process.exitCode = await main();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { buildCommand, createHelpText, main } from "./codex-compat.mjs";
|
|
4
|
+
|
|
5
|
+
test("help describes both compatibility commands", () => {
|
|
6
|
+
assert.match(createHelpText(), /materialize/);
|
|
7
|
+
assert.match(createHelpText(), /bridge/);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("missing required arguments returns a usage error", async () => {
|
|
11
|
+
const messages = [];
|
|
12
|
+
const originalWrite = process.stderr.write;
|
|
13
|
+
process.stderr.write = (message) => messages.push(message);
|
|
14
|
+
try { assert.equal(await main(["materialize"], { findPython: async () => "python" }), 2); } finally { process.stderr.write = originalWrite; }
|
|
15
|
+
assert.equal(JSON.parse(messages.join("")).error.code, "invalid_command");
|
|
16
|
+
assert.match(messages.join(""), /--source-root/);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("unknown command returns structured error without running a child", async () => {
|
|
20
|
+
const messages = [];
|
|
21
|
+
let wasRun = false;
|
|
22
|
+
const originalWrite = process.stderr.write;
|
|
23
|
+
process.stderr.write = (message) => messages.push(message);
|
|
24
|
+
try {
|
|
25
|
+
const exitCode = await main(["unknown"], {
|
|
26
|
+
findPython: async () => "python",
|
|
27
|
+
runCommand: async () => { wasRun = true; return 0; },
|
|
28
|
+
});
|
|
29
|
+
assert.equal(exitCode, 2);
|
|
30
|
+
} finally { process.stderr.write = originalWrite; }
|
|
31
|
+
assert.equal(JSON.parse(messages.join("")).error.code, "invalid_command");
|
|
32
|
+
assert.equal(wasRun, false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("commands use safe argv and forward dry-run and apply", () => {
|
|
36
|
+
const dryRun = buildCommand("materialize", ["--source-root", "source root", "--target-root", "target;root"], "python");
|
|
37
|
+
const apply = buildCommand("materialize", ["--source-root", "source", "--target-root", "target", "--apply"], "python");
|
|
38
|
+
assert.equal(dryRun.arguments.at(-1), "target;root");
|
|
39
|
+
assert.equal(apply.arguments.at(-1), "--apply");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("bridge forwarding and child status use injected runners", async () => {
|
|
43
|
+
let receivedCommand;
|
|
44
|
+
const exitCode = await main(["bridge", "--surface", "TaskCreate", "--payload", '{"name":"ship","status":"pending"}'], {
|
|
45
|
+
findPython: async () => "python-test",
|
|
46
|
+
runCommand: async (command) => { receivedCommand = command; return 7; },
|
|
47
|
+
});
|
|
48
|
+
assert.equal(exitCode, 7);
|
|
49
|
+
assert.equal(receivedCommand.executable, "python-test");
|
|
50
|
+
assert.equal(receivedCommand.arguments.at(-1), '{"name":"ship","status":"pending"}');
|
|
51
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named values the installer reads, kept out of the code that uses them.
|
|
3
|
+
*
|
|
4
|
+
* `install.mjs` holds the install pipeline; the tables below hold the data that
|
|
5
|
+
* pipeline compares against, so a value has one home and one spelling.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Entry names the source walk leaves behind, whatever directory they sit in.
|
|
10
|
+
*
|
|
11
|
+
* Each name belongs to a tool that writes beside the source it reads: Python
|
|
12
|
+
* bytecode caches, the ruff, pytest, and mypy caches, an installed
|
|
13
|
+
* `node_modules` tree, and the macOS Finder's `.DS_Store` marker. A contributor
|
|
14
|
+
* who runs the test suites and then runs `node bin/install.mjs` copies from a
|
|
15
|
+
* source tree carrying all of them, so skipping the names at the walk keeps them
|
|
16
|
+
* out of `~/.claude` and out of the install manifest.
|
|
17
|
+
*/
|
|
18
|
+
export const SKIPPED_SOURCE_ENTRY_NAMES = new Set([
|
|
19
|
+
'__pycache__',
|
|
20
|
+
'.ruff_cache',
|
|
21
|
+
'.pytest_cache',
|
|
22
|
+
'.mypy_cache',
|
|
23
|
+
'node_modules',
|
|
24
|
+
'.DS_Store',
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* File extensions the source walk leaves behind, compared in lower case.
|
|
29
|
+
*
|
|
30
|
+
* Python writes a `.pyc` beside a module inside `__pycache__` and a `.pyo` under
|
|
31
|
+
* an optimized run, and either can land outside a cache directory, so the
|
|
32
|
+
* extension check stands alongside the name check.
|
|
33
|
+
*/
|
|
34
|
+
export const SKIPPED_SOURCE_FILE_EXTENSIONS = new Set(['.pyc', '.pyo']);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The name shape a run backup directory carries under
|
|
38
|
+
* `~/.claude/.claude-dev-env-pruned/`.
|
|
39
|
+
*
|
|
40
|
+
* The installer names each run backup from an ISO timestamp with every `:` and
|
|
41
|
+
* `.` rewritten as `-`, for example `2026-07-25T18-04-11-923Z`. The retention
|
|
42
|
+
* sweep removes only a direct child matching this shape, so a directory any
|
|
43
|
+
* other tool or person left in the backup directory stays where it is.
|
|
44
|
+
*/
|
|
45
|
+
export const RUN_BACKUP_DIRECTORY_NAME_PATTERN =
|
|
46
|
+
/^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z$/;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The directory name skill directories carry in a package source and under
|
|
50
|
+
* `~/.claude`.
|
|
51
|
+
*
|
|
52
|
+
* The copy loop reads `<package-root>/skills` and writes `~/.claude/skills`, the
|
|
53
|
+
* dependency walk reads `<dependency-root>/skills`, the retired-skill prune reads
|
|
54
|
+
* the installed directory, and the per-root stale-file prune names it as one of
|
|
55
|
+
* its roots. One spelling serves every one of them, so the copy destination and
|
|
56
|
+
* the prune target stay the same directory.
|
|
57
|
+
*/
|
|
58
|
+
export const MANAGED_SKILLS_DIRECTORY_NAME = 'skills';
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The directory name hook scripts carry in a package source and under
|
|
62
|
+
* `~/.claude`.
|
|
63
|
+
*
|
|
64
|
+
* The copy loop reads `<package-root>/hooks` and writes `~/.claude/hooks`, the
|
|
65
|
+
* hooks.json reads sit under the same name in each package source, the git-hook
|
|
66
|
+
* shims and the mypy configuration point at the installed directory, the
|
|
67
|
+
* retired-hook diff takes its relative paths against it, and the per-root
|
|
68
|
+
* stale-file prune names it as one of its roots.
|
|
69
|
+
*/
|
|
70
|
+
export const MANAGED_HOOKS_DIRECTORY_NAME = 'hooks';
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The `~/.claude` file name that holds the user's harness settings.
|
|
74
|
+
*
|
|
75
|
+
* The hook merge writes it, the retired-hook prune rewrites it, and the uninstall
|
|
76
|
+
* purge rewrites it, so all three reach the file through one name.
|
|
77
|
+
*/
|
|
78
|
+
export const SETTINGS_FILE_NAME = 'settings.json';
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The home-directory file name `install_mypy_ini.mjs` writes.
|
|
82
|
+
*
|
|
83
|
+
* mypy reads its configuration from the home directory, so this is the one file
|
|
84
|
+
* the installer writes outside `~/.claude`. The install records the path and the
|
|
85
|
+
* uninstall containment guard names it as a permitted location, so `--uninstall`
|
|
86
|
+
* removes the file the install created.
|
|
87
|
+
*/
|
|
88
|
+
export const MYPY_INI_FILE_NAME = '.mypy.ini';
|