claude-dev-env 2.9.0 → 2.11.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 +2 -2
- package/_shared/advisor/CLAUDE.md +3 -2
- package/_shared/advisor/advisor-protocol.md +74 -108
- package/_shared/advisor/reference/advisor-block.md +37 -0
- package/_shared/advisor/reference/cli-chain.md +45 -0
- package/_shared/advisor/reference/consult-format.md +41 -0
- package/_shared/advisor/reference/lifecycle.md +21 -0
- package/_shared/advisor/reference/sol-rung.md +31 -0
- package/_shared/advisor/reference/spawn-walk-log.md +31 -0
- package/_shared/advisor/reference/third-party-bind.md +30 -0
- package/_shared/advisor/reference/warm-up.md +33 -0
- package/_shared/advisor/scripts/codex_sol_advisor.py +449 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/advisor_route_constants.py +21 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +19 -17
- package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +28 -0
- package/_shared/advisor/scripts/model_tier_run_validator.py +32 -9
- package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +474 -0
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +79 -0
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +39 -17
- package/_shared/advisor/scripts/tier_model_ids.py +24 -0
- package/_shared/pr-loop/CLAUDE.md +1 -1
- package/_shared/pr-loop/audit-contract.md +17 -6
- package/_shared/pr-loop/audit-reply-template.md +4 -4
- package/_shared/pr-loop/code-rules-gate.md +3 -5
- package/_shared/pr-loop/fix-protocol.md +2 -3
- package/_shared/pr-loop/gh-payloads.md +1 -1
- package/_shared/pr-loop/scripts/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/README.md +1 -1
- package/_shared/pr-loop/scripts/code_rules_gate.py +2 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +16 -1
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +11 -5
- package/_shared/pr-loop/scripts/preflight.py +9 -4
- package/_shared/pr-loop/scripts/reviews_disabled.py +50 -22
- package/_shared/pr-loop/scripts/tests/conftest.py +20 -0
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +6 -6
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +50 -6
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +1 -1
- package/_shared/pr-loop/state-schema.md +5 -14
- package/agents/CLAUDE.md +2 -2
- package/agents/clean-coder.md +58 -548
- package/agents/code-quality-agent.md +10 -2
- package/agents/code-verifier.md +1 -1
- package/agents/test_agent_frontmatter.py +32 -40
- package/audit-rubrics/CLAUDE.md +2 -1
- package/audit-rubrics/audit-categories.json +704 -0
- package/audit-rubrics/prompts/category-i-concurrency.md +1 -1
- package/bin/CLAUDE.md +16 -5
- package/bin/ever-shipped-skills.mjs +2 -0
- package/bin/install-plan.mjs +402 -0
- package/bin/install-transaction.mjs +455 -0
- package/bin/install.mjs +593 -147
- package/bin/install.plan.test.mjs +194 -0
- package/bin/install.profile-root.test.mjs +154 -0
- package/bin/install.profiles.test.mjs +253 -0
- package/bin/install.settings-defaults.test.mjs +200 -0
- package/bin/install.transaction.test.mjs +400 -0
- package/bin/install.uninstall-transaction.test.mjs +418 -0
- package/bin/merge_managed_permissions.mjs +130 -0
- package/bin/resolve-install-root.mjs +181 -0
- package/bin/select-install-targets.mjs +401 -0
- package/commands/CLAUDE.md +0 -2
- package/docs/references/CLAUDE.md +3 -1
- package/docs/references/advisor-tool.md +25 -7
- package/docs/references/prose-style-enforcement.md +25 -0
- package/docs/references/team-advisor-skill.md +3 -3
- package/docs/references/weak-executor-advisor.md +91 -0
- package/hooks/blocking/CLAUDE.md +6 -6
- package/hooks/blocking/_path_setup.py +9 -5
- package/hooks/blocking/code_rules_docstrings.py +124 -30
- package/hooks/blocking/code_rules_enforcer.py +161 -16
- package/hooks/blocking/code_rules_shared.py +40 -23
- package/hooks/blocking/config/CLAUDE.md +3 -5
- package/hooks/blocking/config/prose_style_enforcement_constants.py +38 -0
- package/hooks/blocking/config/test_prose_style_enforcement_constants.py +45 -0
- package/hooks/blocking/eli11_reply_enforcer.py +70 -113
- package/hooks/blocking/hedging_language_blocker.py +103 -20
- package/hooks/blocking/hook_prose_detector_consistency.py +6 -0
- package/hooks/blocking/intent_only_ending_blocker.py +6 -0
- package/hooks/blocking/plain_language_blocker.py +139 -20
- package/hooks/blocking/pre_tool_use_dispatcher.py +102 -20
- package/hooks/blocking/state_description_blocker.py +7 -1
- package/hooks/blocking/tdd_enforcer.py +8 -0
- package/hooks/blocking/test__path_setup.py +28 -0
- package/hooks/blocking/test_code_rules_enforcer_agent_home_tooling.py +99 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +232 -10
- package/hooks/blocking/test_code_rules_enforcer_ephemeral.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_join_separator_magic.py +41 -0
- package/hooks/blocking/test_code_rules_enforcer_string_magic.py +98 -0
- package/hooks/blocking/test_eli11_reply_enforcer.py +98 -165
- package/hooks/blocking/test_fable_spawn_gate.py +18 -11
- package/hooks/blocking/test_hedging_language_blocker.py +120 -1
- package/hooks/blocking/test_hook_prose_detector_consistency.py +28 -8
- package/hooks/blocking/test_intent_only_ending_blocker.py +27 -2
- package/hooks/blocking/test_package_inventory_stale_blocker.py +11 -4
- package/hooks/blocking/test_plain_language_blocker.py +129 -19
- package/hooks/blocking/test_plain_language_blocker_allowlist.py +70 -26
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +99 -26
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +87 -50
- package/hooks/blocking/test_state_description_blocker.py +45 -2
- package/hooks/blocking/test_stop_dispatcher.py +11 -7
- package/hooks/blocking/test_volatile_path_in_post_blocker.py +12 -12
- package/hooks/blocking/volatile_path_in_post_blocker.py +2 -2
- package/hooks/hooks.json +15 -0
- package/hooks/hooks_constants/CLAUDE.md +14 -3
- package/hooks/hooks_constants/ask_user_question_shape.py +281 -0
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +2 -1
- package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +5 -12
- package/hooks/hooks_constants/hedging_uncertainty_constants.py +42 -0
- package/hooks/hooks_constants/issue_tracker_session_starter_constants.py +23 -0
- package/hooks/hooks_constants/orchestrator_auto_starter_constants.py +23 -0
- package/hooks/hooks_constants/piped_pytest_blocker_constants.py +4 -1
- package/hooks/hooks_constants/plain_language_blocker_constants.py +4 -1
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +6 -0
- package/hooks/hooks_constants/project_paths_reader.py +31 -4
- package/hooks/hooks_constants/prose_matcher_precision_constants.py +40 -0
- package/hooks/hooks_constants/pytest_invocation.py +354 -0
- package/hooks/hooks_constants/session_start_injector.py +163 -0
- package/hooks/hooks_constants/session_start_injector_constants.py +46 -0
- package/hooks/hooks_constants/shell_command_pipeline.py +397 -0
- package/hooks/hooks_constants/shell_command_segments.py +5 -0
- package/hooks/hooks_constants/test_ask_user_question_shape.py +167 -0
- package/hooks/hooks_constants/test_project_paths_reader.py +29 -0
- package/hooks/hooks_constants/test_prose_metrics_parity.py +8 -0
- package/hooks/hooks_constants/test_pytest_invocation.py +130 -0
- package/hooks/hooks_constants/test_session_start_injector.py +168 -0
- package/hooks/hooks_constants/test_shell_command_pipeline.py +135 -0
- package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +1 -1
- package/hooks/hooks_constants/working_style_prompt_constants.py +30 -0
- package/hooks/observability/CLAUDE.md +2 -0
- package/hooks/observability/prose_matcher_advisory.py +237 -0
- package/hooks/observability/test_prose_matcher_advisory.py +143 -0
- package/hooks/session/CLAUDE.md +9 -1
- package/hooks/session/_path_setup.py +13 -0
- package/hooks/session/issue_tracker_session_starter.py +135 -0
- package/hooks/session/orchestrator_auto_starter.py +100 -0
- package/hooks/session/test__path_setup.py +28 -0
- package/hooks/session/test_issue_tracker_session_starter.py +104 -0
- package/hooks/session/test_orchestrator_auto_starter.py +99 -0
- package/hooks/session/test_working_style_prompt.py +47 -0
- package/hooks/session/untracked_repo_detector.py +1 -24
- package/hooks/session/working_style_prompt.py +36 -0
- package/hooks/validators/_path_setup.py +19 -0
- package/hooks/validators/run_all_validators.py +8 -13
- package/installable-surfaces.manifest.json +21 -0
- package/output-styles/CLAUDE.md +1 -3
- package/package.json +4 -2
- package/rules/CLAUDE.md +1 -0
- package/rules/durable-post-artifacts.md +2 -2
- package/rules/eli11-replies.md +6 -1
- package/rules/hedging-claims.md +4 -2
- package/rules/long-horizon-autonomy.md +3 -1
- package/rules/opus5-communication-contract.md +45 -0
- package/rules/plain-language.md +2 -2
- package/rules/research-mode.md +1 -1
- package/scripts/CLAUDE.md +11 -0
- package/scripts/Sync-RepoMain.ps1 +215 -0
- package/scripts/active_capability_references.py +218 -0
- package/scripts/ci/windows-installer-lifecycle.ps1 +78 -0
- package/scripts/claude_chain_runner.py +394 -6
- package/scripts/claude_chain_usage.py +1 -1
- package/scripts/codex_compat_materializer.py +105 -85
- package/scripts/dev_env_scripts_constants/CLAUDE.md +2 -0
- package/scripts/dev_env_scripts_constants/active_capability_constants.py +46 -0
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +74 -0
- package/scripts/dev_env_scripts_constants/verify_installable_package_constants.py +116 -0
- package/scripts/profile-isolation-launchers/config/mcp-bundles.json +25 -0
- package/scripts/profile-isolation-launchers/config/profile-isolation-constants.mjs +60 -0
- package/scripts/profile-isolation-launchers/config/profiles.manifest.json +54 -0
- package/scripts/profile-isolation-launchers/config/shared-allowlist.json +64 -0
- package/scripts/profile-isolation-launchers/launcher-runtime.mjs +180 -0
- package/scripts/profile-isolation-launchers/lib/profile-manifest.mjs +288 -0
- package/scripts/profile-isolation-launchers/mcp-bundles.mjs +275 -0
- package/scripts/profile-isolation-launchers/profile-isolation-contract.test.mjs +221 -0
- package/scripts/profile-isolation-launchers/tests/launcher-runtime.test.mjs +108 -0
- package/scripts/profile-isolation-launchers/tests/mcp-bundles.test.mjs +147 -0
- package/scripts/profile-isolation-launchers/tests/shortcut-contract.test.ps1 +102 -0
- package/scripts/profile-isolation-launchers/tests/version-compatibility.test.mjs +210 -0
- package/scripts/profile-isolation-launchers/version-compatibility.mjs +299 -0
- package/scripts/profile-isolation-launchers/windows/shortcut-inventory.ps1 +127 -0
- package/scripts/profile-isolation-launchers/windows/shortcut-manifest.json +51 -0
- package/scripts/profile-isolation-launchers/windows/shortcut-reconcile.ps1 +77 -0
- package/scripts/spawn_grok_batch.py +3 -0
- package/scripts/test_active_capability_references.py +108 -0
- package/scripts/test_claude_chain_runner.py +414 -82
- package/scripts/test_claude_chain_usage.py +12 -12
- package/scripts/test_resolve_worker_spawn.py +2 -2
- package/scripts/test_verify_installable_package.py +208 -0
- package/scripts/tests/test_codex_compat_materializer.py +33 -0
- package/scripts/verify_installable_package.py +612 -0
- package/settings.json +10 -0
- package/skills/CLAUDE.md +2 -0
- package/skills/_shared/advisor/CLAUDE.md +1 -1
- package/skills/_shared/advisor/scripts/README.md +2 -0
- package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -0
- package/skills/_shared/pr-loop/scripts/audit_category_schema.py +355 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +32 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +7 -19
- package/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +94 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +21 -0
- package/skills/autoconverge/reference/convergence.md +2 -1
- package/skills/autoconverge/reference/stop-conditions.md +5 -3
- package/skills/beat-sheet/SKILL.md +54 -0
- package/skills/beat-sheet/reference/visual-beats.md +29 -0
- package/skills/bugteam/CONSTRAINTS.md +4 -4
- package/skills/bugteam/EXAMPLES.md +1 -1
- package/skills/bugteam/reference/README.md +1 -1
- package/skills/e-code-review/SKILL.md +26 -5
- package/skills/e-code-review/reference/effort-evaluation.md +35 -0
- package/skills/e-code-review/reference/medium.md +15 -4
- package/skills/e-code-review/scripts/config/e_code_review_effort_constants/__init__.py +41 -0
- package/skills/e-code-review/scripts/config/e_code_review_effort_constants/effort_constants.py +40 -0
- package/skills/e-code-review/scripts/e_code_review_scripts_constants/finding_pipeline_constants.py +49 -0
- package/skills/e-code-review/scripts/effort_defaults_evidence.json +186 -0
- package/skills/e-code-review/scripts/effort_evaluation.py +362 -0
- package/skills/e-code-review/scripts/finding_pipeline.py +140 -0
- package/skills/e-code-review/scripts/fixtures/demanding.json +26 -0
- package/skills/e-code-review/scripts/fixtures/easy.json +14 -0
- package/skills/e-code-review/scripts/fixtures/medium.json +20 -0
- package/skills/e-code-review/scripts/grok_code_review.py +16 -7
- package/skills/e-code-review/scripts/test_effort_evaluation.py +180 -0
- package/skills/e-code-review/scripts/test_finding_pipeline.py +197 -0
- package/skills/e-code-review/scripts/test_grok_code_review.py +77 -0
- package/skills/grokify/SKILL.md +1 -1
- package/skills/grokify/templates/handoff-template.md +2 -2
- package/skills/orchestrator/SKILL.md +5 -4
- package/skills/plan-to-pr/scripts/create_packet.py +4 -4
- package/skills/plan-to-pr/scripts/load_skill_constants.py +41 -0
- package/skills/plan-to-pr/scripts/validate_packet.py +4 -4
- package/skills/plan-to-pr/scripts/validate_protocol.py +4 -1
- package/skills/plan-to-pr/scripts/validate_run.py +4 -1
- package/skills/pr-converge/scripts/check_convergence.py +21 -19
- package/skills/pr-converge/scripts/check_convergence_availability.py +50 -7
- package/skills/pr-converge/scripts/conftest.py +35 -0
- package/skills/pr-converge/scripts/test_check_convergence_availability.py +65 -0
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +11 -1
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +9 -2
- package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
- package/skills/rebase/SKILL.md +15 -3
- package/skills/reviewer-gates/SKILL.md +2 -2
- package/skills/show/SKILL.md +51 -0
- package/skills/show/references/accessibility.md +7 -0
- package/skills/show/references/art.md +3 -0
- package/skills/show/references/charts.md +3 -0
- package/skills/show/references/core-design.md +14 -0
- package/skills/show/references/erds.md +3 -0
- package/skills/show/references/flowcharts.md +3 -0
- package/skills/show/references/host-and-html.md +3 -0
- package/skills/show/references/illustrative-diagrams.md +10 -0
- package/skills/show/references/interaction.md +3 -0
- package/skills/show/references/mockups.md +3 -0
- package/skills/show/references/quality-gates.md +7 -0
- package/skills/show/references/structural-diagrams.md +3 -0
- package/skills/show/references/subject-inventory.md +21 -0
- package/skills/show/references/svg-contract.md +22 -0
- package/skills/show/routing.yaml +30 -0
- package/skills/show/samples/pr1262-v2.svg +222 -0
- package/skills/show/scripts/README.md +6 -0
- package/skills/show/scripts/validate-artifact.py +91 -0
- package/skills/show/scripts/validate-package.py +18 -0
- package/skills/show/templates/html-widget.html +4 -0
- package/skills/show/templates/svg-base.svg +19 -0
- package/skills/show/tests/fixtures/css-var.svg +6 -0
- package/skills/show/tests/fixtures/dead-ref.svg +7 -0
- package/skills/show/tests/fixtures/filled-glyph.svg +8 -0
- package/skills/show/tests/fixtures/inherited-fill.svg +18 -0
- package/skills/show/tests/fixtures/invalid.svg +1 -0
- package/skills/show/tests/fixtures/large-canvas.svg +21 -0
- package/skills/show/tests/fixtures/unfilled-connector.svg +15 -0
- package/skills/show/tests/fixtures/valid.html +1 -0
- package/skills/show/tests/test_validate-artifact.py +74 -0
- package/skills/show/tests/test_validators.py +59 -0
- package/skills/show/workflows/create-visual.md +13 -0
- package/skills/show/workflows/review-visual.md +20 -0
- package/skills/split-pr/SKILL.md +85 -0
- package/skills/split-pr/reference/path-layers.md +16 -0
- package/skills/split-pr/reference/proposal-format.md +15 -0
- package/skills/split-pr/reference/split-further-loop.md +10 -0
- package/skills/split-pr/reference/splitting-principles.md +26 -0
- package/skills/split-pr/scripts/analyze_pr.py +279 -0
- package/skills/split-pr/scripts/categorize_files.py +106 -0
- package/skills/split-pr/scripts/config/__init__.py +1 -0
- package/skills/split-pr/scripts/config/dependency_constants.py +14 -0
- package/skills/split-pr/scripts/config/git_operations_constants.py +36 -0
- package/skills/split-pr/scripts/config/packing_constants.py +61 -0
- package/skills/split-pr/scripts/config/plan_constants.py +49 -0
- package/skills/split-pr/scripts/config/split_pr_constants.py +110 -0
- package/skills/split-pr/scripts/execute_split_slices.py +82 -0
- package/skills/split-pr/scripts/pack_files_into_slices.py +212 -0
- package/skills/split-pr/scripts/split_pr_dependency_graph.py +70 -0
- package/skills/split-pr/scripts/split_pr_git_operations.py +184 -0
- package/skills/split-pr/scripts/split_pr_layer_order.py +58 -0
- package/skills/split-pr/scripts/split_pr_paginate.py +119 -0
- package/skills/split-pr/scripts/split_pr_process_runner.py +52 -0
- package/skills/split-pr/scripts/split_pr_script_types.py +126 -0
- package/skills/split-pr/scripts/split_pr_title.py +41 -0
- package/skills/split-pr/scripts/test_analyze_pr.py +228 -0
- package/skills/split-pr/scripts/test_categorize_files.py +55 -0
- package/skills/split-pr/scripts/test_categorize_files_packing.py +59 -0
- package/skills/split-pr/scripts/test_execute_split_slices.py +99 -0
- package/skills/split-pr/scripts/test_split_pr_dependency_graph.py +47 -0
- package/skills/split-pr/scripts/test_split_pr_git_operations.py +125 -0
- package/skills/split-pr/scripts/test_split_pr_layer_order.py +36 -0
- package/skills/split-pr/scripts/test_split_pr_paginate.py +65 -0
- package/skills/split-pr/scripts/test_split_pr_script_types.py +73 -0
- package/skills/split-pr/scripts/test_split_pr_title.py +28 -0
- package/skills/split-pr/scripts/test_verify_dependency_graph.py +46 -0
- package/skills/split-pr/scripts/test_verify_plan.py +56 -0
- package/skills/split-pr/scripts/test_verify_plan_contract.py +50 -0
- package/skills/split-pr/scripts/test_verify_plan_path_normalization.py +45 -0
- package/skills/split-pr/scripts/verify_dependency_graph.py +111 -0
- package/skills/split-pr/scripts/verify_plan.py +139 -0
- package/skills/team-advisor/SKILL.md +7 -4
- package/skills/team-advisor/reference/advisor-docs-review.md +207 -0
- package/system-prompts/software-engineer.xml +11 -2
- package/commands/initialize.md +0 -90
- package/commands/stubcheck.md +0 -88
- package/output-styles/caveman-agent.md +0 -37
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#Requires -Version 7.0
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
Fast-forward a local main-tracking mirror to its remote main.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Runs against any working tree that is meant to stay on one branch and follow
|
|
8
|
+
a remote, such as a read-only mirror kept current by a scheduled task.
|
|
9
|
+
|
|
10
|
+
Safety rules:
|
|
11
|
+
- Only runs when the checked-out branch is the named branch
|
|
12
|
+
- Only runs when there are no tracked changes (untracked files are fine),
|
|
13
|
+
unless -StashDirty is passed
|
|
14
|
+
- Uses merge --ff-only only (never force, never hard reset, never clean)
|
|
15
|
+
- Logs and exits non-zero on skip, diverge, or failure so a Task Scheduler
|
|
16
|
+
history row can surface a problem instead of a silent no-op
|
|
17
|
+
|
|
18
|
+
Exit codes: 0 success, 1 git operation failed, 2 configuration or repo
|
|
19
|
+
problem, 3 skipped by a safety rule, 4 diverged from the remote.
|
|
20
|
+
|
|
21
|
+
.PARAMETER RepoPath
|
|
22
|
+
Absolute path to the git working tree. Required.
|
|
23
|
+
|
|
24
|
+
.PARAMETER Remote
|
|
25
|
+
Remote name whose main is the source of truth (default: origin).
|
|
26
|
+
|
|
27
|
+
.PARAMETER Branch
|
|
28
|
+
Local branch that must be checked out (default: main).
|
|
29
|
+
|
|
30
|
+
.PARAMETER StashDirty
|
|
31
|
+
Changes the tracked-changes branch from skip to stash. With the switch, a
|
|
32
|
+
tree carrying tracked modifications is stashed with the message
|
|
33
|
+
"pre-sync stash <yyyy-MM-dd>: uncommitted other-stream edits before ff to
|
|
34
|
+
<remote>/<branch>" right before the fast-forward, and the stash is left in
|
|
35
|
+
the stash list for a human to inspect. Untracked files are never stashed.
|
|
36
|
+
The stash runs only when the fast-forward is going to happen, so an
|
|
37
|
+
already-up-to-date or diverged run creates no stash entry. If the
|
|
38
|
+
fast-forward then fails, the stash is popped back so the edits do not go
|
|
39
|
+
missing; when that pop also fails, the log names the stash entry so the
|
|
40
|
+
operator can recover it by hand.
|
|
41
|
+
|
|
42
|
+
.PARAMETER LogPath
|
|
43
|
+
Absolute path to the run log (default: $HOME\.claude\logs\sync-repo-main.log).
|
|
44
|
+
#>
|
|
45
|
+
[CmdletBinding()]
|
|
46
|
+
param(
|
|
47
|
+
[Parameter(Mandatory)]
|
|
48
|
+
[string]$RepoPath,
|
|
49
|
+
[string]$Remote = 'origin',
|
|
50
|
+
[string]$Branch = 'main',
|
|
51
|
+
[switch]$StashDirty,
|
|
52
|
+
[string]$LogPath = (Join-Path $HOME '.claude\logs\sync-repo-main.log')
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
$ErrorActionPreference = 'Stop'
|
|
56
|
+
|
|
57
|
+
$logDirectory = Split-Path -Parent $LogPath
|
|
58
|
+
if (-not (Test-Path -LiteralPath $logDirectory)) {
|
|
59
|
+
New-Item -ItemType Directory -Force -Path $logDirectory | Out-Null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function Write-SyncLog {
|
|
63
|
+
param(
|
|
64
|
+
[Parameter(Mandatory)]
|
|
65
|
+
[string]$Level,
|
|
66
|
+
[Parameter(Mandatory)]
|
|
67
|
+
[string]$Message
|
|
68
|
+
)
|
|
69
|
+
$line = '{0} [{1}] {2}' -f (Get-Date -Format 'o'), $Level, $Message
|
|
70
|
+
Add-Content -LiteralPath $LogPath -Value $line -Encoding utf8
|
|
71
|
+
Write-Host $line
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function Get-GitExecutable {
|
|
75
|
+
$command = Get-Command git -ErrorAction SilentlyContinue
|
|
76
|
+
if ($null -eq $command) {
|
|
77
|
+
throw 'git executable not found on PATH'
|
|
78
|
+
}
|
|
79
|
+
return $command.Source
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function Invoke-Git {
|
|
83
|
+
param(
|
|
84
|
+
[Parameter(Mandatory)]
|
|
85
|
+
[string[]]$Arguments
|
|
86
|
+
)
|
|
87
|
+
$output = & $script:gitPath -C $RepoPath @Arguments 2>&1
|
|
88
|
+
$exitCode = $LASTEXITCODE
|
|
89
|
+
return [pscustomobject]@{
|
|
90
|
+
ExitCode = $exitCode
|
|
91
|
+
Output = ($output | Out-String).Trim()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function Get-GitDecoration {
|
|
96
|
+
<#
|
|
97
|
+
Reads a value used only to decorate a log line. The sync has already
|
|
98
|
+
succeeded by the time these run, so a failure here returns a placeholder
|
|
99
|
+
rather than turning a completed sync into a failed run.
|
|
100
|
+
#>
|
|
101
|
+
param(
|
|
102
|
+
[Parameter(Mandatory)]
|
|
103
|
+
[string[]]$Arguments,
|
|
104
|
+
[Parameter(Mandatory)]
|
|
105
|
+
[string]$Placeholder
|
|
106
|
+
)
|
|
107
|
+
$result = Invoke-Git -Arguments $Arguments
|
|
108
|
+
if ($result.ExitCode -ne 0 -or -not $result.Output.Trim()) {
|
|
109
|
+
return $Placeholder
|
|
110
|
+
}
|
|
111
|
+
return $result.Output
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function Invoke-GitOrExit {
|
|
115
|
+
param(
|
|
116
|
+
[Parameter(Mandatory)]
|
|
117
|
+
[string[]]$Arguments,
|
|
118
|
+
[Parameter(Mandatory)]
|
|
119
|
+
[string]$FailureMessage,
|
|
120
|
+
[Parameter(Mandatory)]
|
|
121
|
+
[int]$FailureExitCode
|
|
122
|
+
)
|
|
123
|
+
$result = Invoke-Git -Arguments $Arguments
|
|
124
|
+
if ($result.ExitCode -ne 0) {
|
|
125
|
+
Write-SyncLog -Level 'ERROR' -Message "${FailureMessage}: $($result.Output)"
|
|
126
|
+
exit $FailureExitCode
|
|
127
|
+
}
|
|
128
|
+
return $result.Output
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
if (-not (Test-Path -LiteralPath $RepoPath)) {
|
|
133
|
+
Write-SyncLog -Level 'ERROR' -Message "repo path missing: $RepoPath"
|
|
134
|
+
exit 2
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
$script:gitPath = Get-GitExecutable
|
|
138
|
+
|
|
139
|
+
$currentBranch = Invoke-GitOrExit -Arguments @('branch', '--show-current') `
|
|
140
|
+
-FailureMessage 'cannot read current branch' -FailureExitCode 2
|
|
141
|
+
if ($currentBranch -ne $Branch) {
|
|
142
|
+
Write-SyncLog -Level 'SKIP' -Message "checkout is '$currentBranch', not '$Branch' (remote=$Remote)"
|
|
143
|
+
exit 3
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
$statusOutput = Invoke-GitOrExit -Arguments @('status', '--porcelain') `
|
|
147
|
+
-FailureMessage 'status failed' -FailureExitCode 2
|
|
148
|
+
$trackedDirtyLines = @($statusOutput -split "`r?`n" | Where-Object { $_.Trim() -and -not $_.StartsWith('??') })
|
|
149
|
+
if ($trackedDirtyLines.Count -gt 0 -and -not $StashDirty) {
|
|
150
|
+
Write-SyncLog -Level 'SKIP' -Message ("tracked changes present; refusing sync: " + ($trackedDirtyLines -join '; '))
|
|
151
|
+
exit 3
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
$null = Invoke-GitOrExit -Arguments @('fetch', $Remote, $Branch) `
|
|
155
|
+
-FailureMessage "fetch $Remote $Branch failed" -FailureExitCode 1
|
|
156
|
+
|
|
157
|
+
$tipOutput = Invoke-GitOrExit -Arguments @('rev-parse', $Branch, "$Remote/$Branch") `
|
|
158
|
+
-FailureMessage 'rev-parse of branch tips failed' -FailureExitCode 2
|
|
159
|
+
$tipLines = @($tipOutput -split "`r?`n" | Where-Object { $_.Trim() })
|
|
160
|
+
if ($tipLines.Count -ne 2) {
|
|
161
|
+
Write-SyncLog -Level 'ERROR' -Message "rev-parse returned $($tipLines.Count) tip(s), expected 2: $tipOutput"
|
|
162
|
+
exit 2
|
|
163
|
+
}
|
|
164
|
+
$localTip = $tipLines[0].Trim()
|
|
165
|
+
$remoteTip = $tipLines[1].Trim()
|
|
166
|
+
|
|
167
|
+
if ($localTip -eq $remoteTip) {
|
|
168
|
+
$remoteUrl = Get-GitDecoration -Arguments @('remote', 'get-url', $Remote) -Placeholder '<url unavailable>'
|
|
169
|
+
Write-SyncLog -Level 'OK' -Message "already up to date at $localTip ($Remote/$Branch $remoteUrl)"
|
|
170
|
+
exit 0
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
$ancestorResult = Invoke-Git -Arguments @('merge-base', '--is-ancestor', $Branch, "$Remote/$Branch")
|
|
174
|
+
if ($ancestorResult.ExitCode -ne 0) {
|
|
175
|
+
$countResult = Invoke-Git -Arguments @('rev-list', '--left-right', '--count', "${Branch}...${Remote}/${Branch}")
|
|
176
|
+
Write-SyncLog -Level 'ERROR' -Message "diverged from $Remote/$Branch (counts=$($countResult.Output)); fast-forward refused. local=$localTip remote=$remoteTip"
|
|
177
|
+
exit 4
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
$didStash = $false
|
|
181
|
+
if ($trackedDirtyLines.Count -gt 0) {
|
|
182
|
+
$stashMessage = 'pre-sync stash {0}: uncommitted other-stream edits before ff to {1}/{2}' -f (Get-Date -Format 'yyyy-MM-dd'), $Remote, $Branch
|
|
183
|
+
$stashResult = Invoke-Git -Arguments @('stash', 'push', '-m', $stashMessage)
|
|
184
|
+
if ($stashResult.ExitCode -ne 0) {
|
|
185
|
+
Write-SyncLog -Level 'ERROR' -Message "stash push failed; fast-forward abandoned: $($stashResult.Output)"
|
|
186
|
+
exit 1
|
|
187
|
+
}
|
|
188
|
+
$didStash = $true
|
|
189
|
+
Write-SyncLog -Level 'STASH' -Message ("stashed tracked changes as '" + $stashMessage + "' (left in stash list): " + ($trackedDirtyLines -join '; '))
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
$mergeResult = Invoke-Git -Arguments @('merge', '--ff-only', "$Remote/$Branch")
|
|
193
|
+
if ($mergeResult.ExitCode -ne 0) {
|
|
194
|
+
Write-SyncLog -Level 'ERROR' -Message "merge --ff-only failed: $($mergeResult.Output)"
|
|
195
|
+
if ($didStash) {
|
|
196
|
+
$popResult = Invoke-Git -Arguments @('stash', 'pop')
|
|
197
|
+
if ($popResult.ExitCode -eq 0) {
|
|
198
|
+
Write-SyncLog -Level 'OK' -Message "restored the stashed edits to the working tree after the failed fast-forward"
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
Write-SyncLog -Level 'ERROR' -Message ("stash pop failed after the failed fast-forward; the edits are still stashed as '" + $stashMessage + "' — recover them with git stash list and git stash pop: " + $popResult.Output)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
exit 1
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
$subject = Get-GitDecoration -Arguments @('log', '-1', '--format=%h %s') -Placeholder '<subject unavailable>'
|
|
208
|
+
$remoteUrl = Get-GitDecoration -Arguments @('remote', 'get-url', $Remote) -Placeholder '<url unavailable>'
|
|
209
|
+
Write-SyncLog -Level 'OK' -Message "$Branch $localTip -> $remoteTip | $subject | $Remote/$Branch $remoteUrl"
|
|
210
|
+
exit 0
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
Write-SyncLog -Level 'ERROR' -Message $_.Exception.Message
|
|
214
|
+
exit 1
|
|
215
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Resolve active skill, agent, command, and tool references in prompt surfaces.
|
|
3
|
+
|
|
4
|
+
Builds inventories from committed skill manifests, agent markdown files, and
|
|
5
|
+
commands. Scans active prompt text for slash and backticked capability names.
|
|
6
|
+
Fails when an active (non-inert) reference names a banned capability.
|
|
7
|
+
|
|
8
|
+
::
|
|
9
|
+
|
|
10
|
+
unresolved = unresolved_active_capabilities(package_root)
|
|
11
|
+
ok: unresolved == []
|
|
12
|
+
flag: active text mentions /qbug or `stub-detector`
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from dev_env_scripts_constants.active_capability_constants import (
|
|
22
|
+
ALL_BANNED_ACTIVE_CAPABILITY_NAMES,
|
|
23
|
+
ALL_INERT_FENCE_LANGUAGES,
|
|
24
|
+
BACKTICK_CAPABILITY_PATTERN,
|
|
25
|
+
BANNED_REASON_PREFIX,
|
|
26
|
+
FENCE_CLOSE_PATTERN,
|
|
27
|
+
FENCE_OPEN_PATTERN,
|
|
28
|
+
NEWLINE_JOIN_SEPARATOR,
|
|
29
|
+
PACKAGE_AGENTS_DIRECTORY,
|
|
30
|
+
PACKAGE_COMMANDS_DIRECTORY,
|
|
31
|
+
PACKAGE_SKILLS_DIRECTORY,
|
|
32
|
+
SKILL_MANIFEST_FILENAME,
|
|
33
|
+
SLASH_CAPABILITY_PATTERN,
|
|
34
|
+
UTF8_ENCODING,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class CapabilityInventory:
|
|
40
|
+
"""Committed capability names by kind."""
|
|
41
|
+
|
|
42
|
+
all_skill_names: frozenset[str]
|
|
43
|
+
all_agent_names: frozenset[str]
|
|
44
|
+
all_command_names: frozenset[str]
|
|
45
|
+
|
|
46
|
+
def all_known_names(self) -> frozenset[str]:
|
|
47
|
+
"""Return the union of skill, agent, and command names."""
|
|
48
|
+
return self.all_skill_names | self.all_agent_names | self.all_command_names
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class UnresolvedCapabilityReference:
|
|
53
|
+
"""One active reference that names a banned capability."""
|
|
54
|
+
|
|
55
|
+
file_path: str
|
|
56
|
+
line_number: int
|
|
57
|
+
capability_name: str
|
|
58
|
+
reason: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def build_capability_inventory(from_package_root: Path) -> CapabilityInventory:
|
|
62
|
+
"""Build inventories from committed skill/agent/command files.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
from_package_root: ``packages/claude-dev-env`` root.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Inventory of basenames present on disk.
|
|
69
|
+
"""
|
|
70
|
+
skills_root = from_package_root / PACKAGE_SKILLS_DIRECTORY
|
|
71
|
+
agents_root = from_package_root / PACKAGE_AGENTS_DIRECTORY
|
|
72
|
+
commands_root = from_package_root / PACKAGE_COMMANDS_DIRECTORY
|
|
73
|
+
all_skills: set[str] = set()
|
|
74
|
+
if skills_root.is_dir():
|
|
75
|
+
for each_skill_directory in skills_root.iterdir():
|
|
76
|
+
if each_skill_directory.is_dir() and (
|
|
77
|
+
each_skill_directory / SKILL_MANIFEST_FILENAME
|
|
78
|
+
).is_file():
|
|
79
|
+
all_skills.add(each_skill_directory.name)
|
|
80
|
+
all_agents: set[str] = set()
|
|
81
|
+
if agents_root.is_dir():
|
|
82
|
+
for each_agent_file in agents_root.glob("*.md"):
|
|
83
|
+
all_agents.add(each_agent_file.stem)
|
|
84
|
+
all_commands: set[str] = set()
|
|
85
|
+
if commands_root.is_dir():
|
|
86
|
+
for each_command_file in commands_root.glob("*.md"):
|
|
87
|
+
all_commands.add(each_command_file.stem)
|
|
88
|
+
return CapabilityInventory(
|
|
89
|
+
all_skill_names=frozenset(all_skills),
|
|
90
|
+
all_agent_names=frozenset(all_agents),
|
|
91
|
+
all_command_names=frozenset(all_commands),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def strip_inert_fenced_blocks(markdown_text: str) -> str:
|
|
96
|
+
"""Remove fenced blocks tagged as historical/example content.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
markdown_text: Full markdown source.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Text with inert fenced blocks replaced by blank lines.
|
|
103
|
+
"""
|
|
104
|
+
fence_open_pattern = re.compile(FENCE_OPEN_PATTERN)
|
|
105
|
+
fence_close_pattern = re.compile(FENCE_CLOSE_PATTERN)
|
|
106
|
+
all_lines = markdown_text.splitlines()
|
|
107
|
+
all_kept: list[str] = []
|
|
108
|
+
is_inside_inert = False
|
|
109
|
+
for each_line in all_lines:
|
|
110
|
+
open_match = fence_open_pattern.match(each_line)
|
|
111
|
+
if open_match and not is_inside_inert:
|
|
112
|
+
language = (open_match.group(1) or "").lower()
|
|
113
|
+
if language in ALL_INERT_FENCE_LANGUAGES:
|
|
114
|
+
is_inside_inert = True
|
|
115
|
+
all_kept.append("")
|
|
116
|
+
continue
|
|
117
|
+
if is_inside_inert:
|
|
118
|
+
if fence_close_pattern.match(each_line):
|
|
119
|
+
is_inside_inert = False
|
|
120
|
+
all_kept.append("")
|
|
121
|
+
continue
|
|
122
|
+
all_kept.append(each_line)
|
|
123
|
+
return NEWLINE_JOIN_SEPARATOR.join(all_kept)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def extract_active_capability_names(markdown_text: str) -> list[tuple[int, str]]:
|
|
127
|
+
"""Return (1-based line, capability name) for active references.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
markdown_text: Prompt or skill markdown.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Ordered list of line number and capability name pairs.
|
|
134
|
+
"""
|
|
135
|
+
slash_pattern = re.compile(SLASH_CAPABILITY_PATTERN)
|
|
136
|
+
backtick_pattern = re.compile(BACKTICK_CAPABILITY_PATTERN)
|
|
137
|
+
active_text = strip_inert_fenced_blocks(markdown_text)
|
|
138
|
+
all_hits: list[tuple[int, str]] = []
|
|
139
|
+
for each_line_number, each_line in enumerate(active_text.splitlines(), start=1):
|
|
140
|
+
for each_match in slash_pattern.finditer(each_line):
|
|
141
|
+
all_hits.append((each_line_number, each_match.group(1)))
|
|
142
|
+
for each_match in backtick_pattern.finditer(each_line):
|
|
143
|
+
all_hits.append((each_line_number, each_match.group(1)))
|
|
144
|
+
return all_hits
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def classify_capability_reference(capability_name: str) -> str | None:
|
|
148
|
+
"""Return a failure reason when the capability is banned.
|
|
149
|
+
|
|
150
|
+
Extractors also match ordinary prose tokens, so unknown names pass.
|
|
151
|
+
Only the ban list fails a reference.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
capability_name: Extracted skill/command-like name.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Reason string, or None when the reference is allowed.
|
|
158
|
+
"""
|
|
159
|
+
if capability_name in ALL_BANNED_ACTIVE_CAPABILITY_NAMES:
|
|
160
|
+
return f"{BANNED_REASON_PREFIX}{capability_name}"
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def unresolved_active_capabilities(
|
|
165
|
+
from_package_root: Path,
|
|
166
|
+
*,
|
|
167
|
+
all_relative_markdown_paths: list[str] | None = None,
|
|
168
|
+
) -> list[UnresolvedCapabilityReference]:
|
|
169
|
+
"""Scan package markdown for banned active capability references.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
from_package_root: ``packages/claude-dev-env`` root.
|
|
173
|
+
all_relative_markdown_paths: Optional explicit relative paths to scan;
|
|
174
|
+
defaults to all skills, agents, and commands markdown.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Banned references with file, line, name, and reason.
|
|
178
|
+
"""
|
|
179
|
+
if all_relative_markdown_paths is None:
|
|
180
|
+
all_relative_markdown_paths = _default_markdown_paths(from_package_root)
|
|
181
|
+
all_unresolved: list[UnresolvedCapabilityReference] = []
|
|
182
|
+
for each_relative_path in all_relative_markdown_paths:
|
|
183
|
+
absolute_path = from_package_root / each_relative_path
|
|
184
|
+
if not absolute_path.is_file():
|
|
185
|
+
continue
|
|
186
|
+
markdown_text = absolute_path.read_text(encoding=UTF8_ENCODING)
|
|
187
|
+
for each_line_number, each_name in extract_active_capability_names(
|
|
188
|
+
markdown_text
|
|
189
|
+
):
|
|
190
|
+
reason = classify_capability_reference(each_name)
|
|
191
|
+
if reason is None:
|
|
192
|
+
continue
|
|
193
|
+
all_unresolved.append(
|
|
194
|
+
UnresolvedCapabilityReference(
|
|
195
|
+
file_path=each_relative_path.replace("\\", "/"),
|
|
196
|
+
line_number=each_line_number,
|
|
197
|
+
capability_name=each_name,
|
|
198
|
+
reason=reason,
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
return all_unresolved
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _default_markdown_paths(from_package_root: Path) -> list[str]:
|
|
205
|
+
all_paths: list[str] = []
|
|
206
|
+
for each_directory_name in (
|
|
207
|
+
PACKAGE_SKILLS_DIRECTORY,
|
|
208
|
+
PACKAGE_AGENTS_DIRECTORY,
|
|
209
|
+
PACKAGE_COMMANDS_DIRECTORY,
|
|
210
|
+
):
|
|
211
|
+
directory_path = from_package_root / each_directory_name
|
|
212
|
+
if not directory_path.is_dir():
|
|
213
|
+
continue
|
|
214
|
+
for each_markdown_file in directory_path.rglob("*.md"):
|
|
215
|
+
all_paths.append(
|
|
216
|
+
each_markdown_file.relative_to(from_package_root).as_posix()
|
|
217
|
+
)
|
|
218
|
+
return all_paths
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Windows CI adapter for the Node installer lifecycle driver.
|
|
2
|
+
#
|
|
3
|
+
# The driver already redirects HOME, USERPROFILE, and GIT_CONFIG_GLOBAL into a
|
|
4
|
+
# throwaway sandbox. This adapter records that isolation contract, runs the
|
|
5
|
+
# 16-check driver, and writes bounded evidence under -EvidenceRoot so a failed
|
|
6
|
+
# Windows runner leaves an actionable artifact.
|
|
7
|
+
#
|
|
8
|
+
# Usage (from packages/claude-dev-env):
|
|
9
|
+
# pwsh -NoProfile -File scripts/ci/windows-installer-lifecycle.ps1 -EvidenceRoot <dir>
|
|
10
|
+
|
|
11
|
+
[CmdletBinding()]
|
|
12
|
+
param(
|
|
13
|
+
[Parameter(Mandatory = $true)]
|
|
14
|
+
[string]$EvidenceRoot
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
$ErrorActionPreference = 'Stop'
|
|
18
|
+
|
|
19
|
+
$packageRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
|
20
|
+
$driverPath = Join-Path $packageRoot '.claude\skills\run-claude-dev-env\driver.mjs'
|
|
21
|
+
$allIsolationEnvNames = @(
|
|
22
|
+
'HOME',
|
|
23
|
+
'USERPROFILE',
|
|
24
|
+
'GIT_CONFIG_GLOBAL'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if (-not (Test-Path -LiteralPath $driverPath)) {
|
|
28
|
+
throw "Lifecycle driver missing at $driverPath"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
New-Item -ItemType Directory -Force -Path $EvidenceRoot | Out-Null
|
|
32
|
+
|
|
33
|
+
$preflightPath = Join-Path $EvidenceRoot 'preflight.json'
|
|
34
|
+
$driverLogPath = Join-Path $EvidenceRoot 'driver.log'
|
|
35
|
+
$summaryPath = Join-Path $EvidenceRoot 'summary.json'
|
|
36
|
+
|
|
37
|
+
$preflight = [ordered]@{
|
|
38
|
+
packageRoot = $packageRoot
|
|
39
|
+
driverPath = $driverPath
|
|
40
|
+
platform = [System.Environment]::OSVersion.VersionString
|
|
41
|
+
nodeVersion = (& node --version 2>&1 | Out-String).Trim()
|
|
42
|
+
pythonVersion = (& python --version 2>&1 | Out-String).Trim()
|
|
43
|
+
isolationContract = $allIsolationEnvNames
|
|
44
|
+
note = 'Driver owns isolation roots; adapter never points at the real user profile.'
|
|
45
|
+
}
|
|
46
|
+
$preflight | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $preflightPath -Encoding utf8
|
|
47
|
+
|
|
48
|
+
Write-Host "Evidence root: $EvidenceRoot"
|
|
49
|
+
Write-Host "Driver: $driverPath"
|
|
50
|
+
Write-Host ("Isolation contract: {0} (driver-owned sandbox)" -f ($allIsolationEnvNames -join ', '))
|
|
51
|
+
|
|
52
|
+
$driverOutput = & node $driverPath 2>&1
|
|
53
|
+
$driverExitCode = $LASTEXITCODE
|
|
54
|
+
$driverOutput | ForEach-Object { Write-Host $_ }
|
|
55
|
+
$driverOutputText = ($driverOutput | Out-String)
|
|
56
|
+
[System.IO.File]::WriteAllText($driverLogPath, $driverOutputText)
|
|
57
|
+
|
|
58
|
+
$passedCheckMatch = [regex]::Match($driverOutputText, '(\d+)/(\d+) checks passed')
|
|
59
|
+
$summary = [ordered]@{
|
|
60
|
+
exitCode = $driverExitCode
|
|
61
|
+
allChecksPassed = ($driverExitCode -eq 0 -and $driverOutputText -match 'ALL CHECKS PASSED')
|
|
62
|
+
checksLine = if ($passedCheckMatch.Success) { $passedCheckMatch.Value } else { 'unparsed' }
|
|
63
|
+
evidenceFiles = @(
|
|
64
|
+
'preflight.json',
|
|
65
|
+
'driver.log',
|
|
66
|
+
'summary.json'
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
$summary | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $summaryPath -Encoding utf8
|
|
70
|
+
|
|
71
|
+
if ($driverExitCode -ne 0) {
|
|
72
|
+
throw "Lifecycle driver exited $driverExitCode (see $driverLogPath)"
|
|
73
|
+
}
|
|
74
|
+
if ($driverOutputText -notmatch 'ALL CHECKS PASSED') {
|
|
75
|
+
throw "Lifecycle driver output missing ALL CHECKS PASSED (see $driverLogPath)"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Write-Host "Windows installer lifecycle: OK"
|