@codyswann/lisa 4.49.0 → 4.50.1
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-conflict-markers.mjs +4 -1
- package/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +163 -0
- package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +178 -1
- package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +89 -10
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +44 -0
- package/all/copy-overwrite/scripts/lisa-lint-staged-preflight.mjs +1 -1
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +24 -24
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +490 -81
- package/cdk/package-lisa/package.lisa.json +45 -15
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +76 -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 +121 -227
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/lisa-block-direct-issue-create.ts +45 -1
- package/dist/opencode/plugin-templates/parity-safety-net.sh +44 -0
- package/expo/package-lisa/package.lisa.json +110 -53
- package/harper-fabric/package-lisa/package.lisa.json +62 -32
- package/nestjs/package-lisa/package.lisa.json +57 -27
- package/npm-package/package-lisa/package.lisa.json +43 -7
- package/package.json +11 -7
- package/phaser/copy-overwrite/ast-grep/rules/no-day-truncating-file-age-predicate.yml +50 -0
- package/phaser/package-lisa/package.lisa.json +62 -32
- 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-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +4 -1
- package/plugins/lisa/hooks/block-direct-issue-create.sh +89 -10
- package/plugins/lisa/hooks/discharge-work-item-gates.sh +7 -1
- package/plugins/lisa/hooks/parity-safety-net.sh +44 -0
- package/plugins/lisa/hooks/threshold-ratchet-families.mjs +12 -0
- package/plugins/lisa/rules/eager/00-rule-index.md +79 -0
- package/plugins/lisa/rules/eager/base-rules.md +10 -0
- package/plugins/lisa/rules/eager/config-resolution.md +1 -88
- package/plugins/lisa/rules/reference/automation-runbook-contract.md +97 -0
- package/plugins/lisa/rules/reference/bdd-e2e-coverage.md +56 -0
- package/plugins/lisa/rules/reference/blocker-containment.md +58 -0
- package/plugins/lisa/rules/reference/claim-archaeology.md +42 -0
- package/plugins/lisa/rules/reference/claim-evidence-mapping.md +83 -0
- package/plugins/lisa/rules/reference/claim-time-guards.md +37 -0
- package/plugins/lisa/rules/reference/config-resolution.md +97 -0
- package/plugins/lisa/rules/reference/control-reachability.md +44 -0
- package/plugins/lisa/rules/reference/convergent-review.md +31 -0
- package/plugins/lisa/rules/reference/credential-substrate-precedence.md +57 -0
- package/plugins/lisa/rules/reference/dependency-decision-records.md +37 -0
- package/plugins/lisa/rules/reference/dependency-internalization-kit.md +47 -0
- package/plugins/lisa/rules/reference/dependency-trust-classes.md +48 -0
- package/plugins/lisa/rules/reference/deployed-state-readback.md +52 -0
- package/plugins/lisa/rules/reference/derived-branch-plan.md +52 -0
- package/plugins/lisa/rules/reference/design-source-of-truth.md +43 -0
- package/plugins/lisa/rules/reference/design-value-binding.md +103 -0
- package/plugins/lisa/rules/reference/documentation-source-paths.md +18 -0
- package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +75 -0
- package/plugins/lisa/rules/reference/observability-audit.md +20 -0
- package/plugins/lisa/rules/reference/prd-definition-of-ready.md +37 -0
- package/plugins/lisa/rules/reference/prd-lifecycle-rollup.md +36 -0
- package/plugins/lisa/rules/reference/pre-flight-autofill.md +88 -0
- package/plugins/lisa/rules/reference/promotion-contract.md +21 -0
- package/plugins/lisa/rules/reference/readiness-rubric.md +63 -0
- package/plugins/lisa/rules/reference/ready-role-filing.md +34 -0
- package/plugins/lisa/rules/reference/rejection-detection.md +69 -0
- package/plugins/lisa/rules/reference/repo-scope-split.md +48 -0
- package/plugins/lisa/rules/reference/reset-seed-coverage.md +55 -0
- package/plugins/lisa/rules/reference/usage-accounting.md +38 -0
- package/plugins/lisa/rules/reference/work-item-definition-of-ready.md +32 -0
- package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +4 -1
- package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +89 -10
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +44 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +4 -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-direct-issue-create.sh +89 -10
- package/plugins/lisa-copilot/hooks/discharge-work-item-gates.sh +7 -1
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +44 -0
- package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +12 -0
- package/plugins/lisa-copilot/rules/eager/00-rule-index.md +79 -0
- package/plugins/lisa-copilot/rules/eager/base-rules.md +10 -0
- package/plugins/lisa-copilot/rules/eager/config-resolution.md +1 -88
- package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +97 -0
- package/plugins/lisa-copilot/rules/reference/bdd-e2e-coverage.md +56 -0
- package/plugins/lisa-copilot/rules/reference/blocker-containment.md +58 -0
- package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +42 -0
- package/plugins/lisa-copilot/rules/reference/claim-evidence-mapping.md +83 -0
- package/plugins/lisa-copilot/rules/reference/claim-time-guards.md +37 -0
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +97 -0
- package/plugins/lisa-copilot/rules/reference/control-reachability.md +44 -0
- package/plugins/lisa-copilot/rules/reference/convergent-review.md +31 -0
- package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +57 -0
- package/plugins/lisa-copilot/rules/reference/dependency-decision-records.md +37 -0
- package/plugins/lisa-copilot/rules/reference/dependency-internalization-kit.md +47 -0
- package/plugins/lisa-copilot/rules/reference/dependency-trust-classes.md +48 -0
- package/plugins/lisa-copilot/rules/reference/deployed-state-readback.md +52 -0
- package/plugins/lisa-copilot/rules/reference/derived-branch-plan.md +52 -0
- package/plugins/lisa-copilot/rules/reference/design-source-of-truth.md +43 -0
- package/plugins/lisa-copilot/rules/reference/design-value-binding.md +103 -0
- package/plugins/lisa-copilot/rules/reference/documentation-source-paths.md +18 -0
- package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +75 -0
- package/plugins/lisa-copilot/rules/reference/observability-audit.md +20 -0
- package/plugins/lisa-copilot/rules/reference/prd-definition-of-ready.md +37 -0
- package/plugins/lisa-copilot/rules/reference/prd-lifecycle-rollup.md +36 -0
- package/plugins/lisa-copilot/rules/reference/pre-flight-autofill.md +88 -0
- package/plugins/lisa-copilot/rules/reference/promotion-contract.md +21 -0
- package/plugins/lisa-copilot/rules/reference/readiness-rubric.md +63 -0
- package/plugins/lisa-copilot/rules/reference/ready-role-filing.md +34 -0
- package/plugins/lisa-copilot/rules/reference/rejection-detection.md +69 -0
- package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +48 -0
- package/plugins/lisa-copilot/rules/reference/reset-seed-coverage.md +55 -0
- package/plugins/lisa-copilot/rules/reference/usage-accounting.md +38 -0
- package/plugins/lisa-copilot/rules/reference/work-item-definition-of-ready.md +32 -0
- package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +4 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +89 -10
- package/plugins/lisa-cursor/hooks/discharge-work-item-gates.sh +7 -1
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +44 -0
- package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +12 -0
- package/plugins/lisa-cursor/rules/00-rule-index.mdc +84 -0
- package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +97 -0
- package/plugins/lisa-cursor/rules/base-rules.mdc +10 -0
- package/plugins/lisa-cursor/rules/bdd-e2e-coverage-reference.mdc +56 -0
- package/plugins/lisa-cursor/rules/blocker-containment-reference.mdc +58 -0
- package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +42 -0
- package/plugins/lisa-cursor/rules/claim-evidence-mapping-reference.mdc +83 -0
- package/plugins/lisa-cursor/rules/claim-time-guards-reference.mdc +37 -0
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +97 -0
- package/plugins/lisa-cursor/rules/config-resolution.mdc +1 -88
- package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +44 -0
- package/plugins/lisa-cursor/rules/convergent-review-reference.mdc +31 -0
- package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +57 -0
- package/plugins/lisa-cursor/rules/dependency-decision-records-reference.mdc +37 -0
- package/plugins/lisa-cursor/rules/dependency-internalization-kit-reference.mdc +47 -0
- package/plugins/lisa-cursor/rules/dependency-trust-classes-reference.mdc +48 -0
- package/plugins/lisa-cursor/rules/deployed-state-readback-reference.mdc +52 -0
- package/plugins/lisa-cursor/rules/derived-branch-plan-reference.mdc +52 -0
- package/plugins/lisa-cursor/rules/design-source-of-truth-reference.mdc +43 -0
- package/plugins/lisa-cursor/rules/design-value-binding-reference.mdc +103 -0
- package/plugins/lisa-cursor/rules/documentation-source-paths-reference.mdc +18 -0
- package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +75 -0
- package/plugins/lisa-cursor/rules/observability-audit-reference.mdc +20 -0
- package/plugins/lisa-cursor/rules/prd-definition-of-ready-reference.mdc +37 -0
- package/plugins/lisa-cursor/rules/prd-lifecycle-rollup-reference.mdc +36 -0
- package/plugins/lisa-cursor/rules/pre-flight-autofill-reference.mdc +88 -0
- package/plugins/lisa-cursor/rules/promotion-contract-reference.mdc +21 -0
- package/plugins/lisa-cursor/rules/readiness-rubric-reference.mdc +63 -0
- package/plugins/lisa-cursor/rules/ready-role-filing-reference.mdc +34 -0
- package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +69 -0
- package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +48 -0
- package/plugins/lisa-cursor/rules/reset-seed-coverage-reference.mdc +55 -0
- package/plugins/lisa-cursor/rules/usage-accounting-reference.mdc +38 -0
- package/plugins/lisa-cursor/rules/work-item-definition-of-ready-reference.mdc +32 -0
- package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +4 -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/src/base/hooks/block-direct-issue-create.sh +89 -10
- package/plugins/src/base/hooks/discharge-work-item-gates.sh +7 -1
- package/plugins/src/base/hooks/parity-safety-net.sh +44 -0
- package/plugins/src/base/hooks/threshold-ratchet-families.mjs +12 -0
- package/plugins/src/base/rules/eager/00-rule-index.md +79 -0
- package/plugins/src/base/rules/eager/base-rules.md +10 -0
- package/plugins/src/base/rules/eager/config-resolution.md +1 -88
- package/plugins/src/base/rules/reference/automation-runbook-contract.md +97 -0
- package/plugins/src/base/rules/reference/bdd-e2e-coverage.md +56 -0
- package/plugins/src/base/rules/reference/blocker-containment.md +58 -0
- package/plugins/src/base/rules/reference/claim-archaeology.md +42 -0
- package/plugins/src/base/rules/reference/claim-evidence-mapping.md +83 -0
- package/plugins/src/base/rules/reference/claim-time-guards.md +37 -0
- package/plugins/src/base/rules/reference/config-resolution.md +97 -0
- package/plugins/src/base/rules/reference/control-reachability.md +44 -0
- package/plugins/src/base/rules/reference/convergent-review.md +31 -0
- package/plugins/src/base/rules/reference/credential-substrate-precedence.md +57 -0
- package/plugins/src/base/rules/reference/dependency-decision-records.md +37 -0
- package/plugins/src/base/rules/reference/dependency-internalization-kit.md +47 -0
- package/plugins/src/base/rules/reference/dependency-trust-classes.md +48 -0
- package/plugins/src/base/rules/reference/deployed-state-readback.md +52 -0
- package/plugins/src/base/rules/reference/derived-branch-plan.md +52 -0
- package/plugins/src/base/rules/reference/design-source-of-truth.md +43 -0
- package/plugins/src/base/rules/reference/design-value-binding.md +103 -0
- package/plugins/src/base/rules/reference/documentation-source-paths.md +18 -0
- package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +75 -0
- package/plugins/src/base/rules/reference/observability-audit.md +20 -0
- package/plugins/src/base/rules/reference/prd-definition-of-ready.md +37 -0
- package/plugins/src/base/rules/reference/prd-lifecycle-rollup.md +36 -0
- package/plugins/src/base/rules/reference/pre-flight-autofill.md +88 -0
- package/plugins/src/base/rules/reference/promotion-contract.md +21 -0
- package/plugins/src/base/rules/reference/readiness-rubric.md +63 -0
- package/plugins/src/base/rules/reference/ready-role-filing.md +34 -0
- package/plugins/src/base/rules/reference/rejection-detection.md +69 -0
- package/plugins/src/base/rules/reference/repo-scope-split.md +48 -0
- package/plugins/src/base/rules/reference/reset-seed-coverage.md +55 -0
- package/plugins/src/base/rules/reference/usage-accounting.md +38 -0
- package/plugins/src/base/rules/reference/work-item-definition-of-ready.md +32 -0
- package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +158 -1
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +17 -1
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +4 -0
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +4 -0
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +4 -0
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +4 -1
- package/rails/copy-overwrite/ast-grep/rules/no-day-truncating-file-age-predicate.yml +50 -0
- package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +12 -0
- package/scripts/check-eager-rule-budget.mjs +176 -0
- package/scripts/check-generated-artifact-merge-coverage.mjs +7 -0
- package/scripts/check-rules-pairing.sh +45 -14
- package/scripts/check-shipped-surface-removals.mjs +851 -0
- package/scripts/generate-export-surface.mjs +318 -0
- package/scripts/lib/shipped-surface.mjs +212 -0
- package/scripts/lisa-enforcement-fallback.sh +178 -1
- package/scripts/remedy-conformance.mjs +435 -0
- package/scripts/two-channel-couplings.json +2 -2
- package/typescript/copy-contents/.husky/pre-push +147 -2
- package/typescript/copy-overwrite/ast-grep/rules/no-day-truncating-file-age-predicate.yml +50 -0
- package/typescript/copy-overwrite/audit.ignore.config.json +0 -10
- package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +180 -29
- package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +12 -0
- package/typescript/package-lisa/package.lisa.json +72 -42
- package/plugins/lisa/rules/eager/automation-runbook-contract.md +0 -92
- package/plugins/lisa/rules/eager/bdd-e2e-coverage.md +0 -51
- package/plugins/lisa/rules/eager/blocker-containment.md +0 -53
- package/plugins/lisa/rules/eager/claim-archaeology.md +0 -37
- package/plugins/lisa/rules/eager/claim-evidence-mapping.md +0 -78
- package/plugins/lisa/rules/eager/claim-time-guards.md +0 -32
- package/plugins/lisa/rules/eager/control-reachability.md +0 -39
- package/plugins/lisa/rules/eager/convergent-review.md +0 -24
- package/plugins/lisa/rules/eager/credential-substrate-precedence.md +0 -54
- package/plugins/lisa/rules/eager/dependency-decision-records.md +0 -32
- package/plugins/lisa/rules/eager/dependency-internalization-kit.md +0 -42
- package/plugins/lisa/rules/eager/dependency-trust-classes.md +0 -43
- package/plugins/lisa/rules/eager/deployed-state-readback.md +0 -47
- package/plugins/lisa/rules/eager/derived-branch-plan.md +0 -47
- package/plugins/lisa/rules/eager/design-source-of-truth.md +0 -38
- package/plugins/lisa/rules/eager/design-value-binding.md +0 -98
- package/plugins/lisa/rules/eager/documentation-source-paths.md +0 -13
- package/plugins/lisa/rules/eager/factory-model.md +0 -41
- package/plugins/lisa/rules/eager/leaf-only-lifecycle.md +0 -70
- package/plugins/lisa/rules/eager/observability-audit.md +0 -15
- package/plugins/lisa/rules/eager/prd-definition-of-ready.md +0 -32
- package/plugins/lisa/rules/eager/prd-lifecycle-rollup.md +0 -31
- package/plugins/lisa/rules/eager/pre-flight-autofill.md +0 -84
- package/plugins/lisa/rules/eager/project-learnings.md +0 -22
- package/plugins/lisa/rules/eager/promotion-contract.md +0 -18
- package/plugins/lisa/rules/eager/readiness-rubric.md +0 -58
- package/plugins/lisa/rules/eager/ready-role-filing.md +0 -29
- package/plugins/lisa/rules/eager/rejection-detection.md +0 -64
- package/plugins/lisa/rules/eager/repo-scope-split.md +0 -43
- package/plugins/lisa/rules/eager/reset-seed-coverage.md +0 -50
- package/plugins/lisa/rules/eager/usage-accounting.md +0 -33
- package/plugins/lisa/rules/eager/work-item-definition-of-ready.md +0 -27
- package/plugins/lisa-copilot/rules/eager/automation-runbook-contract.md +0 -92
- package/plugins/lisa-copilot/rules/eager/bdd-e2e-coverage.md +0 -51
- package/plugins/lisa-copilot/rules/eager/blocker-containment.md +0 -53
- package/plugins/lisa-copilot/rules/eager/claim-archaeology.md +0 -37
- package/plugins/lisa-copilot/rules/eager/claim-evidence-mapping.md +0 -78
- package/plugins/lisa-copilot/rules/eager/claim-time-guards.md +0 -32
- package/plugins/lisa-copilot/rules/eager/control-reachability.md +0 -39
- package/plugins/lisa-copilot/rules/eager/convergent-review.md +0 -24
- package/plugins/lisa-copilot/rules/eager/credential-substrate-precedence.md +0 -54
- package/plugins/lisa-copilot/rules/eager/dependency-decision-records.md +0 -32
- package/plugins/lisa-copilot/rules/eager/dependency-internalization-kit.md +0 -42
- package/plugins/lisa-copilot/rules/eager/dependency-trust-classes.md +0 -43
- package/plugins/lisa-copilot/rules/eager/deployed-state-readback.md +0 -47
- package/plugins/lisa-copilot/rules/eager/derived-branch-plan.md +0 -47
- package/plugins/lisa-copilot/rules/eager/design-source-of-truth.md +0 -38
- package/plugins/lisa-copilot/rules/eager/design-value-binding.md +0 -98
- package/plugins/lisa-copilot/rules/eager/documentation-source-paths.md +0 -13
- package/plugins/lisa-copilot/rules/eager/factory-model.md +0 -41
- package/plugins/lisa-copilot/rules/eager/leaf-only-lifecycle.md +0 -70
- package/plugins/lisa-copilot/rules/eager/observability-audit.md +0 -15
- package/plugins/lisa-copilot/rules/eager/prd-definition-of-ready.md +0 -32
- package/plugins/lisa-copilot/rules/eager/prd-lifecycle-rollup.md +0 -31
- package/plugins/lisa-copilot/rules/eager/pre-flight-autofill.md +0 -84
- package/plugins/lisa-copilot/rules/eager/project-learnings.md +0 -22
- package/plugins/lisa-copilot/rules/eager/promotion-contract.md +0 -18
- package/plugins/lisa-copilot/rules/eager/readiness-rubric.md +0 -58
- package/plugins/lisa-copilot/rules/eager/ready-role-filing.md +0 -29
- package/plugins/lisa-copilot/rules/eager/rejection-detection.md +0 -64
- package/plugins/lisa-copilot/rules/eager/repo-scope-split.md +0 -43
- package/plugins/lisa-copilot/rules/eager/reset-seed-coverage.md +0 -50
- package/plugins/lisa-copilot/rules/eager/usage-accounting.md +0 -33
- package/plugins/lisa-copilot/rules/eager/work-item-definition-of-ready.md +0 -27
- package/plugins/lisa-cursor/rules/automation-runbook-contract.mdc +0 -97
- package/plugins/lisa-cursor/rules/bdd-e2e-coverage.mdc +0 -56
- package/plugins/lisa-cursor/rules/blocker-containment.mdc +0 -58
- package/plugins/lisa-cursor/rules/claim-archaeology.mdc +0 -42
- package/plugins/lisa-cursor/rules/claim-evidence-mapping.mdc +0 -83
- package/plugins/lisa-cursor/rules/claim-time-guards.mdc +0 -37
- package/plugins/lisa-cursor/rules/control-reachability.mdc +0 -44
- package/plugins/lisa-cursor/rules/convergent-review.mdc +0 -29
- package/plugins/lisa-cursor/rules/credential-substrate-precedence.mdc +0 -59
- package/plugins/lisa-cursor/rules/dependency-decision-records.mdc +0 -37
- package/plugins/lisa-cursor/rules/dependency-internalization-kit.mdc +0 -47
- package/plugins/lisa-cursor/rules/dependency-trust-classes.mdc +0 -48
- package/plugins/lisa-cursor/rules/deployed-state-readback.mdc +0 -52
- package/plugins/lisa-cursor/rules/derived-branch-plan.mdc +0 -52
- package/plugins/lisa-cursor/rules/design-source-of-truth.mdc +0 -43
- package/plugins/lisa-cursor/rules/design-value-binding.mdc +0 -103
- package/plugins/lisa-cursor/rules/documentation-source-paths.mdc +0 -18
- package/plugins/lisa-cursor/rules/factory-model.mdc +0 -46
- package/plugins/lisa-cursor/rules/leaf-only-lifecycle.mdc +0 -75
- package/plugins/lisa-cursor/rules/observability-audit.mdc +0 -20
- package/plugins/lisa-cursor/rules/prd-definition-of-ready.mdc +0 -37
- package/plugins/lisa-cursor/rules/prd-lifecycle-rollup.mdc +0 -36
- package/plugins/lisa-cursor/rules/pre-flight-autofill.mdc +0 -89
- package/plugins/lisa-cursor/rules/project-learnings.mdc +0 -27
- package/plugins/lisa-cursor/rules/promotion-contract.mdc +0 -23
- package/plugins/lisa-cursor/rules/readiness-rubric.mdc +0 -63
- package/plugins/lisa-cursor/rules/ready-role-filing.mdc +0 -34
- package/plugins/lisa-cursor/rules/rejection-detection.mdc +0 -69
- package/plugins/lisa-cursor/rules/repo-scope-split.mdc +0 -48
- package/plugins/lisa-cursor/rules/reset-seed-coverage.mdc +0 -55
- package/plugins/lisa-cursor/rules/usage-accounting.mdc +0 -38
- package/plugins/lisa-cursor/rules/work-item-definition-of-ready.mdc +0 -32
- package/plugins/src/base/rules/eager/automation-runbook-contract.md +0 -92
- package/plugins/src/base/rules/eager/bdd-e2e-coverage.md +0 -51
- package/plugins/src/base/rules/eager/blocker-containment.md +0 -53
- package/plugins/src/base/rules/eager/claim-archaeology.md +0 -37
- package/plugins/src/base/rules/eager/claim-evidence-mapping.md +0 -78
- package/plugins/src/base/rules/eager/claim-time-guards.md +0 -32
- package/plugins/src/base/rules/eager/control-reachability.md +0 -39
- package/plugins/src/base/rules/eager/convergent-review.md +0 -24
- package/plugins/src/base/rules/eager/credential-substrate-precedence.md +0 -54
- package/plugins/src/base/rules/eager/dependency-decision-records.md +0 -32
- package/plugins/src/base/rules/eager/dependency-internalization-kit.md +0 -42
- package/plugins/src/base/rules/eager/dependency-trust-classes.md +0 -43
- package/plugins/src/base/rules/eager/deployed-state-readback.md +0 -47
- package/plugins/src/base/rules/eager/derived-branch-plan.md +0 -47
- package/plugins/src/base/rules/eager/design-source-of-truth.md +0 -38
- package/plugins/src/base/rules/eager/design-value-binding.md +0 -98
- package/plugins/src/base/rules/eager/documentation-source-paths.md +0 -13
- package/plugins/src/base/rules/eager/factory-model.md +0 -41
- package/plugins/src/base/rules/eager/leaf-only-lifecycle.md +0 -70
- package/plugins/src/base/rules/eager/observability-audit.md +0 -15
- package/plugins/src/base/rules/eager/prd-definition-of-ready.md +0 -32
- package/plugins/src/base/rules/eager/prd-lifecycle-rollup.md +0 -31
- package/plugins/src/base/rules/eager/pre-flight-autofill.md +0 -84
- package/plugins/src/base/rules/eager/project-learnings.md +0 -22
- package/plugins/src/base/rules/eager/promotion-contract.md +0 -18
- package/plugins/src/base/rules/eager/readiness-rubric.md +0 -58
- package/plugins/src/base/rules/eager/ready-role-filing.md +0 -29
- package/plugins/src/base/rules/eager/rejection-detection.md +0 -64
- package/plugins/src/base/rules/eager/repo-scope-split.md +0 -43
- package/plugins/src/base/rules/eager/reset-seed-coverage.md +0 -50
- package/plugins/src/base/rules/eager/usage-accounting.md +0 -33
- package/plugins/src/base/rules/eager/work-item-definition-of-ready.md +0 -27
|
@@ -446,8 +446,11 @@ export function parseArgs(argv) {
|
|
|
446
446
|
if (arg === "--json") {
|
|
447
447
|
json = true;
|
|
448
448
|
} else if (arg === "--root") {
|
|
449
|
+
if (i + 1 >= argv.length) {
|
|
450
|
+
throw new UsageError("--root requires a value");
|
|
451
|
+
}
|
|
449
452
|
const next = argv[i + 1];
|
|
450
|
-
if (next
|
|
453
|
+
if (next.startsWith("--")) {
|
|
451
454
|
throw new UsageError("--root requires a value");
|
|
452
455
|
}
|
|
453
456
|
root = next;
|
|
@@ -358,6 +358,169 @@ function noTestsVerdict(output) {
|
|
|
358
358
|
};
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
+
/**
|
|
362
|
+
* What a tool prints when it exited SUCCESSFULLY having done nothing.
|
|
363
|
+
*
|
|
364
|
+
* `states` is the tool's own positive statement of emptiness; `contradicted`
|
|
365
|
+
* is the line that same tool prints whenever it DID reach a verdict. Both are
|
|
366
|
+
* required. The second is what stops a parent transcript that merely quotes a
|
|
367
|
+
* child's empty lane from being read as empty itself — the same guard, and the
|
|
368
|
+
* same known limit, as the failure path's use of {@link SUMMARY_PATTERN}.
|
|
369
|
+
*
|
|
370
|
+
* A table rather than two conditionals, because the defect is not vitest's. It
|
|
371
|
+
* is "exit 0 attests to work that never happened", and the extension point is
|
|
372
|
+
* one row per tool that can say so. A tool which prints NO count at all —
|
|
373
|
+
* `tsc`, whose silent exit 0 says nothing about how much it compiled — cannot
|
|
374
|
+
* be given a row until it is first made to state what it compiled.
|
|
375
|
+
* CodySwannGT/lisa#3811 is the work that makes it state one.
|
|
376
|
+
*
|
|
377
|
+
* **A row there does NOT close #3811**, and reading it that way would be a
|
|
378
|
+
* costly mistake. That gate compiles 1,210 files, none of them under `tests/`:
|
|
379
|
+
* a WRONG NON-ZERO, not an emptiness. No row keyed on a positive statement can
|
|
380
|
+
* reach it, and the only row that could is one keyed on "I saw no test path in
|
|
381
|
+
* the file list" — the absence-inference this table refuses on principle, since
|
|
382
|
+
* it cannot tell "nothing was compiled" from "I did not look". The row and that
|
|
383
|
+
* ticket's config fix are complementary, not sequential.
|
|
384
|
+
*
|
|
385
|
+
* Both rows are measured, not recalled: vitest 4.1.9 and jest, each run with
|
|
386
|
+
* `--passWithNoTests` against a directory holding no tests.
|
|
387
|
+
*/
|
|
388
|
+
const ZERO_WORK_SIGNATURES = Object.freeze([
|
|
389
|
+
Object.freeze({
|
|
390
|
+
contradicted: SUMMARY_PATTERN,
|
|
391
|
+
// The same pattern the failure path reads, deliberately, so a wording
|
|
392
|
+
// change in the tool breaks BOTH readings visibly instead of silently
|
|
393
|
+
// retiring this one. A tighter pattern keyed on the `, exiting with code
|
|
394
|
+
// 0` suffix would be more precise and would go quietly inert the day that
|
|
395
|
+
// suffix moves — which is the defect class this whole file is about.
|
|
396
|
+
states: NO_TESTS_PATTERN,
|
|
397
|
+
tool: "vitest",
|
|
398
|
+
}),
|
|
399
|
+
Object.freeze({
|
|
400
|
+
/** jest's tally line: `Tests: 3 passed, 3 total`. */
|
|
401
|
+
contradicted: /^[ \t]*Tests:[ \t]/m,
|
|
402
|
+
/** jest's own line: `No tests found, exiting with code 0`. */
|
|
403
|
+
states: /^[ \t]*No tests found/m,
|
|
404
|
+
tool: "jest",
|
|
405
|
+
}),
|
|
406
|
+
]);
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* The root a runner names for itself: ` RUN v4.1.9 /path/to/root`.
|
|
410
|
+
*
|
|
411
|
+
* The capture takes the rest of the line rather than the next run of non-space,
|
|
412
|
+
* because a checkout path may contain spaces and the truncated answer is worse
|
|
413
|
+
* than no answer: `/repo/consumer project` reported as `/repo/consumer` names a
|
|
414
|
+
* tree that exists, so the operator checks the wrong one and the line that was
|
|
415
|
+
* added to end that guessing causes it instead.
|
|
416
|
+
*
|
|
417
|
+
* The capture is one greedy negated class and the trailing whitespace comes off
|
|
418
|
+
* in `rootLine` rather than in the pattern. A lazy `(.+?)` followed by `[ \t]*$`
|
|
419
|
+
* expresses the same intent and is a super-linear backtracking hazard, which
|
|
420
|
+
* the shipped ruleset rejects outright (`sonarjs/slow-regex`) — this module
|
|
421
|
+
* reads gate transcripts, which are exactly the untrusted, arbitrarily long
|
|
422
|
+
* input that makes that rule worth obeying.
|
|
423
|
+
*/
|
|
424
|
+
const RUN_ROOT_PATTERN = /^[ \t]*RUN[ \t]+v\S+[ \t]+([^\r\n]+)/m;
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Where this run happened — always answered, never omitted.
|
|
428
|
+
*
|
|
429
|
+
* The question an operator asks first of an empty run is WHICH TREE it ran in,
|
|
430
|
+
* and several agents lost time inferring it from an absent file count
|
|
431
|
+
* (CodySwannGT/lisa#3715, direction 3).
|
|
432
|
+
*
|
|
433
|
+
* Two sources, and the line says which one it used. The tool's own header is
|
|
434
|
+
* preferred because it is the tool's account of itself; the gate's working
|
|
435
|
+
* directory is the fallback because it is a fact the runner always has. An
|
|
436
|
+
* earlier version quoted ONLY the header and fell silent when it was absent —
|
|
437
|
+
* measured on CI, where the header did not appear in the captured transcript
|
|
438
|
+
* and jest never prints one at all. A direction that reads "make the run state
|
|
439
|
+
* its root" is not satisfied by a line that appears when the tool feels like
|
|
440
|
+
* it; that is the same defect one field over.
|
|
441
|
+
* @param {string} output The command's combined output.
|
|
442
|
+
* @param {string} cwd The directory the gate command ran in.
|
|
443
|
+
* @returns {string} An evidence line naming the root and its source.
|
|
444
|
+
*/
|
|
445
|
+
function rootLine(output, cwd) {
|
|
446
|
+
const match = RUN_ROOT_PATTERN.exec(output);
|
|
447
|
+
return match
|
|
448
|
+
? `the run's root was ${match[1].trimEnd()}, as the tool reported it`
|
|
449
|
+
: `the run's root was ${cwd}, the directory the gate ran in — the tool printed none`;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* What an operator does about a gate that collected nothing.
|
|
454
|
+
*
|
|
455
|
+
* Two remedies and no third, because there are exactly two true situations: the
|
|
456
|
+
* suite exists and the invocation missed it, or the suite does not exist and
|
|
457
|
+
* the gate should not have been declared. `--passWithNoTests` is named outright
|
|
458
|
+
* because it is the flag that converts the second into a green report, and it
|
|
459
|
+
* ships in the integration script of several stack templates for a real reason
|
|
460
|
+
* — a project may genuinely have no integration tests yet. That reason is an
|
|
461
|
+
* argument for declaring the gate `off`, never for a gate reporting PASSED.
|
|
462
|
+
*/
|
|
463
|
+
const ZERO_WORK_REMEDY =
|
|
464
|
+
"declaring this gate means declaring that work exists — either point the " +
|
|
465
|
+
"command at the suite it was meant to run, or declare the gate `off` for " +
|
|
466
|
+
"this moment; `--passWithNoTests` on a gate command turns a missing suite " +
|
|
467
|
+
"into a green report";
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* The verdict for a command that exited 0 while stating it did no work.
|
|
471
|
+
*
|
|
472
|
+
* The success branch is where this defect lives, and it is the one branch the
|
|
473
|
+
* zero-collection diagnosis could not reach: `execute()` returned PASSED on
|
|
474
|
+
* `code === 0` without ever reading the output, so every pattern in this file
|
|
475
|
+
* — including {@link noTestsVerdict}, written for exactly this event — was
|
|
476
|
+
* unreachable on the path that needed it (CodySwannGT/lisa#3715).
|
|
477
|
+
*
|
|
478
|
+
* A separate entry point from {@link diagnoseFailure} rather than a widening of
|
|
479
|
+
* it, because the two are asked different questions. `diagnoseFailure` is asked
|
|
480
|
+
* "what went wrong", and always has an answer. This is asked "did anything
|
|
481
|
+
* happen at all", and its ordinary answer is null: silence means the command
|
|
482
|
+
* said nothing about a count, which stays PASSED and keeps the blast radius off
|
|
483
|
+
* every non-test gate.
|
|
484
|
+
* @param {string|null|undefined} output The command's combined output, or null
|
|
485
|
+
* when the runner could not capture it. An uncaptured success stays a
|
|
486
|
+
* success — capture is on by default, and its loss is not evidence.
|
|
487
|
+
* @param {string} [cwd] The directory the gate command ran in, quoted when the
|
|
488
|
+
* tool printed no root of its own. Defaults to this process's; injected by
|
|
489
|
+
* tests so a fixture needs no directory on disk.
|
|
490
|
+
* @returns {Diagnosis|null} The verdict, or null when the output carries no
|
|
491
|
+
* positive statement that nothing ran.
|
|
492
|
+
*/
|
|
493
|
+
export function diagnoseHollowSuccess(output, cwd = process.cwd()) {
|
|
494
|
+
// A type-contract check at the module boundary, and deliberately the only
|
|
495
|
+
// guard here: an EMPTY transcript states nothing, so the search below
|
|
496
|
+
// already answers null for it and a second check on its length would be a
|
|
497
|
+
// branch no test could ever kill — a poor thing to ship in the file whose
|
|
498
|
+
// subject is controls that cannot fire. This one is kept anyway, and is
|
|
499
|
+
// itself an equivalent mutant under the caller's contract: `normaliseExec`
|
|
500
|
+
// hands over `string | null`, and a null would fall through the search too,
|
|
501
|
+
// by way of `RegExp.test` coercing it to the characters "null". Relying on
|
|
502
|
+
// that coincidence is worse than stating the type.
|
|
503
|
+
if (typeof output !== "string") return null;
|
|
504
|
+
const empty = ZERO_WORK_SIGNATURES.find(
|
|
505
|
+
signature =>
|
|
506
|
+
signature.states.test(output) && !signature.contradicted.test(output)
|
|
507
|
+
);
|
|
508
|
+
if (!empty) return null;
|
|
509
|
+
const stated = output
|
|
510
|
+
.split("\n")
|
|
511
|
+
.map(line => line.trim())
|
|
512
|
+
.filter(line => empty.states.test(line));
|
|
513
|
+
return {
|
|
514
|
+
evidence: capped([...stated, rootLine(output, cwd), ZERO_WORK_REMEDY]),
|
|
515
|
+
kind: DIAGNOSIS.NO_TESTS_RAN,
|
|
516
|
+
proves: ATTRIBUTION[DIAGNOSIS.NO_TESTS_RAN] ?? null,
|
|
517
|
+
summary:
|
|
518
|
+
`the command exited 0 and ${empty.tool} said it collected ZERO test ` +
|
|
519
|
+
"files, so this gate is attesting to work that never happened — exit 0 " +
|
|
520
|
+
"here reports that nothing ran, NOT that anything passed",
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
|
|
361
524
|
/**
|
|
362
525
|
* Which gate's property each kind of failure actually belongs to.
|
|
363
526
|
*
|
|
@@ -276,6 +276,178 @@ resolve_vintages() {
|
|
|
276
276
|
plugin_tree_version="$json_version"
|
|
277
277
|
fi
|
|
278
278
|
note_version "$plugin_tree_version" "$plugin_tree"
|
|
279
|
+
|
|
280
|
+
# The channel this dispatcher races. Deliberately NOT folded into
|
|
281
|
+
# `note_version`: the newest-on-disk maximum answers "is this copy behind
|
|
282
|
+
# something local", and the plugin channel's age is a separate question
|
|
283
|
+
# about a copy that runs in parallel rather than instead.
|
|
284
|
+
resolve_plugin_channel || true
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# ---------------------------------------------------------------------------
|
|
288
|
+
# The other channel
|
|
289
|
+
#
|
|
290
|
+
# YOU CANNOT RETIRE A REFUSAL BY SHIPPING A FIX.
|
|
291
|
+
#
|
|
292
|
+
# These guards reach an agent by two independent channels: this dispatcher,
|
|
293
|
+
# registered in `.claude/settings.json`, and the plugin manifest, which
|
|
294
|
+
# registers the same guards individually. An agent sees the UNION of the two
|
|
295
|
+
# verdicts, so a TIGHTENING on either channel takes effect at once while a
|
|
296
|
+
# RELAXATION is inert until the slower channel catches up — the stale copy goes
|
|
297
|
+
# on refusing, and its refusal wins. What the operator sees is a guard blocking
|
|
298
|
+
# something `main` already permits, which reads as the guard being WRONG rather
|
|
299
|
+
# than OLD, and the move that reading suggests is to route around it.
|
|
300
|
+
#
|
|
301
|
+
# The two channels are two different files refreshed by two different
|
|
302
|
+
# mechanisms: `scripts/lisa-hooks/` when `lisa apply` runs, the plugin's own
|
|
303
|
+
# `hooks/` when the plugin updates. They drift. Measured on one machine on one
|
|
304
|
+
# day, two checkouts of the SAME repository resolved plugin 4.32.2 and 4.47.0.
|
|
305
|
+
#
|
|
306
|
+
# `plugin_tree` above cannot see any of that: it is repo-relative, so it exists
|
|
307
|
+
# only inside the Lisa monorepo, and in a host project the plugin actually in
|
|
308
|
+
# force is somewhere else entirely. The vintage machinery written for #3205
|
|
309
|
+
# was therefore blind to the channel it races. This resolves it properly, from
|
|
310
|
+
# the record the runtime itself keeps.
|
|
311
|
+
#
|
|
312
|
+
# WHAT THIS RECORD CAN AND CANNOT ANSWER. The header above explains why a
|
|
313
|
+
# previous stand-down keyed on `installed_plugins.json` was removed: the record
|
|
314
|
+
# is project-blind as it was read then, enablement-blind (`enabledPlugins` can
|
|
315
|
+
# switch a plugin off without removing its entry), and session-blind (hooks load
|
|
316
|
+
# at session start; the record is rewritten on any install). Those defeat a
|
|
317
|
+
# LIVENESS question — "are the plugin's guards running in this session?" — and
|
|
318
|
+
# nothing on disk answers that one.
|
|
319
|
+
#
|
|
320
|
+
# This asks a different question: "what VINTAGE is the plugin installed for this
|
|
321
|
+
# project?" The record answers that directly, and the project-blindness is
|
|
322
|
+
# resolved by keying the lookup on this repo root rather than on the plugin name.
|
|
323
|
+
# The remaining two blindnesses bound the claim rather than break it: a skew
|
|
324
|
+
# reported here is a real difference between two copies on disk, but a disabled
|
|
325
|
+
# or not-yet-loaded plugin may mean those copies are not both firing right now.
|
|
326
|
+
# So the wording below reports what is INSTALLED and what the union WOULD mean,
|
|
327
|
+
# and never asserts that both channels are live. Reporting is also why this may
|
|
328
|
+
# use the record at all where a stand-down may not: an over-reported skew costs
|
|
329
|
+
# a line of notice, while an over-confident stand-down costs enforcement.
|
|
330
|
+
plugin_channel_version=""
|
|
331
|
+
plugin_channel_path=""
|
|
332
|
+
|
|
333
|
+
# Resolve the vintage of the guard channel the PLUGIN MANIFEST runs.
|
|
334
|
+
#
|
|
335
|
+
# `plugins/installed_plugins.json` records installs PER PROJECT DIRECTORY —
|
|
336
|
+
# which is exactly why one checkout sits versions behind another on the same
|
|
337
|
+
# disk — so the lookup is keyed on this repo root, not on the machine.
|
|
338
|
+
#
|
|
339
|
+
# A record is claimed only when its `installPath` sits under a `lisa/lisa/`
|
|
340
|
+
# marketplace cache. A locally-installed plugin does not match and is left
|
|
341
|
+
# UNRESOLVED on purpose: reporting "agree" about a copy that was never read is
|
|
342
|
+
# the failure this whole section exists to end, and an honest "I could not
|
|
343
|
+
# tell" is the safe direction.
|
|
344
|
+
#
|
|
345
|
+
# `grep` does the scanning because the record runs to tens of thousands of
|
|
346
|
+
# lines; the bash loop then sees a handful. Called only from resolve_vintages,
|
|
347
|
+
# so it inherits that latch and never runs on the silent allow path.
|
|
348
|
+
resolve_plugin_channel() {
|
|
349
|
+
local config_dir="${CLAUDE_CONFIG_DIR-}"
|
|
350
|
+
[ -n "$config_dir" ] || config_dir="${HOME-}/.claude"
|
|
351
|
+
local record="$config_dir/plugins/installed_plugins.json"
|
|
352
|
+
[ -f "$record" ] || return 1
|
|
353
|
+
|
|
354
|
+
local install_pattern="^[[:space:]]*\"installPath\"[[:space:]]*:[[:space:]]*\"(.*/lisa/lisa/[^\"]+)\""
|
|
355
|
+
local version_pattern="^[[:space:]]*\"version\"[[:space:]]*:[[:space:]]*\"([^\"]+)\""
|
|
356
|
+
local line
|
|
357
|
+
local candidate=""
|
|
358
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
359
|
+
# `grep -A` separates non-adjacent groups with `--`. A candidate must not
|
|
360
|
+
# survive across that boundary or one plugin's installPath answers for
|
|
361
|
+
# another plugin's version.
|
|
362
|
+
if [ "$line" = "--" ]; then
|
|
363
|
+
candidate=""
|
|
364
|
+
continue
|
|
365
|
+
fi
|
|
366
|
+
if [[ "$line" =~ $install_pattern ]]; then
|
|
367
|
+
candidate="${BASH_REMATCH[1]}"
|
|
368
|
+
continue
|
|
369
|
+
fi
|
|
370
|
+
if [ -n "$candidate" ] && [[ "$line" =~ $version_pattern ]]; then
|
|
371
|
+
plugin_channel_path="$candidate"
|
|
372
|
+
plugin_channel_version="${BASH_REMATCH[1]}"
|
|
373
|
+
return 0
|
|
374
|
+
fi
|
|
375
|
+
done < <(grep -F -A4 "\"projectPath\": \"$repo_root\"," "$record" 2>/dev/null)
|
|
376
|
+
return 1
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
# Verdict of the last classify_channel_skew call: agree | skew | undetermined.
|
|
380
|
+
#
|
|
381
|
+
# Three answers, never two. "The channels agree" and "I could not read one of
|
|
382
|
+
# them" are DIFFERENT facts, and collapsing them is how a probe reports success
|
|
383
|
+
# while measuring nothing.
|
|
384
|
+
#
|
|
385
|
+
# THIS PROBE REPORTS. IT NEVER BLOCKS. A permitted command stays permitted
|
|
386
|
+
# whatever the verdict, and the finding goes into the once-per-session notice.
|
|
387
|
+
# That is a decision, not an oversight, so here is the reasoning for whoever
|
|
388
|
+
# comes to change it:
|
|
389
|
+
#
|
|
390
|
+
# - The two error directions have asymmetric costs. An over-reported skew
|
|
391
|
+
# costs a line of notice; an over-confident stand-down costs enforcement.
|
|
392
|
+
# When the costs are that lopsided the direction is settled without needing
|
|
393
|
+
# to argue about likelihood.
|
|
394
|
+
# - Failing closed on `undetermined` would refuse work on every host with no
|
|
395
|
+
# `installed_plugins.json` — containers, fresh clones, non-Claude runtimes.
|
|
396
|
+
# That is a large population with nothing wrong with it, and refusing them
|
|
397
|
+
# is a bigger fault than the one being detected.
|
|
398
|
+
# - What is detected is two copies at different AGES, not a compromised
|
|
399
|
+
# guard. Blocking on it turns a diagnostic into an outage.
|
|
400
|
+
# - And the practical argument, which beats the principled one: skew is the
|
|
401
|
+
# common case on a developer machine right now, so as a blocker this would
|
|
402
|
+
# be a permanent stop-work and the first response would be to switch it
|
|
403
|
+
# off. A blocker everyone turns off protects nothing.
|
|
404
|
+
#
|
|
405
|
+
# Pinned by the test named "lets a permitted command through whatever the
|
|
406
|
+
# verdict is", which asserts exit 0 on both the skew and undetermined arms.
|
|
407
|
+
channel_skew_verdict="undetermined"
|
|
408
|
+
channel_skew_line=""
|
|
409
|
+
|
|
410
|
+
# Compare the vintage of the channel this dispatcher runs against the vintage
|
|
411
|
+
# of the channel the plugin manifest runs.
|
|
412
|
+
classify_channel_skew() {
|
|
413
|
+
local mine=""
|
|
414
|
+
if [ "$host_tree_used" -eq 1 ]; then
|
|
415
|
+
mine="$host_tree_version"
|
|
416
|
+
elif [ "$plugin_tree_used" -eq 1 ]; then
|
|
417
|
+
mine="$plugin_tree_version"
|
|
418
|
+
fi
|
|
419
|
+
|
|
420
|
+
if [ -z "$mine" ] || [ -z "$plugin_channel_version" ]; then
|
|
421
|
+
channel_skew_verdict="undetermined"
|
|
422
|
+
channel_skew_line=" cross-channel vintage UNDETERMINED — this dispatcher's guards could not be
|
|
423
|
+
compared with the plugin manifest's copies. Not agreement: an unread copy
|
|
424
|
+
can be any age, and a relaxation shipped to one channel stays inert until
|
|
425
|
+
the other catches up.
|
|
426
|
+
"
|
|
427
|
+
return 0
|
|
428
|
+
fi
|
|
429
|
+
|
|
430
|
+
if [ "$mine" = "$plugin_channel_version" ]; then
|
|
431
|
+
channel_skew_verdict="agree"
|
|
432
|
+
channel_skew_line=""
|
|
433
|
+
return 0
|
|
434
|
+
fi
|
|
435
|
+
|
|
436
|
+
channel_skew_verdict="skew"
|
|
437
|
+
# The sentence is kept whole on one line on purpose: it is the finding, and
|
|
438
|
+
# an operator greps for it.
|
|
439
|
+
channel_skew_line=" cross-channel vintage SKEW — this dispatcher runs lisa $mine; the plugin
|
|
440
|
+
installed for this project is lisa $plugin_channel_version at $plugin_channel_path.
|
|
441
|
+
When both fire on one tool call the agent sees the UNION of their verdicts,
|
|
442
|
+
which means:
|
|
443
|
+
YOU CANNOT RETIRE A REFUSAL BY SHIPPING A FIX.
|
|
444
|
+
A relaxation on the newer channel does nothing until the older one is
|
|
445
|
+
refreshed, so a block you cannot explain from \`main\` is the older copy
|
|
446
|
+
still enforcing.
|
|
447
|
+
repair: refresh both — \`npx @codyswann/lisa apply\` for this checkout's
|
|
448
|
+
guards, and update the installed plugin for the manifest's copies.
|
|
449
|
+
"
|
|
450
|
+
return 0
|
|
279
451
|
}
|
|
280
452
|
|
|
281
453
|
# Description of the last describe_vintage call.
|
|
@@ -613,8 +785,10 @@ if [ "$notice_due" -eq 1 ]; then
|
|
|
613
785
|
if [ "$plugin_tree_used" -eq 1 ]; then
|
|
614
786
|
note_tree_staleness "$plugin_tree" "$plugin_tree_version" "$PLUGIN_REPAIR"
|
|
615
787
|
fi
|
|
788
|
+
classify_channel_skew
|
|
616
789
|
|
|
617
|
-
if [ -n "$stale_notice" ] || [ -n "$shadowed" ] || [ -n "$missing" ]
|
|
790
|
+
if [ -n "$stale_notice" ] || [ -n "$shadowed" ] || [ -n "$missing" ] || \
|
|
791
|
+
[ -n "$channel_skew_line" ]; then
|
|
618
792
|
# Claim the session BEFORE printing. A failed `mkdir` suppresses this copy
|
|
619
793
|
# only when another process left the expected real directory behind. Every
|
|
620
794
|
# other failure leaves the claim unproven and prints, so an unwritable state
|
|
@@ -650,6 +824,9 @@ if [ "$notice_due" -eq 1 ]; then
|
|
|
650
824
|
if [ -n "$missing" ]; then
|
|
651
825
|
printf ' unresolved guards: %s (no copy was dispatched)\n' "$missing"
|
|
652
826
|
fi
|
|
827
|
+
if [ -n "$channel_skew_line" ]; then
|
|
828
|
+
printf '%s' "$channel_skew_line"
|
|
829
|
+
fi
|
|
653
830
|
} >&2
|
|
654
831
|
fi
|
|
655
832
|
fi
|
|
@@ -497,6 +497,82 @@ project_dir = os.environ.get("LISA_GUARD_PROJECT_DIR", "")
|
|
|
497
497
|
OVERRIDE_NAME = "LISA_ALLOW_DIRECT_ISSUE_CREATE"
|
|
498
498
|
HUMAN_GATE_MARKER = "[lisa-human-gate]"
|
|
499
499
|
|
|
500
|
+
# Decoration a hold declaration may sit behind, and nothing more: whitespace,
|
|
501
|
+
# blockquote arrows, list bullets or numbers, emphasis, an opening HTML
|
|
502
|
+
# comment. One character class plus two optional groups, deliberately not a
|
|
503
|
+
# nested quantifier — this runs over untrusted issue bodies.
|
|
504
|
+
HUMAN_GATE_DECORATION = re.compile(r"^[ \t>*_+#-]*(?:\d+[.)][ \t]*)?(?:<!--[ \t]*)?[ \t]*")
|
|
505
|
+
HUMAN_GATE_FENCE = re.compile(r"```.*?```", re.DOTALL)
|
|
506
|
+
HUMAN_GATE_CODE_SPAN = re.compile(r"`[^`\n]*`")
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def declares_human_gate(text):
|
|
510
|
+
"""Whether a text DECLARES a hold rather than mentioning one.
|
|
511
|
+
|
|
512
|
+
The comment this replaces said the marker is matched anywhere because it
|
|
513
|
+
"has no other meaning, so its presence in the title or body IS the
|
|
514
|
+
declaration". That was true when written and is false now: the marker
|
|
515
|
+
acquired a second meaning -- being discussed -- the moment the feature
|
|
516
|
+
became something people file tickets about, and a ticket about the marker
|
|
517
|
+
then declares a hold on itself (CodySwannGT/lisa#3815).
|
|
518
|
+
|
|
519
|
+
A declaration is POSITIONAL: the marker leads its line, behind at most the
|
|
520
|
+
decoration above, with fenced blocks and inline code spans removed first
|
|
521
|
+
because that is how the marker is written ABOUT. The same rule the intake
|
|
522
|
+
matcher applies, so the two surfaces cannot disagree about one body.
|
|
523
|
+
|
|
524
|
+
NOTE the direction here differs from the intake matcher's. There a match
|
|
525
|
+
HOLDS an item, so a false positive parks work. Here a match ADMITS a
|
|
526
|
+
filing, so a false positive is a hole -- which makes this the tightening
|
|
527
|
+
direction on both sides at once.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
text: Any candidate text -- one argv value, a body file, a payload.
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
True when some line of the text declares a hold.
|
|
534
|
+
"""
|
|
535
|
+
if not text:
|
|
536
|
+
return False
|
|
537
|
+
body = HUMAN_GATE_CODE_SPAN.sub("", HUMAN_GATE_FENCE.sub("", str(text)))
|
|
538
|
+
for line in body.split("\n"):
|
|
539
|
+
if HUMAN_GATE_DECORATION.sub("", line, count=1).startswith(
|
|
540
|
+
HUMAN_GATE_MARKER
|
|
541
|
+
):
|
|
542
|
+
return True
|
|
543
|
+
if _comment_carries_marker(line):
|
|
544
|
+
return True
|
|
545
|
+
return False
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def _comment_carries_marker(line):
|
|
549
|
+
"""Whether an HTML comment on this line carries the marker.
|
|
550
|
+
|
|
551
|
+
The second accepted form, and the one this guard needs most: its inputs
|
|
552
|
+
are not markdown documents. A `--body` value is a single line, and a shell
|
|
553
|
+
script writes its declaration as `# <!-- ... -->`, so a purely
|
|
554
|
+
line-leading rule would refuse two declarations people have already
|
|
555
|
+
written -- both of which are in this repository's own test corpus.
|
|
556
|
+
|
|
557
|
+
It is not a loosening. An HTML comment is invisible when rendered, so
|
|
558
|
+
nobody wraps one around the marker in order to TALK about it; a quotation
|
|
559
|
+
has to be visible to be a quotation. Measured over this repository's 42
|
|
560
|
+
matching issue bodies, adding this branch changes not one verdict.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
line: One line of text.
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
True when some comment on the line contains the marker.
|
|
567
|
+
"""
|
|
568
|
+
segments = line.split("<!--")
|
|
569
|
+
for segment in segments[1:]:
|
|
570
|
+
close = segment.find("-->")
|
|
571
|
+
inside = segment if close == -1 else segment[:close]
|
|
572
|
+
if HUMAN_GATE_MARKER in inside:
|
|
573
|
+
return True
|
|
574
|
+
return False
|
|
575
|
+
|
|
500
576
|
# ---------------------------------------------------------------------------
|
|
501
577
|
# WHY THIS IS A TOKEN SCAN AND NOT A PROGRAM RESOLVER
|
|
502
578
|
#
|
|
@@ -583,7 +659,7 @@ LABEL_FLAGS = {"--label", "--labels", "--add-label", "--status", "--state"}
|
|
|
583
659
|
# None — container: state rolls up from children
|
|
584
660
|
#
|
|
585
661
|
# It is a marker with no other meaning, so it is matched wherever the payload
|
|
586
|
-
# reaches —
|
|
662
|
+
# reaches — the asymmetry `HUMAN_GATE_MARKER` gets, and for the related
|
|
587
663
|
# reason. Reading the declaration the writer already emits is also what keeps
|
|
588
664
|
# the guard and the skill from drifting apart a second time: there is one
|
|
589
665
|
# string, defined in one rule, and both read it.
|
|
@@ -1226,7 +1302,7 @@ def text_declares_readiness(text):
|
|
|
1226
1302
|
Returns:
|
|
1227
1303
|
True when the text declares build-ready or a human gate.
|
|
1228
1304
|
"""
|
|
1229
|
-
if
|
|
1305
|
+
if declares_human_gate(text):
|
|
1230
1306
|
return True
|
|
1231
1307
|
for role in (ready_role, upstream_ready_role, default_ready_role):
|
|
1232
1308
|
if not role:
|
|
@@ -1270,7 +1346,7 @@ def scope_declaration(text, state_role_ok):
|
|
|
1270
1346
|
Returns:
|
|
1271
1347
|
True when the text carries a whole-scope declaration.
|
|
1272
1348
|
"""
|
|
1273
|
-
if
|
|
1349
|
+
if declares_human_gate(text):
|
|
1274
1350
|
return True
|
|
1275
1351
|
return bool(state_role_ok and LIFECYCLE_ROLE_READY.search(text))
|
|
1276
1352
|
|
|
@@ -1500,20 +1576,23 @@ def declares_readiness(raw_args, roles, extra="", state_role_ok=False):
|
|
|
1500
1576
|
candidates = [part.strip().strip("'\"") for part in raw.split(",")]
|
|
1501
1577
|
if role in candidates:
|
|
1502
1578
|
return True
|
|
1503
|
-
#
|
|
1504
|
-
#
|
|
1505
|
-
#
|
|
1506
|
-
#
|
|
1507
|
-
|
|
1579
|
+
# Checked per ARGV VALUE rather than against `" ".join(args)`, and that is
|
|
1580
|
+
# what makes the positional rule mean anything here. Joining first destroys
|
|
1581
|
+
# position: after a join, no value's line start survives except the first
|
|
1582
|
+
# argument's, so "the marker leads its line" would be decided by argument
|
|
1583
|
+
# order. A `--title` value is its own single line, which is exactly the
|
|
1584
|
+
# unit the rule is about -- and titles must keep being read, since this
|
|
1585
|
+
# guard reads them and the intake matcher does not.
|
|
1586
|
+
if any(declares_human_gate(value) for value in args):
|
|
1508
1587
|
return True
|
|
1509
1588
|
for path in body_file_paths(args):
|
|
1510
1589
|
try:
|
|
1511
1590
|
with open(path, encoding="utf-8", errors="replace") as handle:
|
|
1512
|
-
if
|
|
1591
|
+
if declares_human_gate(handle.read()):
|
|
1513
1592
|
return True
|
|
1514
1593
|
except OSError:
|
|
1515
1594
|
continue
|
|
1516
|
-
if extra
|
|
1595
|
+
if declares_human_gate(extra):
|
|
1517
1596
|
return True
|
|
1518
1597
|
# The state-based path. Scoped by `state_role_ok` rather than checked
|
|
1519
1598
|
# unconditionally, so this adds a compliant command where none existed and
|
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
# divergence: upstream blocks unconditionally; Lisa allows clean-tree resets.
|
|
30
30
|
# Residual risk (documented, accepted): the dirty check runs in the hook's
|
|
31
31
|
# cwd at hook time, so a `cd elsewhere && git reset --hard` evades it.
|
|
32
|
+
# - `git read-tree` with an update flag AND `--reset` while the working tree
|
|
33
|
+
# is dirty (issue #3978): the plumbing spelling of the same discard, on the
|
|
34
|
+
# same condition, printing the same remedy. Index-only `read-tree`, and
|
|
35
|
+
# `read-tree -m -u` without `--reset` (which git itself refuses rather than
|
|
36
|
+
# clobber a modified file), stay allowed. Lisa-only guard.
|
|
32
37
|
# - `git rebase --abort`/`--quit` while the in-progress rebase holds
|
|
33
38
|
# human-made conflict resolutions (AUTO_MERGE discriminator; issue #1956).
|
|
34
39
|
# Clean or untouched rebase state stays abortable; the apply backend and a
|
|
@@ -1822,6 +1827,45 @@ $DESTRUCTIVE_GUIDANCE"
|
|
|
1822
1827
|
fi
|
|
1823
1828
|
fi
|
|
1824
1829
|
|
|
1830
|
+
# 3a. `git read-tree` with BOTH an update flag and `--reset`, on the same dirty
|
|
1831
|
+
# tree — the plumbing spelling of guard 3 (issue #3978). It resets the index
|
|
1832
|
+
# and writes the result into the working tree, discarding tracked
|
|
1833
|
+
# modifications: the identical destruction, reached through a command that
|
|
1834
|
+
# carries no `--hard` and no `reset` subcommand, so every pattern in
|
|
1835
|
+
# guard 3 misses it. A guard an agent can walk around is a guard that stops
|
|
1836
|
+
# the honest caller and not the one looking for a way through, and this
|
|
1837
|
+
# spelling is strictly worse than the blocked one — it discards WITHOUT
|
|
1838
|
+
# preserving, which is exactly the property guard 3's remedy supplies. Both
|
|
1839
|
+
# refusals therefore print the same $PRESERVE_GUIDANCE, so what they teach
|
|
1840
|
+
# cannot drift apart.
|
|
1841
|
+
#
|
|
1842
|
+
# `--reset` is the discriminator, not `-u`, and the pairing is deliberate:
|
|
1843
|
+
#
|
|
1844
|
+
# - `read-tree` with no update flag touches the INDEX only and destroys
|
|
1845
|
+
# nothing in the working tree, so a blanket `read-tree` block would be
|
|
1846
|
+
# over-broad.
|
|
1847
|
+
# - `read-tree -m -u` (a merge without `--reset`) is safe by git's own
|
|
1848
|
+
# construction: it REFUSES rather than overwrite a locally modified file
|
|
1849
|
+
# ("entry would be overwritten by merge"). `--reset` is precisely the flag
|
|
1850
|
+
# that switches that refusal off.
|
|
1851
|
+
#
|
|
1852
|
+
# So the destructive combination is an update flag AND `--reset` together,
|
|
1853
|
+
# in either order. The update flag is matched in the checkout guard's idiom
|
|
1854
|
+
# so a bundled short option (`-iu`) cannot dodge the anchor, with leading
|
|
1855
|
+
# whitespace required so a `--index-output=/tmp/out-u` style value cannot
|
|
1856
|
+
# read as the flag. Same conditional dirty-tree shape and same accepted
|
|
1857
|
+
# residual risk as guard 3: the probe runs in the hook's cwd at hook time.
|
|
1858
|
+
readonly GIT_READ_TREE="${GIT_CMD}read-tree"
|
|
1859
|
+
if matches "${GIT_READ_TREE}"'[^;&|]*[[:space:]]--reset([[:space:]]|$)' \
|
|
1860
|
+
&& matches "${GIT_READ_TREE}"'[^;&|]*[[:space:]](-[[:alnum:]]*u[[:alnum:]]*|--update)([[:space:]]|=|$)'; then
|
|
1861
|
+
if git rev-parse --is-inside-work-tree >/dev/null 2>&1 \
|
|
1862
|
+
&& [ -n "$(git status --porcelain 2>/dev/null)" ]; then
|
|
1863
|
+
block "git read-tree -u --reset on a dirty working tree would discard uncommitted changes (commit first, or preserve as below)" "$PRESERVE_GUIDANCE
|
|
1864
|
+
|
|
1865
|
+
$DESTRUCTIVE_GUIDANCE"
|
|
1866
|
+
fi
|
|
1867
|
+
fi
|
|
1868
|
+
|
|
1825
1869
|
# 3b. `git rebase --abort` / `--quit` ONLY while human/agent conflict
|
|
1826
1870
|
# resolutions exist (issue #1956). `--abort` restores the pre-rebase branch
|
|
1827
1871
|
# and discards every resolution; `--quit` deletes the rebase bookkeeping
|
|
@@ -376,7 +376,7 @@ export const run = async (argv, cwd) => {
|
|
|
376
376
|
* @returns {boolean} Whether the CLI body should run.
|
|
377
377
|
*/
|
|
378
378
|
export const invokedAsScript = (moduleUrl, argv1 = process.argv[1]) => {
|
|
379
|
-
if (argv1
|
|
379
|
+
if (typeof argv1 !== "string" || argv1.length === 0) return false;
|
|
380
380
|
try {
|
|
381
381
|
return realpathSync(argv1) === realpathSync(fileURLToPath(moduleUrl));
|
|
382
382
|
} catch {
|