claude-dev-env 4.3.0 → 7.1.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/agents/pr-description-writer.md +2 -2
- package/.agents/agents/test_agent_frontmatter.py +2 -2
- package/.agents/agents/test_pr_description_writer_style.py +1 -3
- package/.agents/skills/e-code-review/SKILL.md +12 -12
- package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
- package/.agents/skills/e-code-review/reference/fix.md +15 -17
- package/.agents/skills/e-code-review/reference/loop.md +35 -39
- package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
- package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
- package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
- package/.agents/skills/issue-tracker/SKILL.md +5 -4
- package/.agents/skills/pull-request/SKILL.md +11 -13
- package/.agents/skills/pull-request/reference/publication-tasks.md +3 -3
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +9 -1
- package/.agents/skills/pull-request/scripts/pull_request.py +35 -44
- package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +9 -0
- package/.agents/skills/pull-request/scripts/test_pull_request.py +35 -3
- package/.agents/skills/session-continuity/README.md +189 -0
- package/.agents/skills/session-continuity/SKILL.md +114 -0
- package/.agents/skills/session-continuity/continuity.mjs +375 -0
- package/.agents/skills/session-continuity/continuity.test.mjs +303 -0
- package/.agents/skills/skill-builder/SKILL.md +3 -196
- package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
- package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
- package/bin/cde.mjs +36 -8
- package/bin/cde.test.mjs +48 -0
- package/bin/ever-shipped-skills.mjs +2 -20
- package/bin/ever-shipped-skills.test.mjs +8 -0
- package/bin/expand_home_directory_tokens.mjs +4 -5
- package/bin/expand_home_directory_tokens.test.mjs +128 -0
- package/bin/git_hooks_installer.mjs +22 -3
- package/bin/git_hooks_installer.test.mjs +159 -5
- package/bin/install-session-continuity.mjs +78 -0
- package/bin/install-session-continuity.test.mjs +42 -0
- package/bin/install.agents-home.test.mjs +15 -13
- package/bin/install.codex-rules.test.mjs +5 -2
- package/bin/install.cursor-rules.test.mjs +5 -2
- package/bin/install.mjs +506 -63
- package/bin/install.policy-lint.test.mjs +130 -0
- package/bin/install.profiles.test.mjs +10 -0
- package/bin/install.prune.test.mjs +286 -37
- package/bin/install.pstack-plugin.test.mjs +114 -0
- package/bin/install.test.mjs +537 -120
- package/bin/install.transaction.test.mjs +3 -3
- package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
- package/docs/CODE_RULES.md +1 -1
- package/hooks/advisory/pr_done_reminder.py +342 -0
- package/hooks/advisory/test_pr_done_reminder.py +249 -0
- package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
- package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
- package/hooks/blocking/code_rules_paired_test.py +22 -15
- package/hooks/blocking/precommit_code_rules_gate.py +5 -8
- package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
- package/hooks/blocking/test_bash_post_call_dispatcher.py +65 -4
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
- package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
- package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
- package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
- package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +43 -342
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
- package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
- package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
- package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
- package/hooks/git-hooks/post_commit.py +33 -0
- package/hooks/git-hooks/pre_commit.py +18 -49
- package/hooks/git-hooks/pre_push.py +45 -541
- package/hooks/git-hooks/test_installed_policy_lint.py +208 -0
- package/hooks/git-hooks/test_native_hook_support.py +129 -0
- package/hooks/git-hooks/test_native_policy_commit.py +120 -0
- package/hooks/git-hooks/test_post_commit.py +43 -0
- package/hooks/git-hooks/test_pre_commit.py +38 -148
- package/hooks/git-hooks/test_pre_push.py +48 -1631
- package/hooks/git-hooks/test_verification_notice.py +60 -0
- package/hooks/git-hooks/test_verification_start.py +173 -0
- package/hooks/git-hooks/verification_notice.py +192 -0
- package/hooks/git-hooks/verification_notice_context.py +253 -0
- package/hooks/git-hooks/verification_notice_state.py +237 -0
- package/hooks/git-hooks/verification_start.py +100 -0
- package/hooks/hooks.json +1 -120
- package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
- package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
- package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
- package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -93
- package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
- package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +19 -2
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
- package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +46 -201
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
- package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
- package/hooks/validators/run_all_validators.py +5 -1
- package/hooks/validators/security_checks.py +57 -2
- package/hooks/validators/test_hook_timing_harness.py +2 -5
- package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
- package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
- package/hooks/validators/test_security_checks.py +57 -0
- package/package.json +2 -2
- package/rules/confirm-implementation-forks.md +2 -0
- package/rules/durable-post-artifacts.md +2 -1
- package/rules/git-workflow.md +18 -6
- package/rules/pstack-host-mapping.md +48 -0
- package/rules/vault-context.md +3 -5
- package/rules/verify-runtime-state.md +7 -0
- package/scripts/audit-hooks-cli.mjs +5 -0
- package/scripts/audit-hooks.mjs +72 -16
- package/scripts/automatic_advisory/__init__.py +1 -0
- package/scripts/automatic_advisory/checkout.py +51 -0
- package/scripts/automatic_advisory/cli.py +334 -0
- package/scripts/automatic_advisory/config/__init__.py +1 -0
- package/scripts/automatic_advisory/config/constants.py +93 -0
- package/scripts/automatic_advisory/config/timing.py +4 -0
- package/scripts/automatic_advisory/configuration.py +197 -0
- package/scripts/automatic_advisory/execution.py +350 -0
- package/scripts/automatic_advisory/git.py +69 -0
- package/scripts/automatic_advisory/model.py +107 -0
- package/scripts/automatic_advisory/process_host.py +32 -0
- package/scripts/automatic_advisory/publisher.py +84 -0
- package/scripts/automatic_advisory/runner.py +399 -0
- package/scripts/automatic_advisory/state.py +116 -0
- package/scripts/automatic_advisory/window_flags.py +41 -0
- package/scripts/automatic_advisory/windows_job.py +122 -0
- package/scripts/check.ps1 +0 -1
- package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
- package/scripts/ci/installer-lifecycle.test.mjs +26 -0
- package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
- package/scripts/codex_compat_materializer.py +99 -13
- package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
- package/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +35 -43
- package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +10 -10
- package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +14 -1
- package/scripts/local_report_cli.py +184 -0
- package/scripts/local_report_core.py +247 -0
- package/scripts/local_report_publisher.py +12 -0
- package/scripts/local_report_validation.py +357 -0
- package/scripts/local_verification/__init__.py +23 -0
- package/scripts/local_verification/check_runner.py +364 -0
- package/scripts/local_verification/check_support.py +99 -0
- package/scripts/local_verification/cli.py +158 -0
- package/scripts/local_verification/command_runner.py +84 -0
- package/scripts/local_verification/config/__init__.py +1 -0
- package/scripts/local_verification/config/constants.py +89 -0
- package/scripts/local_verification/config/timing.py +1 -0
- package/scripts/local_verification/git_state.py +146 -0
- package/scripts/local_verification/manifest.py +224 -0
- package/scripts/local_verification/model.py +174 -0
- package/scripts/local_verification/runner.py +234 -0
- package/scripts/policy_lint/adapter_detectors.py +13 -6
- package/scripts/policy_lint/adapter_pairing.py +58 -3
- package/scripts/policy_lint/adapters.py +7 -4
- package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
- package/scripts/policy_lint/config/constants.py +5 -0
- package/scripts/policy_lint/engine.py +48 -4
- package/scripts/policy_lint/model.py +1 -0
- package/scripts/policy_lint/registry.py +1 -1
- package/scripts/policy_lint/selection.py +2 -1
- package/scripts/policy_lint/selection_git.py +2 -0
- package/scripts/pr_verification/__init__.py +1 -0
- package/scripts/pr_verification/branch_refs.py +114 -0
- package/scripts/pr_verification/config/__init__.py +1 -0
- package/scripts/pr_verification/config/constants.py +84 -0
- package/scripts/pr_verification/config/timing.py +5 -0
- package/scripts/pr_verification/github.py +137 -0
- package/scripts/pr_verification/github_api.py +376 -0
- package/scripts/pr_verification/github_labels.py +88 -0
- package/scripts/pr_verification/github_parsing.py +182 -0
- package/scripts/pr_verification/github_transport.py +91 -0
- package/scripts/pr_verification/lock.py +122 -0
- package/scripts/pr_verification/model.py +43 -0
- package/scripts/pr_verification/requirements.txt +1 -0
- package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
- package/scripts/shared_tree_paths.py +32 -3
- package/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +97 -0
- package/scripts/test_shared_tree_paths.py +33 -0
- package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
- package/scripts/tests/test-audit-hooks.mjs +94 -2
- package/scripts/tests/test_automatic_advisory.py +376 -0
- package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
- package/scripts/tests/test_automatic_advisory_polling.py +216 -0
- package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
- package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
- package/scripts/tests/test_automatic_advisory_settings.py +48 -0
- package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
- package/scripts/tests/test_closed_pr_label.py +157 -0
- package/scripts/tests/test_codex_compat_materializer.py +189 -6
- package/scripts/tests/test_github_labels.py +101 -0
- package/scripts/tests/test_github_transport.py +125 -0
- package/scripts/tests/test_local_report_publisher.py +396 -0
- package/scripts/tests/test_local_report_publisher_cli.py +85 -0
- package/scripts/tests/test_local_verification.py +348 -0
- package/scripts/tests/test_local_verification_cli.py +98 -0
- package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
- package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
- package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
- package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
- package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
- package/scripts/tests/test_policy_lint_selection.py +25 -2
- package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
- package/scripts/tests/test_pr_verification_github.py +392 -0
- package/scripts/tests/test_pr_verification_lock.py +168 -0
- package/scripts/tests/test_rules.py +3 -7
- package/scripts/tests/test_verification_notice.py +347 -0
- package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
- package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
- package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
- package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
- package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
- package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
- package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
- package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
- package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
- package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
- package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
- package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
- package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
- package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
- package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
- package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
- package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
- package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
- package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
- package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
- package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
- package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
- package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
- package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
- package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
- package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
- package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
- package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
- package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
- package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
- package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
- package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
- package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
- package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
- package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
- package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
- package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
- package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
- package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
- package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
- package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/SKILL.md +0 -431
- package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
- package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
- package/.agents/skills/autoconverge/reference/convergence.md +0 -219
- package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
- package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
- package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
- package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
- package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
- package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
- package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
- package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
- package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
- package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
- package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
- package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
- package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
- package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
- package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
- package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
- package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
- package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
- package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
- package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
- package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
- package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
- package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
- package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
- package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
- package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
- package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
- package/.agents/skills/hitl/SKILL.md +0 -45
- package/.agents/skills/pr-cleanup/SKILL.md +0 -140
- package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
- package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
- package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
- package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
- package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
- package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
- package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
- package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
- package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
- package/.agents/skills/pr-refinement/SKILL.md +0 -31
- package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
- package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
- package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
- package/.agents/skills/pr-small-cl/SKILL.md +0 -52
- package/.agents/skills/pr-title-description/SKILL.md +0 -96
- package/.agents/skills/prototype/SKILL.md +0 -85
- package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
- package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
- package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
- package/.agents/skills/prototype/scripts/conftest.py +0 -15
- package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
- package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
- package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
- package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
- package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
- package/.agents/skills/prototype/workflows/promotion.md +0 -25
- package/.agents/skills/prototype/workflows/sandbox.md +0 -35
- package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
- package/.agents/skills/rebase/AGENTS.md +0 -1
- package/.agents/skills/rebase/SKILL.md +0 -178
- package/.agents/skills/review-router/SKILL.md +0 -51
- package/.agents/skills/review-router/reference/route-policy.json +0 -7
- package/.agents/skills/review-router/reference/task-seeds.md +0 -1
- package/.agents/skills/review-router/scripts/conftest.py +0 -4
- package/.agents/skills/review-router/scripts/review_router.py +0 -50
- package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
- package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
- package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
- package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
- package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
- package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
- package/.agents/skills/review-tier/SKILL.md +0 -56
- package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
- package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
- package/.agents/skills/review-tier/scripts/conftest.py +0 -4
- package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
- package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
- package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
- package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
- package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
- package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
- package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
- package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
- package/.agents/skills/session-log/AGENTS.md +0 -1
- package/.agents/skills/session-log/SKILL.md +0 -170
- package/.agents/skills/session-log/templates/frontmatter.md +0 -40
- package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
- package/.agents/skills/session-tidy/AGENTS.md +0 -1
- package/.agents/skills/session-tidy/SKILL.md +0 -191
- package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
- package/.agents/skills/skill-builder/references/description-field.md +0 -111
- package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
- package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
- package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
- package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
- package/.agents/skills/skill-builder/references/skill-types.md +0 -232
- package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
- package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
- package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
- package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
- package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
- package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
- package/.agents/skills/update/.claude/CLAUDE.md +0 -1
- package/.agents/skills/update/AGENTS.md +0 -1
- package/.agents/skills/update/SKILL.md +0 -190
- package/_shared/pr-loop/AGENTS.md +0 -1
- package/_shared/pr-loop/audit-contract.md +0 -183
- package/_shared/pr-loop/audit-reply-template.md +0 -151
- package/_shared/pr-loop/code-rules-gate.md +0 -65
- package/_shared/pr-loop/fix-protocol.md +0 -60
- package/_shared/pr-loop/gh-payloads.md +0 -75
- package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
- package/_shared/pr-loop/precatch-rubric.md +0 -66
- package/_shared/pr-loop/preflight-proposal.md +0 -56
- package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
- package/_shared/pr-loop/state-schema.md +0 -45
- package/_shared/pr-loop/worker-spawn.md +0 -189
- package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
- package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
- package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
- package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
- package/hooks/blocking/destructive_command_blocker.py +0 -2073
- package/hooks/blocking/fable_spawn_gate.py +0 -187
- package/hooks/blocking/luna_fast_mode_gate.py +0 -160
- package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
- package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
- package/hooks/blocking/pii_prevention_blocker.py +0 -365
- package/hooks/blocking/piped_pytest_blocker.py +0 -1223
- package/hooks/blocking/question_to_user_enforcer.py +0 -130
- package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
- package/hooks/blocking/sensitive_file_protector.py +0 -167
- package/hooks/blocking/session_edit_stage_gate.py +0 -684
- package/hooks/blocking/session_handoff_blocker.py +0 -182
- package/hooks/blocking/shell_substitution_blocker.py +0 -350
- package/hooks/blocking/test_ask_user_question_shape.py +0 -69
- package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
- package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
- package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
- package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
- package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
- package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
- package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
- package/hooks/blocking/test_fable_spawn_gate.py +0 -381
- package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
- package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
- package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
- package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
- package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
- package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
- package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
- package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
- package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
- package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
- package/hooks/blocking/test_sensitive_file_protector.py +0 -365
- package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
- package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
- package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
- package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
- package/hooks/blocking/test_session_handoff_blocker.py +0 -318
- package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
- package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
- package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
- package/hooks/blocking/unscoped_search_blocker.py +0 -391
- package/hooks/blocking/write_existing_file_blocker.py +0 -124
- package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
- package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
- package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
- package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
- package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
- package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
- package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
- /package/.agents/skills/{_shared/pr-loop → session-continuity}/AGENTS.md +0 -0
- /package/{_shared/pr-loop → .agents/skills/session-continuity}/CLAUDE.md +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
|
|
5
|
+
from .config.constants import (
|
|
6
|
+
GITHUB_SHAPE_ERROR_TEMPLATE,
|
|
7
|
+
PULL_BASE_KEY,
|
|
8
|
+
PULL_DRAFT_KEY,
|
|
9
|
+
PULL_HEAD_KEY,
|
|
10
|
+
PULL_LIST_RESOURCE_NAME,
|
|
11
|
+
PULL_MERGE_SHA_KEY,
|
|
12
|
+
PULL_NUMBER_KEY,
|
|
13
|
+
PULL_REF_KEY,
|
|
14
|
+
PULL_RESOURCE_NAME,
|
|
15
|
+
PULL_SHA_KEY,
|
|
16
|
+
)
|
|
17
|
+
from .model import PullRequestCandidate
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GitHubError(RuntimeError):
|
|
21
|
+
"""Raised when GitHub metadata has an invalid shape."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _require_pull_list(all_pull_entries: object) -> list[object]:
|
|
25
|
+
if isinstance(all_pull_entries, list):
|
|
26
|
+
return all_pull_entries
|
|
27
|
+
raise GitHubError(
|
|
28
|
+
GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=PULL_LIST_RESOURCE_NAME)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _parse_pull_candidates(
|
|
33
|
+
repository_slug: str,
|
|
34
|
+
all_pull_entries: list[object],
|
|
35
|
+
should_require_merge_commit: bool,
|
|
36
|
+
) -> tuple[PullRequestCandidate, ...]:
|
|
37
|
+
all_candidates: list[PullRequestCandidate] = []
|
|
38
|
+
for each_pull in all_pull_entries:
|
|
39
|
+
maybe_candidate = parse_recognized_candidate(
|
|
40
|
+
repository_slug,
|
|
41
|
+
each_pull,
|
|
42
|
+
should_require_merge_commit=should_require_merge_commit,
|
|
43
|
+
)
|
|
44
|
+
if maybe_candidate is not None:
|
|
45
|
+
all_candidates.append(maybe_candidate)
|
|
46
|
+
return tuple(all_candidates)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _read_pull_branches(
|
|
50
|
+
all_pull_fields: Mapping[str, object],
|
|
51
|
+
) -> tuple[Mapping[str, object], Mapping[str, object]]:
|
|
52
|
+
base = require_mapping(all_pull_fields.get(PULL_BASE_KEY), PULL_RESOURCE_NAME)
|
|
53
|
+
head = require_mapping(all_pull_fields.get(PULL_HEAD_KEY), PULL_RESOURCE_NAME)
|
|
54
|
+
return base, head
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _read_pull_draft(all_pull_fields: Mapping[str, object]) -> bool:
|
|
58
|
+
is_draft = all_pull_fields.get(PULL_DRAFT_KEY)
|
|
59
|
+
if isinstance(is_draft, bool):
|
|
60
|
+
return is_draft
|
|
61
|
+
raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=PULL_RESOURCE_NAME))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def parse_candidate(
|
|
65
|
+
repository_slug: str,
|
|
66
|
+
raw_pull: object,
|
|
67
|
+
*,
|
|
68
|
+
should_require_merge_commit: bool,
|
|
69
|
+
) -> PullRequestCandidate:
|
|
70
|
+
"""Parse pull request metadata.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
repository_slug: Repository slug.
|
|
74
|
+
raw_pull: GitHub pull JSON.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Parsed pull request.
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
GitHubError: If metadata is invalid.
|
|
81
|
+
"""
|
|
82
|
+
pull = require_mapping(raw_pull, PULL_RESOURCE_NAME)
|
|
83
|
+
base, head = _read_pull_branches(pull)
|
|
84
|
+
pull_number = require_positive_integer(pull, PULL_NUMBER_KEY, PULL_RESOURCE_NAME)
|
|
85
|
+
return PullRequestCandidate(
|
|
86
|
+
repository_slug=repository_slug,
|
|
87
|
+
pull_request_number=pull_number,
|
|
88
|
+
base_ref=require_text(base, PULL_REF_KEY, PULL_RESOURCE_NAME),
|
|
89
|
+
base_sha=require_text(base, PULL_SHA_KEY, PULL_RESOURCE_NAME),
|
|
90
|
+
head_sha=require_text(head, PULL_SHA_KEY, PULL_RESOURCE_NAME),
|
|
91
|
+
merge_sha=_read_merge_sha(pull, should_require_merge_commit),
|
|
92
|
+
is_draft=_read_pull_draft(pull),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _read_merge_sha(
|
|
97
|
+
all_pull_fields: Mapping[str, object],
|
|
98
|
+
should_require_merge_commit: bool,
|
|
99
|
+
) -> str:
|
|
100
|
+
merge_sha = all_pull_fields.get(PULL_MERGE_SHA_KEY)
|
|
101
|
+
if merge_sha is None and not should_require_merge_commit:
|
|
102
|
+
return ""
|
|
103
|
+
if not isinstance(merge_sha, str) or not merge_sha:
|
|
104
|
+
raise GitHubError(
|
|
105
|
+
GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=PULL_RESOURCE_NAME)
|
|
106
|
+
)
|
|
107
|
+
return merge_sha
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def parse_recognized_candidate(
|
|
111
|
+
repository_slug: str,
|
|
112
|
+
raw_pull: object,
|
|
113
|
+
*,
|
|
114
|
+
should_require_merge_commit: bool = True,
|
|
115
|
+
) -> PullRequestCandidate | None:
|
|
116
|
+
"""Parse a pull request with a merge commit.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
repository_slug: Repository slug.
|
|
120
|
+
raw_pull: GitHub pull JSON.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Parsed pull request or None.
|
|
124
|
+
"""
|
|
125
|
+
pull = require_mapping(raw_pull, PULL_RESOURCE_NAME)
|
|
126
|
+
if not should_require_merge_commit:
|
|
127
|
+
return parse_candidate(
|
|
128
|
+
repository_slug,
|
|
129
|
+
pull,
|
|
130
|
+
should_require_merge_commit=False,
|
|
131
|
+
)
|
|
132
|
+
if pull.get(PULL_MERGE_SHA_KEY) is None:
|
|
133
|
+
return None
|
|
134
|
+
return parse_candidate(repository_slug, pull, should_require_merge_commit=True)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def require_mapping(raw_payload: object, resource: str) -> Mapping[str, object]:
|
|
138
|
+
if not isinstance(raw_payload, Mapping):
|
|
139
|
+
raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=resource))
|
|
140
|
+
return raw_payload
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def require_text(
|
|
144
|
+
all_payload_fields: Mapping[str, object], field_name: str, resource: str
|
|
145
|
+
) -> str:
|
|
146
|
+
"""Read required text from a JSON object.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
all_payload_fields: JSON object.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Required text.
|
|
153
|
+
|
|
154
|
+
Raises:
|
|
155
|
+
GitHubError: If the field is absent.
|
|
156
|
+
"""
|
|
157
|
+
field_text = all_payload_fields.get(field_name)
|
|
158
|
+
if not isinstance(field_text, str) or not field_text:
|
|
159
|
+
raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=resource))
|
|
160
|
+
return field_text
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def require_positive_integer(
|
|
164
|
+
all_payload_fields: Mapping[str, object], field_name: str, resource: str
|
|
165
|
+
) -> int:
|
|
166
|
+
"""Read a required positive integer.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
all_payload_fields: JSON object.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Required integer.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
GitHubError: If the field is invalid.
|
|
176
|
+
"""
|
|
177
|
+
field_integer = all_payload_fields.get(field_name)
|
|
178
|
+
if isinstance(field_integer, bool) or not isinstance(field_integer, int):
|
|
179
|
+
raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=resource))
|
|
180
|
+
if field_integer < 1:
|
|
181
|
+
raise GitHubError(GITHUB_SHAPE_ERROR_TEMPLATE.format(resource=resource))
|
|
182
|
+
return field_integer
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import urllib.error
|
|
4
|
+
import urllib.request
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from http.client import HTTPResponse as OpenedHttpReply
|
|
8
|
+
|
|
9
|
+
import jwt
|
|
10
|
+
|
|
11
|
+
from .config.constants import (
|
|
12
|
+
UTF8_ENCODING,
|
|
13
|
+
)
|
|
14
|
+
from .config.timing import HTTP_TIMEOUT_SECONDS
|
|
15
|
+
from .github_parsing import GitHubError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class HttpReply:
|
|
20
|
+
status_code: int
|
|
21
|
+
headers: dict[str, str]
|
|
22
|
+
body: bytes
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
HttpRequester = Callable[[str, str, dict[str, str], bytes | None], HttpReply]
|
|
26
|
+
JwtEncoder = Callable[[dict[str, int | str], str, str], str]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _read_http_error(error: urllib.error.HTTPError) -> HttpReply:
|
|
30
|
+
return HttpReply(
|
|
31
|
+
status_code=error.code,
|
|
32
|
+
headers=dict(error.headers.items()),
|
|
33
|
+
body=error.read(),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def request_http(
|
|
38
|
+
method: str,
|
|
39
|
+
url: str,
|
|
40
|
+
all_headers: dict[str, str],
|
|
41
|
+
body: bytes | None,
|
|
42
|
+
) -> HttpReply:
|
|
43
|
+
"""Send one GitHub HTTP request.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
method: HTTP method.
|
|
47
|
+
url: Absolute request URL.
|
|
48
|
+
all_headers: Request headers.
|
|
49
|
+
body: Encoded request body, or None.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The HTTP reply, including error replies with a status code.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
GitHubError: If the request fails before a status code exists.
|
|
56
|
+
"""
|
|
57
|
+
request = urllib.request.Request(
|
|
58
|
+
url=url, data=body, headers=all_headers, method=method
|
|
59
|
+
)
|
|
60
|
+
try:
|
|
61
|
+
with urllib.request.urlopen(request, timeout=HTTP_TIMEOUT_SECONDS) as reply:
|
|
62
|
+
return _read_http_reply(reply)
|
|
63
|
+
except urllib.error.HTTPError as error:
|
|
64
|
+
return _read_http_error(error)
|
|
65
|
+
except (urllib.error.URLError, OSError) as error:
|
|
66
|
+
raise GitHubError(str(error)) from error
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _read_http_reply(reply: OpenedHttpReply) -> HttpReply:
|
|
70
|
+
return HttpReply(
|
|
71
|
+
status_code=reply.status,
|
|
72
|
+
headers=dict(reply.headers.items()),
|
|
73
|
+
body=reply.read(),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def encode_app_jwt(
|
|
78
|
+
all_claims: dict[str, int | str], private_key: str, algorithm: str
|
|
79
|
+
) -> str:
|
|
80
|
+
"""Encode an app JWT.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
all_claims: JWT claims.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Encoded token.
|
|
87
|
+
"""
|
|
88
|
+
encoded_token = jwt.encode(all_claims, private_key, algorithm=algorithm)
|
|
89
|
+
if isinstance(encoded_token, bytes):
|
|
90
|
+
return encoded_token.decode(UTF8_ENCODING)
|
|
91
|
+
return encoded_token
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from types import TracebackType
|
|
7
|
+
from typing import Self
|
|
8
|
+
|
|
9
|
+
if sys.platform == "win32":
|
|
10
|
+
import msvcrt
|
|
11
|
+
else:
|
|
12
|
+
import fcntl
|
|
13
|
+
|
|
14
|
+
from .config.constants import (
|
|
15
|
+
SUPERVISOR_LOCK_ERROR,
|
|
16
|
+
SUPERVISOR_LOCK_FILE_MODE,
|
|
17
|
+
SUPERVISOR_LOCK_FILENAME,
|
|
18
|
+
SUPERVISOR_LOCK_IO_ERROR,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if sys.platform == "win32":
|
|
22
|
+
|
|
23
|
+
def _acquire_file_lock(file_descriptor: int) -> None:
|
|
24
|
+
"""Acquire one nonblocking Windows lock."""
|
|
25
|
+
os.lseek(file_descriptor, 0, os.SEEK_SET)
|
|
26
|
+
msvcrt.locking(file_descriptor, msvcrt.LK_NBLCK, 1)
|
|
27
|
+
|
|
28
|
+
def _release_file_lock(file_descriptor: int) -> None:
|
|
29
|
+
"""Release one Windows lock."""
|
|
30
|
+
os.lseek(file_descriptor, 0, os.SEEK_SET)
|
|
31
|
+
msvcrt.locking(file_descriptor, msvcrt.LK_UNLCK, 1)
|
|
32
|
+
|
|
33
|
+
else:
|
|
34
|
+
|
|
35
|
+
def _acquire_file_lock(file_descriptor: int) -> None:
|
|
36
|
+
"""Acquire one nonblocking POSIX lock."""
|
|
37
|
+
fcntl.flock(file_descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
38
|
+
|
|
39
|
+
def _release_file_lock(file_descriptor: int) -> None:
|
|
40
|
+
"""Release one POSIX lock."""
|
|
41
|
+
fcntl.flock(file_descriptor, fcntl.LOCK_UN)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SupervisorLockError(RuntimeError):
|
|
45
|
+
"""Raised when the supervisor lock cannot be acquired or released."""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class SupervisorLock:
|
|
49
|
+
def __init__(self, cache_root: Path) -> None:
|
|
50
|
+
self.cache_root = cache_root.resolve()
|
|
51
|
+
self.lock_path = self.cache_root / SUPERVISOR_LOCK_FILENAME
|
|
52
|
+
self.file_descriptor: int | None = None
|
|
53
|
+
|
|
54
|
+
def __enter__(self) -> Self:
|
|
55
|
+
self.acquire()
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
def __exit__(
|
|
59
|
+
self,
|
|
60
|
+
exception_type: type[BaseException] | None,
|
|
61
|
+
exception: BaseException | None,
|
|
62
|
+
traceback: TracebackType | None,
|
|
63
|
+
) -> None:
|
|
64
|
+
self.release()
|
|
65
|
+
|
|
66
|
+
def acquire(self) -> None:
|
|
67
|
+
"""Acquire the operating-system lock for this cache root.
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
SupervisorLockError: If another owner holds the lock or I/O fails.
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
self.cache_root.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
file_descriptor = os.open(
|
|
75
|
+
self.lock_path,
|
|
76
|
+
os.O_CREAT | os.O_RDWR,
|
|
77
|
+
SUPERVISOR_LOCK_FILE_MODE,
|
|
78
|
+
)
|
|
79
|
+
except OSError as error:
|
|
80
|
+
raise SupervisorLockError(SUPERVISOR_LOCK_IO_ERROR) from error
|
|
81
|
+
try:
|
|
82
|
+
_acquire_file_lock(file_descriptor)
|
|
83
|
+
except (BlockingIOError, PermissionError) as error:
|
|
84
|
+
_close_file_descriptor_without_raising(file_descriptor)
|
|
85
|
+
raise SupervisorLockError(SUPERVISOR_LOCK_ERROR) from error
|
|
86
|
+
except OSError as error:
|
|
87
|
+
_close_file_descriptor_without_raising(file_descriptor)
|
|
88
|
+
raise SupervisorLockError(SUPERVISOR_LOCK_IO_ERROR) from error
|
|
89
|
+
self.file_descriptor = file_descriptor
|
|
90
|
+
|
|
91
|
+
def release(self) -> None:
|
|
92
|
+
"""Release the operating-system lock and close its file descriptor.
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
SupervisorLockError: If unlocking or closing the file fails.
|
|
96
|
+
"""
|
|
97
|
+
file_descriptor = self.file_descriptor
|
|
98
|
+
if file_descriptor is None:
|
|
99
|
+
return
|
|
100
|
+
self.file_descriptor = None
|
|
101
|
+
try:
|
|
102
|
+
_release_file_lock(file_descriptor)
|
|
103
|
+
except OSError as error:
|
|
104
|
+
raise SupervisorLockError(SUPERVISOR_LOCK_IO_ERROR) from error
|
|
105
|
+
finally:
|
|
106
|
+
_close_file_descriptor(file_descriptor)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _close_file_descriptor(file_descriptor: int) -> None:
|
|
110
|
+
"""Close one lock file descriptor."""
|
|
111
|
+
try:
|
|
112
|
+
os.close(file_descriptor)
|
|
113
|
+
except OSError as error:
|
|
114
|
+
raise SupervisorLockError(SUPERVISOR_LOCK_IO_ERROR) from error
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _close_file_descriptor_without_raising(file_descriptor: int) -> None:
|
|
118
|
+
"""Close one lock file descriptor while a lock failure is already being raised."""
|
|
119
|
+
try:
|
|
120
|
+
os.close(file_descriptor)
|
|
121
|
+
except OSError:
|
|
122
|
+
return
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
from .config.constants import (
|
|
7
|
+
STATUS_ERROR,
|
|
8
|
+
STATUS_FAILURE,
|
|
9
|
+
STATUS_PENDING,
|
|
10
|
+
STATUS_SUCCESS,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class StatusState(str, Enum):
|
|
15
|
+
PENDING = STATUS_PENDING
|
|
16
|
+
ERROR = STATUS_ERROR
|
|
17
|
+
FAILURE = STATUS_FAILURE
|
|
18
|
+
SUCCESS = STATUS_SUCCESS
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class RepositorySettings:
|
|
23
|
+
slug: str
|
|
24
|
+
clone_url: str
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def name(self) -> str:
|
|
28
|
+
return self.slug.split("/", maxsplit=1)[1]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class PullRequestCandidate:
|
|
33
|
+
repository_slug: str
|
|
34
|
+
pull_request_number: int
|
|
35
|
+
base_ref: str
|
|
36
|
+
base_sha: str
|
|
37
|
+
head_sha: str
|
|
38
|
+
merge_sha: str
|
|
39
|
+
is_draft: bool
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def key(self) -> tuple[str, int]:
|
|
43
|
+
return self.repository_slug, self.pull_request_number
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
PyJWT[crypto]>=2.10,<3
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep a pstack skills-directory plugin manifest listing every skill folder.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code loads a folder under `~/.claude/skills` as a plugin when the
|
|
5
|
+
* folder holds `.claude-plugin/plugin.json`, and it namespaces that plugin's
|
|
6
|
+
* skills as `pstack:how`. It does not scan a plugin root for skill folders, so
|
|
7
|
+
* the manifest names each sub-skill path:
|
|
8
|
+
*
|
|
9
|
+
* plugin root holds: how/SKILL.md, why/SKILL.md, docs/, .claude-plugin/
|
|
10
|
+
* manifest before: "skills": ["./how"]
|
|
11
|
+
* manifest after: "skills": ["./how", "./why"] <- why/ picked up
|
|
12
|
+
* second run: no write <- the list matches
|
|
13
|
+
* docs/, .claude-plugin/ <- skipped, no SKILL.md
|
|
14
|
+
*
|
|
15
|
+
* The installer calls `refreshPstackPluginManifest` for a pstack folder the
|
|
16
|
+
* user installed. Running this file directly refreshes the same manifest after
|
|
17
|
+
* a pstack update.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
existsSync,
|
|
22
|
+
mkdirSync,
|
|
23
|
+
readdirSync,
|
|
24
|
+
readFileSync,
|
|
25
|
+
realpathSync,
|
|
26
|
+
writeFileSync,
|
|
27
|
+
} from 'node:fs';
|
|
28
|
+
import { homedir } from 'node:os';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
import { fileURLToPath } from 'node:url';
|
|
31
|
+
|
|
32
|
+
export const PSTACK_PLUGIN_DIRECTORY_NAME = 'pstack';
|
|
33
|
+
export const PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH = join('.claude-plugin', 'plugin.json');
|
|
34
|
+
export const SKILL_MANIFEST_FILE_NAME = 'SKILL.md';
|
|
35
|
+
export const SKILL_PATH_PREFIX = './';
|
|
36
|
+
|
|
37
|
+
const CLAUDE_HOME_DIRECTORY_NAME = '.claude';
|
|
38
|
+
const MANIFEST_SKILLS_KEY = 'skills';
|
|
39
|
+
const MANIFEST_INDENT_SPACES = 2;
|
|
40
|
+
const FILE_ENCODING = 'utf8';
|
|
41
|
+
const HIDDEN_NAME_PREFIX = '.';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Fields a first write seeds from the published pstack plugin. An existing
|
|
45
|
+
* manifest keeps its own values. This refresher changes only the skills list.
|
|
46
|
+
*/
|
|
47
|
+
const SEEDED_MANIFEST_FIELDS = Object.freeze({
|
|
48
|
+
$schema: 'https://anthropic.com/claude-code/plugin.schema.json',
|
|
49
|
+
name: PSTACK_PLUGIN_DIRECTORY_NAME,
|
|
50
|
+
version: '0.14.8',
|
|
51
|
+
description:
|
|
52
|
+
'if you want to go fast, go deep first. pstack helps you write less, but higher '
|
|
53
|
+
+ 'quality code. rigorous agent workflows you can parallelize with confidence.',
|
|
54
|
+
author: Object.freeze({ name: 'Lauren Tan' }),
|
|
55
|
+
homepage: 'https://github.com/cursor/plugins/tree/main/pstack',
|
|
56
|
+
repository: 'https://github.com/cursor/plugins',
|
|
57
|
+
license: 'MIT',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Name every sub-folder of a plugin root that Claude Code loads as a skill.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} pluginRoot Plugin folder holding one sub-folder per skill.
|
|
64
|
+
* @returns {string[]} The sorted names of each visible sub-folder holding a
|
|
65
|
+
* skill manifest. A hidden folder and a folder without `SKILL.md` are left out.
|
|
66
|
+
*/
|
|
67
|
+
export function findPstackSkillDirectoryNames(pluginRoot) {
|
|
68
|
+
return readdirSync(pluginRoot, { withFileTypes: true })
|
|
69
|
+
.filter((eachEntry) => eachEntry.isDirectory())
|
|
70
|
+
.map((eachEntry) => eachEntry.name)
|
|
71
|
+
.filter((eachName) => !eachName.startsWith(HIDDEN_NAME_PREFIX))
|
|
72
|
+
.filter((eachName) => existsSync(join(pluginRoot, eachName, SKILL_MANIFEST_FILE_NAME)))
|
|
73
|
+
.sort();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readManifestSkillPaths(manifestPath) {
|
|
77
|
+
if (!existsSync(manifestPath)) return { manifest: null, previousSkillPaths: [] };
|
|
78
|
+
const manifest = JSON.parse(readFileSync(manifestPath, FILE_ENCODING));
|
|
79
|
+
const listedSkillPaths = manifest[MANIFEST_SKILLS_KEY];
|
|
80
|
+
if (typeof listedSkillPaths === 'string') {
|
|
81
|
+
return { manifest, previousSkillPaths: [listedSkillPaths] };
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(listedSkillPaths)) {
|
|
84
|
+
return { manifest, previousSkillPaths: listedSkillPaths };
|
|
85
|
+
}
|
|
86
|
+
return { manifest, previousSkillPaths: [] };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Rewrite a pstack plugin manifest's skills list to match its folder.
|
|
91
|
+
*
|
|
92
|
+
* Writes only when the list changes. A second call on an unchanged folder
|
|
93
|
+
* leaves the file byte for byte. An existing manifest keeps every field but
|
|
94
|
+
* the skills list.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} pluginRoot Plugin folder holding the skill sub-folders.
|
|
97
|
+
* @returns {{didWrite: boolean, reason: string, previousSkillPaths: string[],
|
|
98
|
+
* currentSkillPaths: string[]}} Whether this call wrote, and the paths
|
|
99
|
+
* before and after. `reason` names a skip, and is empty on a write or an
|
|
100
|
+
* unchanged list.
|
|
101
|
+
*/
|
|
102
|
+
export function refreshPstackPluginManifest(pluginRoot) {
|
|
103
|
+
const declined = { didWrite: false, previousSkillPaths: [], currentSkillPaths: [] };
|
|
104
|
+
if (!existsSync(pluginRoot)) {
|
|
105
|
+
return { ...declined, reason: 'plugin root is absent' };
|
|
106
|
+
}
|
|
107
|
+
if (existsSync(join(pluginRoot, SKILL_MANIFEST_FILE_NAME))) {
|
|
108
|
+
return { ...declined, reason: 'plugin root is itself a skill' };
|
|
109
|
+
}
|
|
110
|
+
const allSkillNames = findPstackSkillDirectoryNames(pluginRoot);
|
|
111
|
+
if (allSkillNames.length === 0) {
|
|
112
|
+
return { ...declined, reason: 'plugin root holds no skills' };
|
|
113
|
+
}
|
|
114
|
+
const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
|
|
115
|
+
const { manifest, previousSkillPaths } = readManifestSkillPaths(manifestPath);
|
|
116
|
+
const currentSkillPaths = allSkillNames.map((eachName) => SKILL_PATH_PREFIX + eachName);
|
|
117
|
+
const outcome = { reason: '', previousSkillPaths, currentSkillPaths };
|
|
118
|
+
const isUnchanged = manifest !== null
|
|
119
|
+
&& previousSkillPaths.length === currentSkillPaths.length
|
|
120
|
+
&& previousSkillPaths.every((eachPath, eachIndex) => eachPath === currentSkillPaths[eachIndex]);
|
|
121
|
+
if (isUnchanged) {
|
|
122
|
+
return { ...outcome, didWrite: false };
|
|
123
|
+
}
|
|
124
|
+
const refreshedManifest = {
|
|
125
|
+
...(manifest === null ? SEEDED_MANIFEST_FIELDS : manifest),
|
|
126
|
+
[MANIFEST_SKILLS_KEY]: currentSkillPaths,
|
|
127
|
+
};
|
|
128
|
+
mkdirSync(join(pluginRoot, '.claude-plugin'), { recursive: true });
|
|
129
|
+
writeFileSync(
|
|
130
|
+
manifestPath,
|
|
131
|
+
JSON.stringify(refreshedManifest, null, MANIFEST_INDENT_SPACES) + '\n',
|
|
132
|
+
FILE_ENCODING,
|
|
133
|
+
);
|
|
134
|
+
return { ...outcome, didWrite: true };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Return the pstack plugin folder under a Claude home.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} claudeHome The Claude home directory holding `skills`.
|
|
141
|
+
* @returns {string} The pstack plugin folder path, present or not.
|
|
142
|
+
*/
|
|
143
|
+
export function pstackPluginRootFor(claudeHome) {
|
|
144
|
+
return join(claudeHome, 'skills', PSTACK_PLUGIN_DIRECTORY_NAME);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Return the Claude home used when no folder argument is given.
|
|
149
|
+
*
|
|
150
|
+
* A named profile sets `CLAUDE_CONFIG_DIR`. The installer writes into that
|
|
151
|
+
* root. The command-line form reads the same variable.
|
|
152
|
+
*
|
|
153
|
+
* @param {Record<string, string|undefined>} environment The process environment.
|
|
154
|
+
* @returns {string} The Claude home directory holding `skills`.
|
|
155
|
+
*/
|
|
156
|
+
export function claudeHomeFrom(environment) {
|
|
157
|
+
return environment.CLAUDE_CONFIG_DIR || join(homedir(), CLAUDE_HOME_DIRECTORY_NAME);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function main() {
|
|
161
|
+
const explicitRoot = process.argv[2];
|
|
162
|
+
const pluginRoot = explicitRoot || pstackPluginRootFor(claudeHomeFrom(process.env));
|
|
163
|
+
const outcome = refreshPstackPluginManifest(pluginRoot);
|
|
164
|
+
if (!outcome.didWrite && outcome.reason) {
|
|
165
|
+
console.log(`No manifest written: ${outcome.reason}.`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (!outcome.didWrite) {
|
|
169
|
+
console.log(`No change. ${outcome.currentSkillPaths.length} skills listed.`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
console.log(`Updated. ${outcome.currentSkillPaths.length} skills listed.`);
|
|
173
|
+
for (const eachPath of outcome.currentSkillPaths) {
|
|
174
|
+
if (!outcome.previousSkillPaths.includes(eachPath)) console.log(` added ${eachPath}`);
|
|
175
|
+
}
|
|
176
|
+
for (const eachPath of outcome.previousSkillPaths) {
|
|
177
|
+
if (!outcome.currentSkillPaths.includes(eachPath)) console.log(` removed ${eachPath}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Return whether node ran this module as its entry point.
|
|
183
|
+
*
|
|
184
|
+
* A directory link to this script gives node an invoked path that differs from
|
|
185
|
+
* this module's own path. Both sides resolve to their real location before
|
|
186
|
+
* the comparison.
|
|
187
|
+
*
|
|
188
|
+
* @param {string|undefined} invokedPath The path node reports as `argv[1]`.
|
|
189
|
+
* @returns {boolean} True when node ran this module as its entry point.
|
|
190
|
+
*/
|
|
191
|
+
function isEntryPoint(invokedPath) {
|
|
192
|
+
if (!invokedPath) return false;
|
|
193
|
+
try {
|
|
194
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(invokedPath);
|
|
195
|
+
} catch {
|
|
196
|
+
return fileURLToPath(import.meta.url) === invokedPath;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (isEntryPoint(process.argv[1])) main();
|