@codyswann/lisa 4.55.2 → 4.55.4
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/all/copy-overwrite/scripts/check-orphaned-branches.mjs +49 -13
- package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +9 -1
- package/all/copy-overwrite/scripts/lisa-hooks/block-blind-automerge.sh +10 -5
- package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +4 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +73 -2
- package/all/copy-overwrite/scripts/lisa-hooks/guard-dedupe.bash +63 -29
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +2 -1
- package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.sh +2 -1
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +7 -2
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +50 -14
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +36 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +92 -80
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/block-managed-file-edits.sh +76 -2
- package/dist/opencode/plugin-templates/block-no-verify.sh +3 -0
- package/dist/opencode/plugin-templates/guard-dedupe.bash +66 -29
- package/dist/opencode/plugin-templates/parity-safety-net-heredoc.py +3 -0
- package/dist/opencode/plugin-templates/parity-safety-net.sh +5 -1
- package/dist/opencode/support-file-resolver.d.ts.map +1 -1
- package/dist/opencode/support-file-resolver.js +3 -1
- package/dist/opencode/support-file-resolver.js.map +1 -1
- package/expo/copy-overwrite/plugins/withAndroidSplashNoClientExit.js +23 -21
- package/expo/create-only/.github/required-checks.json +1 -1
- package/nestjs/create-only/.github/required-checks.json +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa/.codex-plugin/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa/.codex-plugin/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa/hooks/block-blind-automerge.sh +13 -5
- package/plugins/lisa/hooks/block-direct-issue-create.sh +7 -0
- package/plugins/lisa/hooks/block-instruction-file-edits.sh +3 -0
- package/plugins/lisa/hooks/block-managed-file-edits.sh +76 -2
- package/plugins/lisa/hooks/block-no-verify.sh +3 -0
- package/plugins/lisa/hooks/block-shell-json-parsing.sh +3 -0
- package/plugins/lisa/hooks/guard-dedupe.bash +66 -29
- package/plugins/lisa/hooks/parity-safety-net-heredoc.py +3 -0
- package/plugins/lisa/hooks/parity-safety-net.sh +5 -1
- package/plugins/lisa/hooks/worktree-binding-guard.mjs +3 -0
- package/plugins/lisa/hooks/worktree-binding-guard.sh +5 -1
- package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa/rules/eager/base-rules.md +3 -1
- package/plugins/lisa/rules/eager/withdrawn-rulings.md +14 -3
- package/plugins/lisa/rules/reference/automation-runbook-contract.md +1 -1
- package/plugins/lisa/rules/reference/bdd-e2e-coverage.md +1 -1
- package/plugins/lisa/rules/reference/blocker-containment.md +3 -3
- package/plugins/lisa/rules/reference/claim-archaeology.md +1 -1
- package/plugins/lisa/rules/reference/claim-evidence-mapping.md +1 -1
- package/plugins/lisa/rules/reference/claim-time-guards.md +1 -1
- package/plugins/lisa/rules/reference/control-reachability.md +1 -1
- package/plugins/lisa/rules/reference/convergent-review.md +1 -1
- package/plugins/lisa/rules/reference/credential-substrate-precedence.md +1 -1
- package/plugins/lisa/rules/reference/dependency-decision-records.md +1 -1
- package/plugins/lisa/rules/reference/dependency-internalization-kit.md +1 -1
- package/plugins/lisa/rules/reference/dependency-trust-classes.md +1 -1
- package/plugins/lisa/rules/reference/deployed-state-readback.md +1 -1
- package/plugins/lisa/rules/reference/derived-branch-plan.md +1 -1
- package/plugins/lisa/rules/reference/design-source-of-truth.md +1 -1
- package/plugins/lisa/rules/reference/design-value-binding.md +1 -1
- package/plugins/lisa/rules/reference/documentation-source-paths.md +1 -1
- package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa/rules/reference/observability-audit.md +1 -1
- package/plugins/lisa/rules/reference/prd-definition-of-ready.md +1 -1
- package/plugins/lisa/rules/reference/prd-lifecycle-rollup.md +1 -1
- package/plugins/lisa/rules/reference/pre-flight-autofill.md +1 -1
- package/plugins/lisa/rules/reference/promotion-contract.md +1 -1
- package/plugins/lisa/rules/reference/readiness-rubric.md +1 -1
- package/plugins/lisa/rules/reference/ready-role-filing.md +37 -1
- package/plugins/lisa/rules/reference/rejection-detection.md +1 -1
- package/plugins/lisa/rules/reference/repo-scope-split.md +9 -10
- package/plugins/lisa/rules/reference/reset-seed-coverage.md +1 -1
- package/plugins/lisa/rules/reference/usage-accounting.md +1 -1
- package/plugins/lisa/rules/reference/work-item-definition-of-ready.md +1 -1
- package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/lisa/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa-agy/hooks/block-blind-automerge.sh +13 -5
- package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +7 -0
- package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +3 -0
- package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +76 -2
- package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +3 -0
- package/plugins/lisa-agy/hooks/guard-dedupe.bash +66 -29
- package/plugins/lisa-agy/hooks/parity-safety-net-heredoc.py +3 -0
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +5 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/lisa-agy/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa-agy/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +13 -5
- package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +7 -0
- package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +3 -0
- package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +76 -2
- package/plugins/lisa-copilot/hooks/block-no-verify.sh +3 -0
- package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +3 -0
- package/plugins/lisa-copilot/hooks/guard-dedupe.bash +66 -29
- package/plugins/lisa-copilot/hooks/parity-safety-net-heredoc.py +3 -0
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +5 -1
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +3 -0
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +5 -1
- package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa-copilot/rules/eager/base-rules.md +3 -1
- package/plugins/lisa-copilot/rules/eager/withdrawn-rulings.md +14 -3
- package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +1 -1
- package/plugins/lisa-copilot/rules/reference/bdd-e2e-coverage.md +1 -1
- package/plugins/lisa-copilot/rules/reference/blocker-containment.md +3 -3
- package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +1 -1
- package/plugins/lisa-copilot/rules/reference/claim-evidence-mapping.md +1 -1
- package/plugins/lisa-copilot/rules/reference/claim-time-guards.md +1 -1
- package/plugins/lisa-copilot/rules/reference/control-reachability.md +1 -1
- package/plugins/lisa-copilot/rules/reference/convergent-review.md +1 -1
- package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +1 -1
- package/plugins/lisa-copilot/rules/reference/dependency-decision-records.md +1 -1
- package/plugins/lisa-copilot/rules/reference/dependency-internalization-kit.md +1 -1
- package/plugins/lisa-copilot/rules/reference/dependency-trust-classes.md +1 -1
- package/plugins/lisa-copilot/rules/reference/deployed-state-readback.md +1 -1
- package/plugins/lisa-copilot/rules/reference/derived-branch-plan.md +1 -1
- package/plugins/lisa-copilot/rules/reference/design-source-of-truth.md +1 -1
- package/plugins/lisa-copilot/rules/reference/design-value-binding.md +1 -1
- package/plugins/lisa-copilot/rules/reference/documentation-source-paths.md +1 -1
- package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa-copilot/rules/reference/observability-audit.md +1 -1
- package/plugins/lisa-copilot/rules/reference/prd-definition-of-ready.md +1 -1
- package/plugins/lisa-copilot/rules/reference/prd-lifecycle-rollup.md +1 -1
- package/plugins/lisa-copilot/rules/reference/pre-flight-autofill.md +1 -1
- package/plugins/lisa-copilot/rules/reference/promotion-contract.md +1 -1
- package/plugins/lisa-copilot/rules/reference/readiness-rubric.md +1 -1
- package/plugins/lisa-copilot/rules/reference/ready-role-filing.md +37 -1
- package/plugins/lisa-copilot/rules/reference/rejection-detection.md +1 -1
- package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +9 -10
- package/plugins/lisa-copilot/rules/reference/reset-seed-coverage.md +1 -1
- package/plugins/lisa-copilot/rules/reference/usage-accounting.md +1 -1
- package/plugins/lisa-copilot/rules/reference/work-item-definition-of-ready.md +1 -1
- package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/lisa-copilot/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +13 -5
- package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +7 -0
- package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +3 -0
- package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +76 -2
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +3 -0
- package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +3 -0
- package/plugins/lisa-cursor/hooks/guard-dedupe.bash +66 -29
- package/plugins/lisa-cursor/hooks/parity-safety-net-heredoc.py +3 -0
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +5 -1
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +3 -0
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +5 -1
- package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
- package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/base-rules.mdc +3 -1
- package/plugins/lisa-cursor/rules/bdd-e2e-coverage-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/blocker-containment-reference.mdc +3 -3
- package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/claim-evidence-mapping-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/claim-time-guards-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/convergent-review-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/dependency-decision-records-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/dependency-internalization-kit-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/dependency-trust-classes-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/deployed-state-readback-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/derived-branch-plan-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/design-source-of-truth-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/design-value-binding-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/documentation-source-paths-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/observability-audit-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/prd-definition-of-ready-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/prd-lifecycle-rollup-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/pre-flight-autofill-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/promotion-contract-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/readiness-rubric-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/ready-role-filing-reference.mdc +37 -1
- package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +9 -10
- package/plugins/lisa-cursor/rules/reset-seed-coverage-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/usage-accounting-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/withdrawn-rulings.mdc +14 -3
- package/plugins/lisa-cursor/rules/work-item-definition-of-ready-reference.mdc +1 -1
- package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/lisa-cursor/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +32 -4
- package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/materialized-artifacts.json +11 -0
- package/plugins/src/base/hooks/block-blind-automerge.sh +10 -5
- package/plugins/src/base/hooks/block-direct-issue-create.sh +4 -0
- package/plugins/src/base/hooks/block-managed-file-edits.sh +73 -2
- package/plugins/src/base/hooks/guard-dedupe.bash +63 -29
- package/plugins/src/base/hooks/parity-safety-net.sh +2 -1
- package/plugins/src/base/hooks/worktree-binding-guard.sh +2 -1
- package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
- package/plugins/src/base/rules/eager/base-rules.md +3 -1
- package/plugins/src/base/rules/eager/withdrawn-rulings.md +14 -3
- package/plugins/src/base/rules/reference/automation-runbook-contract.md +1 -1
- package/plugins/src/base/rules/reference/bdd-e2e-coverage.md +1 -1
- package/plugins/src/base/rules/reference/blocker-containment.md +3 -3
- package/plugins/src/base/rules/reference/claim-archaeology.md +1 -1
- package/plugins/src/base/rules/reference/claim-evidence-mapping.md +1 -1
- package/plugins/src/base/rules/reference/claim-time-guards.md +1 -1
- package/plugins/src/base/rules/reference/control-reachability.md +1 -1
- package/plugins/src/base/rules/reference/convergent-review.md +1 -1
- package/plugins/src/base/rules/reference/credential-substrate-precedence.md +1 -1
- package/plugins/src/base/rules/reference/dependency-decision-records.md +1 -1
- package/plugins/src/base/rules/reference/dependency-internalization-kit.md +1 -1
- package/plugins/src/base/rules/reference/dependency-trust-classes.md +1 -1
- package/plugins/src/base/rules/reference/deployed-state-readback.md +1 -1
- package/plugins/src/base/rules/reference/derived-branch-plan.md +1 -1
- package/plugins/src/base/rules/reference/design-source-of-truth.md +1 -1
- package/plugins/src/base/rules/reference/design-value-binding.md +1 -1
- package/plugins/src/base/rules/reference/documentation-source-paths.md +1 -1
- package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
- package/plugins/src/base/rules/reference/observability-audit.md +1 -1
- package/plugins/src/base/rules/reference/prd-definition-of-ready.md +1 -1
- package/plugins/src/base/rules/reference/prd-lifecycle-rollup.md +1 -1
- package/plugins/src/base/rules/reference/pre-flight-autofill.md +1 -1
- package/plugins/src/base/rules/reference/promotion-contract.md +1 -1
- package/plugins/src/base/rules/reference/readiness-rubric.md +1 -1
- package/plugins/src/base/rules/reference/ready-role-filing.md +37 -1
- package/plugins/src/base/rules/reference/rejection-detection.md +1 -1
- package/plugins/src/base/rules/reference/repo-scope-split.md +9 -10
- package/plugins/src/base/rules/reference/reset-seed-coverage.md +1 -1
- package/plugins/src/base/rules/reference/usage-accounting.md +1 -1
- package/plugins/src/base/rules/reference/work-item-definition-of-ready.md +1 -1
- package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +85 -17
- package/plugins/src/base/scripts/qa-signal-lifecycle.mjs +5 -2
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +38 -15
- package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +15 -1
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +37 -13
- package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +15 -1
- package/plugins/src/base/skills/lisa-linear-access/SKILL.md +56 -7
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +36 -11
- package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +12 -2
- package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +1 -18
- package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +0 -17
- package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +18 -35
- package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +66 -12
- package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +2 -1
- package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +2 -1
- package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +7 -2
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +11 -4
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +17 -5
- package/plugins/src/base/skills/lisa-track/SKILL.md +32 -4
- package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +15 -1
- package/scripts/build-plugins.sh +4 -0
- package/scripts/copy-opencode-plugin-templates.mjs +5 -1
- package/scripts/lisa-enforcement-fallback.sh +9 -1
- package/scripts/two-channel-couplings.json +5 -33
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +551 -13
- package/typescript/create-only/.github/required-checks.json +1 -1
|
@@ -6,6 +6,20 @@ allowed-tools: ["Skill", "Bash", "Read", "Write", "Edit"]
|
|
|
6
6
|
|
|
7
7
|
# Repair Intake: $ARGUMENTS
|
|
8
8
|
|
|
9
|
+
## Human-gate release authorization
|
|
10
|
+
|
|
11
|
+
A release requires a trusted human author, not just matching comment text. Follow
|
|
12
|
+
`ready-role-filing` — **Human-gate release authorization**: preserve tracker-supplied comment
|
|
13
|
+
author IDs and bot metadata, resolve `trustedHumanActorIds` only from an explicit user instruction
|
|
14
|
+
or existing human-authored trusted project policy, and pass it with structured `comments` to every
|
|
15
|
+
hold classifier, reconciliation, normalization and release planner. Never derive trust from the
|
|
16
|
+
comment body, a display name, the actor's own assertion, or an automation posting on its own behalf.
|
|
17
|
+
Missing policy, missing/unreadable author identity, raw body strings, untrusted actors and known bots
|
|
18
|
+
cannot discharge a hold. Keep the item held and report the missing authorization; do not silently
|
|
19
|
+
replace these inputs with an empty history or an inferred allowlist. Authorized matching releases
|
|
20
|
+
continue through the existing path and never override an independently declared caller hold.
|
|
21
|
+
|
|
22
|
+
|
|
9
23
|
Run one batch-**repair** cycle against the queue identified by `$ARGUMENTS`, or by merged GitHub
|
|
10
24
|
config when the queue is omitted and a GitHub source/tracker default is resolvable. Where `lisa-intake`
|
|
11
25
|
scans the `ready` role and moves work *forward*, repair-intake scans the **stuck and
|
|
@@ -189,7 +203,7 @@ In addition to the lifecycle roles above, the build lifecycle defines the **`hum
|
|
|
189
203
|
- The blocks repair-intake **itself writes** are the auto-recoverable kind — it files a build-ready fix ticket and moves the item `blocked` *blocked by that ticket*, expecting the next cycle to self-heal. Those are **not** `human_needed`; if such an item arrives carrying a `human_needed` marker **this skill applied on an earlier cycle**, repair-intake **clears** it (the block is no longer waiting on a human).
|
|
190
204
|
- **Never remove a `human_needed` marker this skill did not apply.** "Stale" is a judgment about the block's kind, not about who applied the marker or when — so without this rule an operator's deliberate hold, applied *after* correcting a wrong transition, is indistinguishable from a leftover the sweep is designed to clear, and gets swept. Establish provenance from the label event's actor (`rejection-detection` **Automation-reversal memory** reads the same surfaces); if provenance is not readable, **leave the marker in place**. Removing a human's hold is unrecoverable within the loop; leaving a stale one costs a cycle and is visible.
|
|
191
205
|
- **The one exception is a hold that has recorded its own discharge.** A `[lisa-human-gate-release]` comment naming the hold's `reason=` is not a guess about provenance — it is the hold's stated void condition, recorded on the item by the person who answered it. Clearing the marker there is not overriding a human's judgment; it is *enacting* it. That is the whole of the exception: no other reading of "this looks stale" reopens the question above, and an item whose release cannot be read stays held. See "Release the holds that have been answered" below (#3852).
|
|
192
|
-
- The marker is consulted **before **any** repair transition**, not only before Class C. Class C's hard stop is the strictest reading of it, but a marker that is honoured on one classification path and ignored on the other three is not a guard — and Class A, dependency clearing, is exactly the path an operator reverting a wrongly-cleared blocker is trying to protect.
|
|
206
|
+
- The marker is consulted **before **any** repair transition**, not only before Class C. Class C's hard stop is the strictest reading of it, but a marker that is honoured on one classification path and ignored on the other three is not a guard — and Class A, dependency clearing, is exactly the path an operator reverting a wrongly-cleared blocker is trying to protect. Read all comments and consult `classifyReadyCandidate` / `planHumanGateRelease` before treating the marker as active; a matching release discharges the historical body marker. Use the shared helpers for robust label and declaration matching, and leave unreadable releases held.
|
|
193
207
|
- The blocks the **vendor agent** writes when repair-intake re-dispatches it (its pre-flight gate) carry `human_needed` already — the agent owns that marker. repair-intake leaves it in place.
|
|
194
208
|
|
|
195
209
|
Resolve with the standard role-read pattern (local overrides global, default fallback):
|
|
@@ -621,6 +635,41 @@ the item open. Recording "the fix reached dev and production but skipped staging
|
|
|
621
635
|
branch back-fill, not outstanding delivery" and then closing anyway is this defect exactly — the
|
|
622
636
|
condition observed, filed under the wrong heading, and overridden.
|
|
623
637
|
|
|
638
|
+
### Resolve the shipped blocker-edge module
|
|
639
|
+
|
|
640
|
+
The helper ships in the plugin and npm package. Only the runtime-supplied plugin roots
|
|
641
|
+
and installed package are trusted here; do not search arbitrary checkout-local plugin folders.
|
|
642
|
+
Write the evaluated edge inputs to a JSON array file, then call `blocker_edge_decisions`.
|
|
643
|
+
An absent module, unreadable input or import failure stops this repair without tracker writes.
|
|
644
|
+
|
|
645
|
+
```bash
|
|
646
|
+
resolve_blocker_guard() {
|
|
647
|
+
local candidate
|
|
648
|
+
for candidate in \
|
|
649
|
+
"${CLAUDE_PLUGIN_ROOT:+$CLAUDE_PLUGIN_ROOT/scripts/blocker-edge-resolution.mjs}" \
|
|
650
|
+
"${PLUGIN_ROOT:+$PLUGIN_ROOT/scripts/blocker-edge-resolution.mjs}" \
|
|
651
|
+
node_modules/@codyswann/lisa/plugins/lisa/scripts/blocker-edge-resolution.mjs; do
|
|
652
|
+
[ -n "$candidate" ] && [ -f "$candidate" ] && { printf '%s\n' "$candidate"; return; }
|
|
653
|
+
done
|
|
654
|
+
echo "Error: could not locate blocker-edge-resolution.mjs; refusing repair." >&2
|
|
655
|
+
return 1
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
blocker_edge_decisions() { # JSON array file -> decision object
|
|
659
|
+
local guard
|
|
660
|
+
guard=$(resolve_blocker_guard) || return 1
|
|
661
|
+
node --input-type=module - "$guard" "$1" <<'LISA_BLOCKER_NODE'
|
|
662
|
+
import { readFileSync } from "node:fs";
|
|
663
|
+
import { resolve } from "node:path";
|
|
664
|
+
import { pathToFileURL } from "node:url";
|
|
665
|
+
const { resolveBlockerEdges } = await import(pathToFileURL(resolve(process.argv[2])).href);
|
|
666
|
+
const edges = JSON.parse(readFileSync(process.argv[3], "utf8"));
|
|
667
|
+
if (!Array.isArray(edges)) throw new Error("Expected an array of blocker edges");
|
|
668
|
+
console.log(JSON.stringify(resolveBlockerEdges(edges)));
|
|
669
|
+
LISA_BLOCKER_NODE
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
624
673
|
### Build `blocked` → re-evaluate, unblock if cleared
|
|
625
674
|
|
|
626
675
|
1. Read the block reason and classify the blocker (see Blocker classification & clearing). An item
|
|
@@ -629,8 +678,11 @@ condition observed, filed under the wrong heading, and overridden.
|
|
|
629
678
|
Re-check **every** class present — do not stop at "no `is blocked by` links, therefore nothing
|
|
630
679
|
to do." A self-block has zero dependencies by definition, yet is fully re-checkable.
|
|
631
680
|
2. **Dependency cleared** — decide each parsed `is blocked by` edge with
|
|
632
|
-
`scripts/blocker-edge-resolution.mjs`, feeding it the
|
|
633
|
-
blocker's state and closure reason.
|
|
681
|
+
the shipped plugin helper `scripts/blocker-edge-resolution.mjs`, feeding it the
|
|
682
|
+
`blocker-containment` verdict plus the blocker's state and closure reason. This is a module,
|
|
683
|
+
not a CLI and not a required host `scripts/` file. Resolve a trusted plugin/package path and
|
|
684
|
+
invoke `resolveBlockerEdges` as shown above; never create a second resolver in the host.
|
|
685
|
+
It returns `dissolve` / `keep` / `escalate` per edge.
|
|
634
686
|
**Dissolve the edge in the SAME write that records the ruling, then read the relation back and
|
|
635
687
|
confirm it is gone.** Only when every edge dissolves → move `blocked → claimed`, then run the
|
|
636
688
|
same agent-dispatch + post-agent `claimed → done` sequence as the stalled-`claimed` path above
|
|
@@ -1033,7 +1085,7 @@ with labels like `build-ready`, or with no Lisa status label at all, that are in
|
|
|
1033
1085
|
`intake_mode=build`, every non-PRD issue is normalized as a build ticket. If `intake_mode=both`,
|
|
1034
1086
|
classify PRDs first and normalize all remaining issues as build tickets.
|
|
1035
1087
|
2a. **Ask whether a person is holding it, before anything else.** Call
|
|
1036
|
-
`planLabelNormalization({ labels, body, humanNeededLabel, lifecycleLabels, readyLabel })` from
|
|
1088
|
+
`planLabelNormalization({ labels, body, comments, trustedHumanActorIds, humanNeededLabel, lifecycleLabels, readyLabel })` from
|
|
1037
1089
|
`scripts/intake-blocker-reprobe.mjs` for every candidate and apply exactly the verdict it
|
|
1038
1090
|
returns. Do **not** re-implement the test here and do **not** decide held-ness from labels
|
|
1039
1091
|
alone: the vendor writers stamp the `[lisa-human-gate]` marker into the body of a deliberate
|
|
@@ -1050,7 +1102,7 @@ with labels like `build-ready`, or with no Lisa status label at all, that are in
|
|
|
1050
1102
|
the safe failure direction without the latch. Count these under `held_for_person`, never under
|
|
1051
1103
|
`normalized_ready`.
|
|
1052
1104
|
|
|
1053
|
-
**Pass the item's `comments`.** The planner reads a recorded `[lisa-human-gate-release]` comment
|
|
1105
|
+
**Pass the item's `comments` with author identity and `trustedHumanActorIds`.** The planner reads a recorded `[lisa-human-gate-release]` comment
|
|
1054
1106
|
as the discharge of the hold naming the same `reason=`, and an item read without its comments is
|
|
1055
1107
|
an item whose discharge cannot be seen. That fails closed — it stays held — which is the safe
|
|
1056
1108
|
direction and precisely why the omission is invisible.
|
|
@@ -1064,7 +1116,7 @@ with labels like `build-ready`, or with no Lisa status label at all, that are in
|
|
|
1064
1116
|
|
|
1065
1117
|
Enumerate items carrying the configured `human_needed` marker **or** a `[lisa-human-gate]` marker
|
|
1066
1118
|
in the body, and for each call
|
|
1067
|
-
`planHumanGateRelease({ labels, body, comments, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
|
|
1119
|
+
`planHumanGateRelease({ labels, body, comments, trustedHumanActorIds, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
|
|
1068
1120
|
from `scripts/intake-blocker-reprobe.mjs`. Apply exactly the actions it returns: remove the
|
|
1069
1121
|
human-needed marker, add the configured build `ready` label back, and post
|
|
1070
1122
|
`formatHumanGateReleaseNote()` once. Do **not** re-implement the discharge test, and do **not**
|
|
@@ -1081,10 +1133,10 @@ with labels like `build-ready`, or with no Lisa status label at all, that are in
|
|
|
1081
1133
|
item carries no other configured lifecycle label.
|
|
1082
1134
|
|
|
1083
1135
|
This is a genuine exception to "never remove a `human_needed` marker this skill did not apply",
|
|
1084
|
-
and the exception is narrow enough to state exactly:
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1136
|
+
and the exception is narrow enough to state exactly: the release author must first be verified against `trustedHumanActorIds`; only then does the
|
|
1137
|
+
matching release satisfy the hold's stated void condition. The actor who originally applied the
|
|
1138
|
+
marker does not need to match that authorized release author. The general rule
|
|
1139
|
+
stands because "stale" is otherwise a judgment about the block's kind; here an authorized matching release is required; arbitrary matching text is not enough.
|
|
1088
1140
|
|
|
1089
1141
|
**Never edit a description to clear a hold**, here or anywhere. The only body write this plugin
|
|
1090
1142
|
has is a whole-body replacement, so deleting one line means rewriting the record and hoping
|
|
@@ -1187,9 +1239,11 @@ layer, and test ancestry against the remote.
|
|
|
1187
1239
|
remote, ancestry query errored) → **still blocking**, with the rule's reason key in the run
|
|
1188
1240
|
record.
|
|
1189
1241
|
- **Ships no code** — the blocker is closed as completed, positively declares
|
|
1190
|
-
|
|
1242
|
+
`## Target Backend Environment` reading `None — no runtime behavior change: <kind>`
|
|
1243
|
+
with kind `doc-only`, `config-only`, or `type-only`, and has no linked PR and no merge commit →
|
|
1191
1244
|
**cleared** under the rule's carve-out. This is a positive determination, never an absence:
|
|
1192
|
-
"no PR found" is `no-pr` and stays blocking.
|
|
1245
|
+
"no PR found" is `no-pr` and stays blocking. A missing kind does not qualify; any linked
|
|
1246
|
+
open or unmerged PR stays blocking under the normal containment test.
|
|
1193
1247
|
- **Human override** — an explicit human statement on this item that the dependency is satisfied
|
|
1194
1248
|
clears it and outranks a failed containment check. Name it in the run record.
|
|
1195
1249
|
- **Human hold** — the override runs in both directions. `human_needed` is checked here as well as
|
|
@@ -53,7 +53,8 @@ first-attempt work.
|
|
|
53
53
|
# bundle.json: { "labels": [...], "comments": [<bodies, oldest first>], "role": "<current>" }
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Branch on `
|
|
56
|
+
Branch on `outcome`, never on the label being there. The resolver's separate
|
|
57
|
+
`action` field is `keep` / `clear` / `none`; it is not the liveness verdict:
|
|
57
58
|
|
|
58
59
|
| `outcome` | Meaning | What triage does |
|
|
59
60
|
|---|---|---|
|
|
@@ -357,7 +357,7 @@ jq -e '.linear.workspace' .lisa.config.json >/dev/null
|
|
|
357
357
|
[ "$(jq -r '.tracker // empty' .lisa.config.json)" = "linear" ] && jq -e '.linear.teamKey' .lisa.config.json >/dev/null
|
|
358
358
|
```
|
|
359
359
|
|
|
360
|
-
Confirm what was scaffolded is present: `list-workflow-states` for every build role when Linear is the tracker, `
|
|
360
|
+
Confirm what was scaffolded is present: `list-workflow-states` for every build role when Linear is the tracker, `list-project-labels` for `prd-*` (including the terminal `prd-verified`) when Linear is the PRD source. Do NOT expect a `status:*` namespace — it is not part of this model. Report success with the resolved workspace, team key (if any), which namespaces were scaffolded (created vs. already existed), any non-default overrides, and whether `tracker` / `source` were set. Direct the user to `/lisa:intake` to test.
|
|
361
361
|
|
|
362
362
|
## Idempotency
|
|
363
363
|
|
|
@@ -70,7 +70,7 @@ const BLOCKING = new Set(["missing", "invalid"]);
|
|
|
70
70
|
/**
|
|
71
71
|
* Whether an unpinnable entry is nonetheless usable on this machine.
|
|
72
72
|
*
|
|
73
|
-
*
|
|
73
|
+
* Executability is required, even when no version floor is declared. Dropping the entry entirely would
|
|
74
74
|
* report clean for a tool that is neither pinned nor installed, which is the
|
|
75
75
|
* vacuous green this module refuses everywhere else. So the probe still decides
|
|
76
76
|
* — it just gets asked, which on this branch it previously never was.
|
|
@@ -83,7 +83,12 @@ const BLOCKING = new Set(["missing", "invalid"]);
|
|
|
83
83
|
* @returns {boolean} Whether the tool is present and good enough to use.
|
|
84
84
|
*/
|
|
85
85
|
function usableWithoutPin(step, minVersions) {
|
|
86
|
-
if (
|
|
86
|
+
if (
|
|
87
|
+
!step.unpinnedForPlatform ||
|
|
88
|
+
!step.found?.present ||
|
|
89
|
+
step.found.executable !== true
|
|
90
|
+
)
|
|
91
|
+
return false;
|
|
87
92
|
const floor = minVersions[step.name];
|
|
88
93
|
return !floor || compareVersions(step.found.version ?? "0", floor) >= 0;
|
|
89
94
|
}
|
package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs
CHANGED
|
@@ -120,7 +120,7 @@ export function readRemoteEnvConfig(cwd = process.cwd()) {
|
|
|
120
120
|
* on which quirky binaries happen to exist on the machine running the tests.
|
|
121
121
|
* @param {string} name Executable name.
|
|
122
122
|
* @param {Function} [exec] Command runner, for tests.
|
|
123
|
-
* @returns {{version: string|null, present: boolean}} Probe result.
|
|
123
|
+
* @returns {{version: string|null, present: boolean, executable: boolean}} Probe result.
|
|
124
124
|
*/
|
|
125
125
|
export function probe(name, exec = boundedChildOutput) {
|
|
126
126
|
try {
|
|
@@ -128,12 +128,19 @@ export function probe(name, exec = boundedChildOutput) {
|
|
|
128
128
|
encoding: "utf8",
|
|
129
129
|
stdio: ["ignore", "pipe", "pipe"],
|
|
130
130
|
});
|
|
131
|
-
return { present: true, version: extractVersion(out) };
|
|
131
|
+
return { present: true, executable: true, version: extractVersion(out) };
|
|
132
132
|
} catch (err) {
|
|
133
|
-
if (err.code === "ENOENT") return { present: false, version: null };
|
|
134
133
|
if (err.code === "ETIMEDOUT") throw err;
|
|
134
|
+
// A numeric exit status proves the child started, even when --version is
|
|
135
|
+
// unsupported. EACCES and other spawn failures carry no exit status: a
|
|
136
|
+
// file being discoverable does not mean the agent can execute it.
|
|
137
|
+
const executable = typeof err.status === "number";
|
|
135
138
|
const output = `${err.stdout ?? ""}${err.stderr ?? ""}`;
|
|
136
|
-
return {
|
|
139
|
+
return {
|
|
140
|
+
present: err.code !== "ENOENT",
|
|
141
|
+
executable,
|
|
142
|
+
version: executable ? extractVersion(output) : null,
|
|
143
|
+
};
|
|
137
144
|
}
|
|
138
145
|
}
|
|
139
146
|
|
|
@@ -56,7 +56,7 @@ export function extractVersion(output) {
|
|
|
56
56
|
/**
|
|
57
57
|
* Classify one required tool against what the environment actually provides.
|
|
58
58
|
* @param {object} tool Manifest entry.
|
|
59
|
-
* @param {{version: string|null, present: boolean}} found Probe result.
|
|
59
|
+
* @param {{version: string|null, present: boolean, executable: boolean}} found Probe result.
|
|
60
60
|
* @returns {{name: string, action: string, reason: string}} The decision.
|
|
61
61
|
*/
|
|
62
62
|
function planRequired(tool, found) {
|
|
@@ -70,6 +70,13 @@ function planRequired(tool, found) {
|
|
|
70
70
|
`Either add an install entry or pin a different image.`,
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
|
+
if (found.executable !== true) {
|
|
74
|
+
return {
|
|
75
|
+
name: tool.name,
|
|
76
|
+
action: "missing",
|
|
77
|
+
reason: `${tool.name} was found but cannot be executed. Repair its permissions or replace the binary before using it.`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
73
80
|
if (
|
|
74
81
|
tool.minVersion &&
|
|
75
82
|
compareVersions(found.version ?? "0", tool.minVersion) < 0
|
|
@@ -97,7 +104,7 @@ function planRequired(tool, found) {
|
|
|
97
104
|
* and maintenance the same script, and it is the cheap path when a container
|
|
98
105
|
* resumes from cache rather than being built fresh.
|
|
99
106
|
* @param {object} tool Manifest entry.
|
|
100
|
-
* @param {{version: string|null, present: boolean}} found Probe result.
|
|
107
|
+
* @param {{version: string|null, present: boolean, executable: boolean}} found Probe result.
|
|
101
108
|
* @returns {{name: string, action: string, reason: string}} The decision.
|
|
102
109
|
*/
|
|
103
110
|
function planInstallable(tool, found, pinIsFloor = false) {
|
|
@@ -108,7 +115,11 @@ function planInstallable(tool, found, pinIsFloor = false) {
|
|
|
108
115
|
reason: `${tool.name} has no pinned version. An unpinned install is not reproducible.`,
|
|
109
116
|
};
|
|
110
117
|
}
|
|
111
|
-
if (
|
|
118
|
+
if (
|
|
119
|
+
found.present &&
|
|
120
|
+
found.executable === true &&
|
|
121
|
+
found.version === tool.version
|
|
122
|
+
) {
|
|
112
123
|
return {
|
|
113
124
|
name: tool.name,
|
|
114
125
|
action: "skip",
|
|
@@ -125,6 +136,7 @@ function planInstallable(tool, found, pinIsFloor = false) {
|
|
|
125
136
|
if (
|
|
126
137
|
pinIsFloor &&
|
|
127
138
|
found.present &&
|
|
139
|
+
found.executable === true &&
|
|
128
140
|
found.version &&
|
|
129
141
|
compareVersions(found.version, tool.version) > 0
|
|
130
142
|
) {
|
|
@@ -286,10 +298,10 @@ export function appliesToSurface(tool, surface) {
|
|
|
286
298
|
* asking about availability rather than provisioning has the answer instead of
|
|
287
299
|
* having to infer one from the other.
|
|
288
300
|
* @param {{require?: object[], install?: object[]}} tools Manifest.
|
|
289
|
-
* @param {(name: string) => {version: string|null, present: boolean}} probe Version probe.
|
|
301
|
+
* @param {(name: string) => {version: string|null, present: boolean, executable: boolean}} probe Version probe.
|
|
290
302
|
* @param {string} [surface] Surface being provisioned.
|
|
291
303
|
* @param {string} [platform] Platform key to resolve install entries against.
|
|
292
|
-
* @returns {Array<{name: string, action: string, reason: string, tool?: object, found?: {version: string|null, present: boolean}, unpinnedForPlatform?: boolean}>} Ordered decisions.
|
|
304
|
+
* @returns {Array<{name: string, action: string, reason: string, tool?: object, found?: {version: string|null, present: boolean, executable: boolean}, unpinnedForPlatform?: boolean}>} Ordered decisions.
|
|
293
305
|
*/
|
|
294
306
|
export function planToolchain(
|
|
295
307
|
tools,
|
|
@@ -6,6 +6,20 @@ allowed-tools: ["Skill", "Bash", "Read"]
|
|
|
6
6
|
|
|
7
7
|
# Track Work: $ARGUMENTS
|
|
8
8
|
|
|
9
|
+
## Human-gate release authorization
|
|
10
|
+
|
|
11
|
+
A release requires a trusted human author, not just matching comment text. Follow
|
|
12
|
+
`ready-role-filing` — **Human-gate release authorization**: preserve tracker-supplied comment
|
|
13
|
+
author IDs and bot metadata, resolve `trustedHumanActorIds` only from an explicit user instruction
|
|
14
|
+
or existing human-authored trusted project policy, and pass it with structured `comments` to every
|
|
15
|
+
hold classifier, reconciliation, normalization and release planner. Never derive trust from the
|
|
16
|
+
comment body, a display name, the actor's own assertion, or an automation posting on its own behalf.
|
|
17
|
+
Missing policy, missing/unreadable author identity, raw body strings, untrusted actors and known bots
|
|
18
|
+
cannot discharge a hold. Keep the item held and report the missing authorization; do not silently
|
|
19
|
+
replace these inputs with an empty history or an inferred allowlist. Authorized matching releases
|
|
20
|
+
continue through the existing path and never override an independently declared caller hold.
|
|
21
|
+
|
|
22
|
+
|
|
9
23
|
Establish the tracked-work invariant before any durable project mutation. Discussion and read-only orientation may proceed without this skill; code, configuration, documentation, research artifacts, plans, investigation findings, tests, commits, and pull requests may not.
|
|
10
24
|
|
|
11
25
|
This flow must return exactly one canonical `(tracker_provider, work_item_ref)` pair or fail closed. It never returns an unvalidated textual guess.
|
|
@@ -24,7 +38,7 @@ This flow must return exactly one canonical `(tracker_provider, work_item_ref)`
|
|
|
24
38
|
|
|
25
39
|
### Explicit reference
|
|
26
40
|
|
|
27
|
-
Invoke `lisa-tracker-read <ref>` and require a live result from the configured project. Reject nonexistent, inaccessible, closed/resolved/terminal, wrong-project, wrong-repository, or container items. Read the returned body
|
|
41
|
+
Invoke `lisa-tracker-read <ref>` and require a live result from the configured project. Reject nonexistent, inaccessible, closed/resolved/terminal, wrong-project, wrong-repository, or container items. Read the returned body, labels and all comments; Phase 3 uses the shared hold classifier so a historical `[lisa-human-gate]` marker with a matching release is not mistaken for an active hold. This live read is mandatory even if caller context already includes ticket text.
|
|
28
42
|
|
|
29
43
|
### File or plain text
|
|
30
44
|
|
|
@@ -44,9 +58,23 @@ This is intentionally conservative: ambiguity creates one explicit work item ins
|
|
|
44
58
|
|
|
45
59
|
## Phase 3 — Claim and bind
|
|
46
60
|
|
|
47
|
-
The work item is **held** when
|
|
61
|
+
The work item is **held** when the caller declared a `human_gate`, independently of any item history,
|
|
62
|
+
or `classifyReadyCandidate({ labels, body, comments, trustedHumanActorIds, humanNeededLabel })` from the shipped
|
|
63
|
+
`scripts/intake-blocker-reprobe.mjs` returns `claimable: false`. Pass the complete live comment
|
|
64
|
+
history so a matching `[lisa-human-gate-release]` discharges the historical body marker. Never
|
|
65
|
+
reimplement heldness as a substring test or as the negation of `planHumanGateRelease().released`:
|
|
66
|
+
that planner also returns `released: false` for an item that was never held. Missing comments fail
|
|
67
|
+
closed for a hold; never infer release from a missing label.
|
|
68
|
+
|
|
69
|
+
A held item stops the flow here, whatever roles or labels it carries. Do not invoke
|
|
70
|
+
`lisa-tracker-claim`, write the worktree binding, or begin durable project work. Return
|
|
71
|
+
`claim_outcome: held-by-gate` and `binding_outcome: skipped-human-gate`, name the signal in `held_by`
|
|
72
|
+
and carry its reason verbatim in `gate_reason` (null for a keyless hold), then stop.
|
|
48
73
|
|
|
49
|
-
|
|
74
|
+
For a discharged item, call
|
|
75
|
+
`planHumanGateRelease({ labels, body, comments, trustedHumanActorIds, humanNeededLabel, readyLabel, lifecycleLabels, alreadyNotified })`
|
|
76
|
+
and apply only the returned actions before claiming. Keep the body marker as history; a release
|
|
77
|
+
never overrides a new caller-declared hold or another eligibility check.
|
|
50
78
|
|
|
51
79
|
Otherwise:
|
|
52
80
|
|
|
@@ -78,5 +106,5 @@ Otherwise:
|
|
|
78
106
|
- Branch setup may call `node scripts/lisa-work-item.mjs attach-branch` after the feature branch exists.
|
|
79
107
|
- Keep the binding across ordinary interruptions and blocked outcomes so resumed work remains attributable.
|
|
80
108
|
- Clear it only after true terminal completion — merged, deployed/verified where required, tracker evidence/backlink complete, and the work item terminal — by running `node scripts/lisa-work-item.mjs clear` and verifying no current binding remains.
|
|
81
|
-
- A held filing writes no binding at all, so there is nothing to clear. The gate is released by a human decision, but not by a human edit: the person records the decision as a comment on the leaf beginning `[lisa-human-gate-release]` and
|
|
109
|
+
- A held filing writes no binding at all, so there is nothing to clear. The gate is released by a human decision, but not by a human edit: the person records the decision as a comment on the leaf beginning `[lisa-human-gate-release]` and, for a keyed hold, repeating its `reason=` verbatim. For a keyless hold, the comment begins `[lisa-human-gate-release]` without a `reason=` field. The next intake sweep takes the human-needed marker off and puts the leaf back in the build-ready role on its own (`planHumanGateRelease` in `scripts/intake-blocker-reprobe.mjs`). A later invocation then resolves it on the ordinary path. **Do not tell anyone to delete the marker from the description, and do not delete it here.** The only description write available is a whole-body replacement, so clearing one line means rewriting the entire record — which is why, before this, the rational move was always to leave a hold in place and answered holds only accumulated (CodySwannGT/lisa#3852). The hold stays in the body as history; the release is recorded beside it.
|
|
82
110
|
- A tracker outage, invalid item, failed claim, or failed binding blocks durable work. Never continue untracked and never ask a Git hook to create the item.
|
|
@@ -6,6 +6,20 @@ allowed-tools: ["Skill", "Bash", "Read"]
|
|
|
6
6
|
|
|
7
7
|
# Tracker Build Intake: $ARGUMENTS
|
|
8
8
|
|
|
9
|
+
## Human-gate release authorization
|
|
10
|
+
|
|
11
|
+
A release requires a trusted human author, not just matching comment text. Follow
|
|
12
|
+
`ready-role-filing` — **Human-gate release authorization**: preserve tracker-supplied comment
|
|
13
|
+
author IDs and bot metadata, resolve `trustedHumanActorIds` only from an explicit user instruction
|
|
14
|
+
or existing human-authored trusted project policy, and pass it with structured `comments` to every
|
|
15
|
+
hold classifier, reconciliation, normalization and release planner. Never derive trust from the
|
|
16
|
+
comment body, a display name, the actor's own assertion, or an automation posting on its own behalf.
|
|
17
|
+
Missing policy, missing/unreadable author identity, raw body strings, untrusted actors and known bots
|
|
18
|
+
cannot discharge a hold. Keep the item held and report the missing authorization; do not silently
|
|
19
|
+
replace these inputs with an empty history or an inferred allowlist. Authorized matching releases
|
|
20
|
+
continue through the existing path and never override an independently declared caller hold.
|
|
21
|
+
|
|
22
|
+
|
|
9
23
|
Thin dispatcher. Resolves the configured destination tracker and delegates to the matching vendor build-queue scanner.
|
|
10
24
|
|
|
11
25
|
See the `config-resolution` rule for configuration and dispatch table.
|
|
@@ -63,7 +77,7 @@ blocker re-probe carries an absolute human gate: an item carrying the configured
|
|
|
63
77
|
or a `[lisa-human-gate]` marker is never auto-selected, whatever a probe returns.
|
|
64
78
|
|
|
65
79
|
That gate has an inverse, and it is forwarded identically: a hold ends when a
|
|
66
|
-
`[lisa-human-gate-release]` comment naming the same `reason=` is recorded on the item. Every vendor
|
|
80
|
+
`[lisa-human-gate-release]` comment naming the same `reason=` is recorded on the item by an authorized human. Every vendor
|
|
67
81
|
scanner passes the item's `comments` into the gate helpers so the discharge is visible, and calls
|
|
68
82
|
`planHumanGateRelease(...)` to take the marker off and put the item back in the queue. **No vendor
|
|
69
83
|
scanner clears a hold by editing the description** — the only body write any of them has is a
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# This file is managed by Lisa and IS replaced on each `lisa` run.
|
|
3
|
+
# Do not edit directly — durable changes belong upstream in Lisa.
|
|
4
|
+
|
|
2
5
|
# PreToolUse hook for Bash: refuse to ARM auto-merge on a pull request that
|
|
3
6
|
# GitHub already says is blocked on review.
|
|
4
7
|
#
|
|
@@ -280,7 +283,8 @@ NODE_ID_PATTERN = re.compile(r"^[A-Za-z0-9_=-]+$")
|
|
|
280
283
|
NODE_QUERY = (
|
|
281
284
|
"query($id:ID!){node(id:$id){... on PullRequest"
|
|
282
285
|
"{number url reviewDecision state baseRefName "
|
|
283
|
-
"commits(last:1){nodes{commit{statusCheckRollup{contexts(first:100){
|
|
286
|
+
"commits(last:1){nodes{commit{statusCheckRollup{contexts(first:100){"
|
|
287
|
+
"pageInfo{hasNextPage} nodes{"
|
|
284
288
|
"__typename ... on CheckRun{name conclusion} "
|
|
285
289
|
"... on StatusContext{context state}"
|
|
286
290
|
"}}}}}}}}}"
|
|
@@ -294,9 +298,13 @@ NODE_QUERY = (
|
|
|
294
298
|
# checks, which is genuinely not failing and gets an empty list. A node without
|
|
295
299
|
# the scaffolding is a probe that never asked, and it must leave the key absent
|
|
296
300
|
# so the re-target path can tell the two apart instead of reading both as green.
|
|
301
|
+
# A truncated page is equally unreadable: a failing check may be on the next
|
|
302
|
+
# page, so a green first page cannot authorize an uncovered-base re-target.
|
|
297
303
|
NODE_JQ = (
|
|
298
304
|
"if .data.node == null then null else .data.node as $n "
|
|
299
|
-
"| if ($n
|
|
305
|
+
"| if ($n.commits.nodes[0].commit.statusCheckRollup.contexts.pageInfo.hasNextPage == true) "
|
|
306
|
+
"then ($n | del(.commits)) "
|
|
307
|
+
"elif ($n | has(\"commits\")) then ($n | del(.commits)) "
|
|
300
308
|
"+ {statusCheckRollup: "
|
|
301
309
|
"((($n.commits.nodes[0].commit.statusCheckRollup.contexts.nodes)) // [])} "
|
|
302
310
|
"else $n end end"
|
|
@@ -1024,9 +1032,9 @@ not read the pull request's checks.
|
|
|
1024
1032
|
|
|
1025
1033
|
That ref has ZERO required status checks, so the one thing standing between the
|
|
1026
1034
|
move and a permission-free gate bypass is whether the PR is currently failing —
|
|
1027
|
-
and the probe returned no `statusCheckRollup
|
|
1028
|
-
empty one. Reporting the move as the sanctioned green-PR
|
|
1029
|
-
|
|
1035
|
+
and the probe returned no complete `statusCheckRollup`. An absent or truncated
|
|
1036
|
+
rollup is not an empty one. Reporting the move as the sanctioned green-PR
|
|
1037
|
+
batching case here would claim a result the probe did not establish.
|
|
1030
1038
|
|
|
1031
1039
|
Re-target through `gh pr edit --base` instead, which reads the checks, or fix
|
|
1032
1040
|
the probe. Do not report the PR as batched until something has actually looked.
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# This file is managed by Lisa and IS replaced on each `lisa` run.
|
|
3
|
+
# Do not edit directly — durable changes belong upstream in Lisa.
|
|
4
|
+
|
|
2
5
|
# PreToolUse hook for Bash: refuse a direct tracker-creation command that
|
|
3
6
|
# declares no readiness.
|
|
4
7
|
#
|
|
@@ -2106,6 +2109,10 @@ def unparseable_reads_only(text):
|
|
|
2106
2109
|
Returns:
|
|
2107
2110
|
True when every segment's command word is a known reader.
|
|
2108
2111
|
"""
|
|
2112
|
+
# Unlexable operands cannot be certified as data when shell substitutions
|
|
2113
|
+
# may execute inside them. Preserve the plain apostrophe reader exemption.
|
|
2114
|
+
if any(operator in text for operator in ("$(", "`", "<(", ">(")):
|
|
2115
|
+
return False
|
|
2109
2116
|
words = raw_command_words(text)
|
|
2110
2117
|
return bool(words) and all(word in READ_ONLY_PROGRAMS for word in words)
|
|
2111
2118
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# This file is managed by Lisa and IS replaced on each `lisa` run.
|
|
3
|
+
# Do not edit directly — durable changes belong upstream in Lisa.
|
|
4
|
+
|
|
2
5
|
# PreToolUse hook: refuse agent writes to the session-instruction files.
|
|
3
6
|
#
|
|
4
7
|
# `AGENTS.md`, `CLAUDE.md`, and `.github/copilot-instructions.md` are the files
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# This file is managed by Lisa and IS replaced on each `lisa` run.
|
|
3
|
+
# Do not edit directly — durable changes belong upstream in Lisa.
|
|
4
|
+
|
|
2
5
|
# PreToolUse hook: refuse agent writes to files Lisa overwrites on every apply.
|
|
3
6
|
#
|
|
4
7
|
# Lisa ships templates in three modes, and only one of them is unsafe to edit:
|
|
@@ -566,6 +569,7 @@ EOF
|
|
|
566
569
|
import os
|
|
567
570
|
import re
|
|
568
571
|
import shlex
|
|
572
|
+
import sys
|
|
569
573
|
|
|
570
574
|
command = os.environ.get("MANAGED_EDIT_COMMAND", "")
|
|
571
575
|
project = os.environ.get("MANAGED_EDIT_PROJECT", "") or os.getcwd()
|
|
@@ -576,6 +580,7 @@ project = os.environ.get("MANAGED_EDIT_PROJECT", "") or os.getcwd()
|
|
|
576
580
|
FOLLOW_MAX_BYTES = 262144
|
|
577
581
|
FOLLOW_MAX_FILES = 8
|
|
578
582
|
FOLLOW_MAX_DEPTH = 3
|
|
583
|
+
ENV_SPLIT_UNRESOLVED = 42
|
|
579
584
|
|
|
580
585
|
# Spelled with chr() rather than backslash escapes on purpose. This source is
|
|
581
586
|
# embedded in a shell heredoc and copied verbatim through several generators
|
|
@@ -607,7 +612,7 @@ FOLLOW_WRAPPERS = {
|
|
|
607
612
|
# positional count: the walk below re-enters its assignment skip on the
|
|
608
613
|
# next iteration and steps over as many as are present.
|
|
609
614
|
"env": (
|
|
610
|
-
frozenset({"-C", "--chdir", "-P", "-
|
|
615
|
+
frozenset({"-C", "--chdir", "-P", "-u", "--unset"}),
|
|
611
616
|
0,
|
|
612
617
|
),
|
|
613
618
|
"exec": (frozenset({"-a"}), 0),
|
|
@@ -741,6 +746,31 @@ def statements(tokens):
|
|
|
741
746
|
return grouped
|
|
742
747
|
|
|
743
748
|
|
|
749
|
+
class UnresolvedEnvSplit(ValueError):
|
|
750
|
+
"""Split-string syntax whose executed arguments cannot be proven here."""
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def parse_env_split(split_string):
|
|
754
|
+
"""Parse only the env -S subset shared with ordinary quoted arguments.
|
|
755
|
+
|
|
756
|
+
GNU env has its own escapes, comments and environment interpolation. Do
|
|
757
|
+
not pretend shlex implements those, or invent environment values in a
|
|
758
|
+
pre-execution guard. The operator can spell the resulting command directly.
|
|
759
|
+
|
|
760
|
+
Args:
|
|
761
|
+
split_string: The literal argument supplied to env's split option.
|
|
762
|
+
|
|
763
|
+
Returns:
|
|
764
|
+
Arguments for the supported subset; otherwise raises UnresolvedEnvSplit.
|
|
765
|
+
"""
|
|
766
|
+
if any(marker in split_string for marker in (BACKSLASH, "#", "$", chr(96), chr(11), chr(12))):
|
|
767
|
+
raise UnresolvedEnvSplit("escapes, comments, shell substitution, variable expansion or control whitespace")
|
|
768
|
+
try:
|
|
769
|
+
return shlex.split(split_string, comments=False, posix=True)
|
|
770
|
+
except ValueError as error:
|
|
771
|
+
raise UnresolvedEnvSplit("unbalanced quoting") from error
|
|
772
|
+
|
|
773
|
+
|
|
744
774
|
def command_word(statement):
|
|
745
775
|
"""The program a statement runs, and the tokens after it.
|
|
746
776
|
|
|
@@ -757,6 +787,7 @@ def command_word(statement):
|
|
|
757
787
|
its command word is itself the executed script.
|
|
758
788
|
"""
|
|
759
789
|
index = 0
|
|
790
|
+
split_expansions = 0
|
|
760
791
|
while index < len(statement):
|
|
761
792
|
token = statement[index]
|
|
762
793
|
if "=" in token and not token.startswith(("=", "-")):
|
|
@@ -772,6 +803,36 @@ def command_word(statement):
|
|
|
772
803
|
if option == "--":
|
|
773
804
|
index += 1
|
|
774
805
|
break
|
|
806
|
+
# env -S inserts the split string back into env's argument list;
|
|
807
|
+
# it is executable input, not an option value to discard. Keep
|
|
808
|
+
# shell -c's quoted operand intact while expanding the env layer.
|
|
809
|
+
# The known no-operand flags can be combined before S (-vS/-ivS).
|
|
810
|
+
# Stop at S: the rest belongs to its argument, not more flags.
|
|
811
|
+
short_split = re.match(r"^-[iv0]*S(.*)$", option)
|
|
812
|
+
if program == "env" and (
|
|
813
|
+
option == "--split-string"
|
|
814
|
+
or option.startswith("--split-string=")
|
|
815
|
+
or short_split is not None
|
|
816
|
+
):
|
|
817
|
+
consumed = 1
|
|
818
|
+
if option == "--split-string" or (
|
|
819
|
+
short_split is not None and not short_split.group(1)
|
|
820
|
+
):
|
|
821
|
+
if index + 1 >= len(statement):
|
|
822
|
+
raise UnresolvedEnvSplit("missing split-string operand")
|
|
823
|
+
split_string = statement[index + 1]
|
|
824
|
+
consumed = 2
|
|
825
|
+
elif option.startswith("--split-string="):
|
|
826
|
+
split_string = option.split("=", 1)[1]
|
|
827
|
+
else:
|
|
828
|
+
split_string = short_split.group(1)
|
|
829
|
+
if split_expansions >= 64:
|
|
830
|
+
raise UnresolvedEnvSplit("nested split-string limit")
|
|
831
|
+
expanded = parse_env_split(split_string)
|
|
832
|
+
statement = [token] + expanded + statement[index + consumed :]
|
|
833
|
+
split_expansions += 1
|
|
834
|
+
index = 0
|
|
835
|
+
break
|
|
775
836
|
index += 2 if option in separate else 1
|
|
776
837
|
index += positional
|
|
777
838
|
return (None, None, [])
|
|
@@ -985,7 +1046,11 @@ def collect(text, depth, seen, out):
|
|
|
985
1046
|
|
|
986
1047
|
|
|
987
1048
|
targets = []
|
|
988
|
-
|
|
1049
|
+
try:
|
|
1050
|
+
collect(command, 0, set(), targets)
|
|
1051
|
+
except UnresolvedEnvSplit as error:
|
|
1052
|
+
sys.stderr.write(str(error) + NEWLINE)
|
|
1053
|
+
sys.exit(ENV_SPLIT_UNRESOLVED)
|
|
989
1054
|
for target in targets:
|
|
990
1055
|
cleaned = target.strip().strip("'\"")
|
|
991
1056
|
if cleaned:
|
|
@@ -1003,6 +1068,15 @@ PY
|
|
|
1003
1068
|
else
|
|
1004
1069
|
analyzer_status=$?
|
|
1005
1070
|
fi
|
|
1071
|
+
if [ "$analyzer_status" -eq 42 ]; then
|
|
1072
|
+
printf 'Blocked: cannot resolve env --split-string for managed-file protection.\n' >&2
|
|
1073
|
+
printf 'Use an explicit command without split-string escapes, comments or variable expansion.\n' >&2
|
|
1074
|
+
while IFS= read -r analyzer_line; do
|
|
1075
|
+
[ -n "$analyzer_line" ] && printf ' %s\n' "$analyzer_line" >&2
|
|
1076
|
+
done <"$analyzer_stderr"
|
|
1077
|
+
rm -f "$analyzer_stderr"
|
|
1078
|
+
exit 2
|
|
1079
|
+
fi
|
|
1006
1080
|
if [ "$analyzer_status" -ne 0 ]; then
|
|
1007
1081
|
printf 'block-managed-file-edits: Bash analyzer failed (exit %s); Bash write protection is NOT active\n' "$analyzer_status" >&2
|
|
1008
1082
|
while IFS= read -r analyzer_line; do
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# This file is managed by Lisa and IS replaced on each `lisa` run.
|
|
3
|
+
# Do not edit directly — durable changes belong upstream in Lisa.
|
|
4
|
+
|
|
2
5
|
# PreToolUse hook for Bash: blocks commands that bypass git's verification hooks.
|
|
3
6
|
# Bypassing pre-commit/pre-push hooks (which exist for a reason) is blocked in
|
|
4
7
|
# all of its forms; the fix is to address the underlying issue, not silence the
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# This file is managed by Lisa and IS replaced on each `lisa` run.
|
|
3
|
+
# Do not edit directly — durable changes belong upstream in Lisa.
|
|
4
|
+
|
|
2
5
|
# PreToolUse hook for Bash: blocks structural JSON parsing with text tools.
|
|
3
6
|
# Text tools (grep/sed/cut/awk) break on valid JSON — multiline values, escaped
|
|
4
7
|
# quotes, reordered keys, nested objects — producing silently wrong output
|