@sun-asterisk/sungen 3.2.35 → 3.3.0-alpha.10
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/dist/basis/anchor-grammar.d.ts +32 -0
- package/dist/basis/anchor-grammar.d.ts.map +1 -0
- package/dist/basis/anchor-grammar.js +82 -0
- package/dist/basis/anchor-grammar.js.map +1 -0
- package/dist/basis/anchor-hash.d.ts +6 -0
- package/dist/basis/anchor-hash.d.ts.map +1 -0
- package/dist/basis/anchor-hash.js +28 -0
- package/dist/basis/anchor-hash.js.map +1 -0
- package/dist/basis/bm51-tables.d.ts +20 -0
- package/dist/basis/bm51-tables.d.ts.map +1 -0
- package/dist/basis/bm51-tables.js +71 -0
- package/dist/basis/bm51-tables.js.map +1 -0
- package/dist/basis/extract-spec-anchors.d.ts +39 -0
- package/dist/basis/extract-spec-anchors.d.ts.map +1 -0
- package/dist/basis/extract-spec-anchors.js +81 -0
- package/dist/basis/extract-spec-anchors.js.map +1 -0
- package/dist/basis/migration-sentinel.d.ts +47 -0
- package/dist/basis/migration-sentinel.d.ts.map +1 -0
- package/dist/basis/migration-sentinel.js +219 -0
- package/dist/basis/migration-sentinel.js.map +1 -0
- package/dist/basis/spec-frontmatter.d.ts +18 -0
- package/dist/basis/spec-frontmatter.d.ts.map +1 -0
- package/dist/basis/spec-frontmatter.js +55 -0
- package/dist/basis/spec-frontmatter.js.map +1 -0
- package/dist/basis/spec-sections.d.ts +18 -0
- package/dist/basis/spec-sections.d.ts.map +1 -0
- package/dist/basis/spec-sections.js +27 -0
- package/dist/basis/spec-sections.js.map +1 -0
- package/dist/basis/test-basis-schema.d.ts +28 -0
- package/dist/basis/test-basis-schema.d.ts.map +1 -0
- package/dist/basis/test-basis-schema.js +141 -0
- package/dist/basis/test-basis-schema.js.map +1 -0
- package/dist/capabilities/discover.d.ts +4 -0
- package/dist/capabilities/discover.d.ts.map +1 -1
- package/dist/capabilities/discover.js +16 -2
- package/dist/capabilities/discover.js.map +1 -1
- package/dist/capabilities/registry.d.ts +15 -0
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/cli/case-triage-hint.d.ts +14 -0
- package/dist/cli/case-triage-hint.d.ts.map +1 -0
- package/dist/cli/case-triage-hint.js +23 -0
- package/dist/cli/case-triage-hint.js.map +1 -0
- package/dist/cli/commands/audit.js +1 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.js +1 -1
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/contract-derive-command.d.ts +12 -0
- package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
- package/dist/cli/commands/contract-derive-command.js +113 -0
- package/dist/cli/commands/contract-derive-command.js.map +1 -0
- package/dist/cli/commands/contract.d.ts +20 -0
- package/dist/cli/commands/contract.d.ts.map +1 -0
- package/dist/cli/commands/contract.js +195 -0
- package/dist/cli/commands/contract.js.map +1 -0
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +109 -19
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/gate.d.ts.map +1 -1
- package/dist/cli/commands/gate.js +2 -1
- package/dist/cli/commands/gate.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +17 -3
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/journey.d.ts.map +1 -1
- package/dist/cli/commands/journey.js +47 -17
- package/dist/cli/commands/journey.js.map +1 -1
- package/dist/cli/commands/next.d.ts +13 -0
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +98 -9
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/triage.d.ts +2 -1
- package/dist/cli/commands/triage.d.ts.map +1 -1
- package/dist/cli/commands/triage.js +45 -3
- package/dist/cli/commands/triage.js.map +1 -1
- package/dist/cli/commands/update.d.ts +11 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +37 -8
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.js +24 -33
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/json-error-on-exit.d.ts +34 -0
- package/dist/cli/json-error-on-exit.d.ts.map +1 -0
- package/dist/cli/json-error-on-exit.js +63 -0
- package/dist/cli/json-error-on-exit.js.map +1 -0
- package/dist/cli/project-sungen-invocation.d.ts +13 -0
- package/dist/cli/project-sungen-invocation.d.ts.map +1 -0
- package/dist/cli/project-sungen-invocation.js +95 -0
- package/dist/cli/project-sungen-invocation.js.map +1 -0
- package/dist/cli/run-command-hints.d.ts +9 -0
- package/dist/cli/run-command-hints.d.ts.map +1 -0
- package/dist/cli/run-command-hints.js +84 -0
- package/dist/cli/run-command-hints.js.map +1 -0
- package/dist/cli/waiting-for-you-block.d.ts +18 -0
- package/dist/cli/waiting-for-you-block.d.ts.map +1 -0
- package/dist/cli/waiting-for-you-block.js +22 -0
- package/dist/cli/waiting-for-you-block.js.map +1 -0
- package/dist/contract/accept-opaque-steps.d.ts +3 -0
- package/dist/contract/accept-opaque-steps.d.ts.map +1 -0
- package/dist/contract/accept-opaque-steps.js +114 -0
- package/dist/contract/accept-opaque-steps.js.map +1 -0
- package/dist/contract/approve-contract.d.ts +38 -0
- package/dist/contract/approve-contract.d.ts.map +1 -0
- package/dist/contract/approve-contract.js +93 -0
- package/dist/contract/approve-contract.js.map +1 -0
- package/dist/contract/assertion-matcher-vocabulary.d.ts +24 -0
- package/dist/contract/assertion-matcher-vocabulary.d.ts.map +1 -0
- package/dist/contract/assertion-matcher-vocabulary.js +100 -0
- package/dist/contract/assertion-matcher-vocabulary.js.map +1 -0
- package/dist/contract/build-contract-verdict.d.ts +32 -0
- package/dist/contract/build-contract-verdict.d.ts.map +1 -0
- package/dist/contract/build-contract-verdict.js +123 -0
- package/dist/contract/build-contract-verdict.js.map +1 -0
- package/dist/contract/build-trace-conformance.d.ts +22 -0
- package/dist/contract/build-trace-conformance.d.ts.map +1 -0
- package/dist/contract/build-trace-conformance.js +151 -0
- package/dist/contract/build-trace-conformance.js.map +1 -0
- package/dist/contract/classify-contract-change.d.ts +24 -0
- package/dist/contract/classify-contract-change.d.ts.map +1 -0
- package/dist/contract/classify-contract-change.js +98 -0
- package/dist/contract/classify-contract-change.js.map +1 -0
- package/dist/contract/classify-contract-unit.d.ts +18 -0
- package/dist/contract/classify-contract-unit.d.ts.map +1 -0
- package/dist/contract/classify-contract-unit.js +49 -0
- package/dist/contract/classify-contract-unit.js.map +1 -0
- package/dist/contract/classify-trace-conformance.d.ts +47 -0
- package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
- package/dist/contract/classify-trace-conformance.js +164 -0
- package/dist/contract/classify-trace-conformance.js.map +1 -0
- package/dist/contract/contract-git.d.ts +24 -0
- package/dist/contract/contract-git.d.ts.map +1 -0
- package/dist/contract/contract-git.js +144 -0
- package/dist/contract/contract-git.js.map +1 -0
- package/dist/contract/contract-hash.d.ts +7 -0
- package/dist/contract/contract-hash.d.ts.map +1 -0
- package/dist/contract/contract-hash.js +38 -0
- package/dist/contract/contract-hash.js.map +1 -0
- package/dist/contract/contract-report-file.d.ts +4 -0
- package/dist/contract/contract-report-file.d.ts.map +1 -0
- package/dist/contract/contract-report-file.js +60 -0
- package/dist/contract/contract-report-file.js.map +1 -0
- package/dist/contract/contract-schema.d.ts +92 -0
- package/dist/contract/contract-schema.d.ts.map +1 -0
- package/dist/contract/contract-schema.js +124 -0
- package/dist/contract/contract-schema.js.map +1 -0
- package/dist/contract/contract-units.d.ts +12 -0
- package/dist/contract/contract-units.d.ts.map +1 -0
- package/dist/contract/contract-units.js +96 -0
- package/dist/contract/contract-units.js.map +1 -0
- package/dist/contract/contracts-lock-file.d.ts +24 -0
- package/dist/contract/contracts-lock-file.d.ts.map +1 -0
- package/dist/contract/contracts-lock-file.js +141 -0
- package/dist/contract/contracts-lock-file.js.map +1 -0
- package/dist/contract/data-normalize.d.ts +10 -0
- package/dist/contract/data-normalize.d.ts.map +1 -0
- package/dist/contract/data-normalize.js +110 -0
- package/dist/contract/data-normalize.js.map +1 -0
- package/dist/contract/derive-free-feature.d.ts +49 -0
- package/dist/contract/derive-free-feature.d.ts.map +1 -0
- package/dist/contract/derive-free-feature.js +183 -0
- package/dist/contract/derive-free-feature.js.map +1 -0
- package/dist/contract/field-semantic-redaction.d.ts +21 -0
- package/dist/contract/field-semantic-redaction.d.ts.map +1 -0
- package/dist/contract/field-semantic-redaction.js +118 -0
- package/dist/contract/field-semantic-redaction.js.map +1 -0
- package/dist/contract/free-feature-blocks.d.ts +10 -0
- package/dist/contract/free-feature-blocks.d.ts.map +1 -0
- package/dist/contract/free-feature-blocks.js +94 -0
- package/dist/contract/free-feature-blocks.js.map +1 -0
- package/dist/contract/lock-secret-guard.d.ts +22 -0
- package/dist/contract/lock-secret-guard.d.ts.map +1 -0
- package/dist/contract/lock-secret-guard.js +134 -0
- package/dist/contract/lock-secret-guard.js.map +1 -0
- package/dist/contract/lock-vs-git-head.d.ts +26 -0
- package/dist/contract/lock-vs-git-head.d.ts.map +1 -0
- package/dist/contract/lock-vs-git-head.js +28 -0
- package/dist/contract/lock-vs-git-head.js.map +1 -0
- package/dist/contract/normalize-contract-step.d.ts +21 -0
- package/dist/contract/normalize-contract-step.d.ts.map +1 -0
- package/dist/contract/normalize-contract-step.js +70 -0
- package/dist/contract/normalize-contract-step.js.map +1 -0
- package/dist/contract/normalize-feature-steps.d.ts +7 -0
- package/dist/contract/normalize-feature-steps.d.ts.map +1 -0
- package/dist/contract/normalize-feature-steps.js +127 -0
- package/dist/contract/normalize-feature-steps.js.map +1 -0
- package/dist/contract/oracle-strength.d.ts +12 -0
- package/dist/contract/oracle-strength.d.ts.map +1 -0
- package/dist/contract/oracle-strength.js +88 -0
- package/dist/contract/oracle-strength.js.map +1 -0
- package/dist/contract/project-contract-blockers.d.ts +10 -0
- package/dist/contract/project-contract-blockers.d.ts.map +1 -0
- package/dist/contract/project-contract-blockers.js +75 -0
- package/dist/contract/project-contract-blockers.js.map +1 -0
- package/dist/contract/record-contract-approval.d.ts +36 -0
- package/dist/contract/record-contract-approval.d.ts.map +1 -0
- package/dist/contract/record-contract-approval.js +126 -0
- package/dist/contract/record-contract-approval.js.map +1 -0
- package/dist/contract/render-free-gherkin.d.ts +42 -0
- package/dist/contract/render-free-gherkin.d.ts.map +1 -0
- package/dist/contract/render-free-gherkin.js +147 -0
- package/dist/contract/render-free-gherkin.js.map +1 -0
- package/dist/contract/render-trace-sentence.d.ts +20 -0
- package/dist/contract/render-trace-sentence.d.ts.map +1 -0
- package/dist/contract/render-trace-sentence.js +109 -0
- package/dist/contract/render-trace-sentence.js.map +1 -0
- package/dist/contract/render-trace-target.d.ts +14 -0
- package/dist/contract/render-trace-target.d.ts.map +1 -0
- package/dist/contract/render-trace-target.js +70 -0
- package/dist/contract/render-trace-target.js.map +1 -0
- package/dist/contract/resolve-unit-trace-identities.d.ts +20 -0
- package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
- package/dist/contract/resolve-unit-trace-identities.js +62 -0
- package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts +44 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.js +219 -0
- package/dist/contract/trace/collect-trace-evidence.js.map +1 -0
- package/dist/contract/trace/identity-cache.d.ts +19 -0
- package/dist/contract/trace/identity-cache.d.ts.map +1 -0
- package/dist/contract/trace/identity-cache.js +104 -0
- package/dist/contract/trace/identity-cache.js.map +1 -0
- package/dist/contract/trace/read-file-once.d.ts +20 -0
- package/dist/contract/trace/read-file-once.d.ts.map +1 -0
- package/dist/contract/trace/read-file-once.js +135 -0
- package/dist/contract/trace/read-file-once.js.map +1 -0
- package/dist/contract/trace/scenario-id-claims.d.ts +11 -0
- package/dist/contract/trace/scenario-id-claims.d.ts.map +1 -0
- package/dist/contract/trace/scenario-id-claims.js +89 -0
- package/dist/contract/trace/scenario-id-claims.js.map +1 -0
- package/dist/contract/trace/select-final-attempt.d.ts +29 -0
- package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
- package/dist/contract/trace/select-final-attempt.js +119 -0
- package/dist/contract/trace/select-final-attempt.js.map +1 -0
- package/dist/contract/trace/snapshot-html-renderer.d.ts +31 -0
- package/dist/contract/trace/snapshot-html-renderer.d.ts.map +1 -0
- package/dist/contract/trace/snapshot-html-renderer.js +172 -0
- package/dist/contract/trace/snapshot-html-renderer.js.map +1 -0
- package/dist/contract/trace/snapshot-resolver.d.ts +21 -0
- package/dist/contract/trace/snapshot-resolver.d.ts.map +1 -0
- package/dist/contract/trace/snapshot-resolver.js +65 -0
- package/dist/contract/trace/snapshot-resolver.js.map +1 -0
- package/dist/contract/trace/snapshot-store.d.ts +66 -0
- package/dist/contract/trace/snapshot-store.d.ts.map +1 -0
- package/dist/contract/trace/snapshot-store.js +173 -0
- package/dist/contract/trace/snapshot-store.js.map +1 -0
- package/dist/contract/trace/target-identity.d.ts +74 -0
- package/dist/contract/trace/target-identity.d.ts.map +1 -0
- package/dist/contract/trace/target-identity.js +68 -0
- package/dist/contract/trace/target-identity.js.map +1 -0
- package/dist/contract/trace/test-step-join.d.ts +65 -0
- package/dist/contract/trace/test-step-join.d.ts.map +1 -0
- package/dist/contract/trace/test-step-join.js +80 -0
- package/dist/contract/trace/test-step-join.js.map +1 -0
- package/dist/contract/trace/trace-binding.d.ts +22 -0
- package/dist/contract/trace/trace-binding.d.ts.map +1 -0
- package/dist/contract/trace/trace-binding.js +91 -0
- package/dist/contract/trace/trace-binding.js.map +1 -0
- package/dist/contract/trace/trace-file-reader.d.ts +47 -0
- package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
- package/dist/contract/trace/trace-file-reader.js +137 -0
- package/dist/contract/trace/trace-file-reader.js.map +1 -0
- package/dist/contract/trace/trace-fingerprint.d.ts +22 -0
- package/dist/contract/trace/trace-fingerprint.d.ts.map +1 -0
- package/dist/contract/trace/trace-fingerprint.js +96 -0
- package/dist/contract/trace/trace-fingerprint.js.map +1 -0
- package/dist/contract/trace/trace-freshness.d.ts +27 -0
- package/dist/contract/trace/trace-freshness.d.ts.map +1 -0
- package/dist/contract/trace/trace-freshness.js +146 -0
- package/dist/contract/trace/trace-freshness.js.map +1 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts +28 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
- package/dist/contract/trace/trace-identity-resolution.js +87 -0
- package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
- package/dist/contract/trace/trace-run-record.d.ts +32 -0
- package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
- package/dist/contract/trace/trace-run-record.js +135 -0
- package/dist/contract/trace/trace-run-record.js.map +1 -0
- package/dist/contract/trace/trace-steps.d.ts +19 -0
- package/dist/contract/trace/trace-steps.d.ts.map +1 -0
- package/dist/contract/trace/trace-steps.js +79 -0
- package/dist/contract/trace/trace-steps.js.map +1 -0
- package/dist/contract/trace/trace-zip-limits.d.ts +45 -0
- package/dist/contract/trace/trace-zip-limits.d.ts.map +1 -0
- package/dist/contract/trace/trace-zip-limits.js +52 -0
- package/dist/contract/trace/trace-zip-limits.js.map +1 -0
- package/dist/contract/trace/trace-zip-reader.d.ts +10 -0
- package/dist/contract/trace/trace-zip-reader.d.ts.map +1 -0
- package/dist/contract/trace/trace-zip-reader.js +186 -0
- package/dist/contract/trace/trace-zip-reader.js.map +1 -0
- package/dist/contract/verify-approval-decisions.d.ts +5 -0
- package/dist/contract/verify-approval-decisions.d.ts.map +1 -0
- package/dist/contract/verify-approval-decisions.js +109 -0
- package/dist/contract/verify-approval-decisions.js.map +1 -0
- package/dist/decisions/append-decision.d.ts +12 -0
- package/dist/decisions/append-decision.d.ts.map +1 -0
- package/dist/decisions/append-decision.js +73 -0
- package/dist/decisions/append-decision.js.map +1 -0
- package/dist/decisions/confirmed-decision-commands.d.ts +41 -0
- package/dist/decisions/confirmed-decision-commands.d.ts.map +1 -0
- package/dist/decisions/confirmed-decision-commands.js +190 -0
- package/dist/decisions/confirmed-decision-commands.js.map +1 -0
- package/dist/decisions/decision-file.d.ts +31 -0
- package/dist/decisions/decision-file.d.ts.map +1 -0
- package/dist/decisions/decision-file.js +139 -0
- package/dist/decisions/decision-file.js.map +1 -0
- package/dist/decisions/decision-identity.d.ts +12 -0
- package/dist/decisions/decision-identity.d.ts.map +1 -0
- package/dist/decisions/decision-identity.js +78 -0
- package/dist/decisions/decision-identity.js.map +1 -0
- package/dist/decisions/decision-integrity-check.d.ts +12 -0
- package/dist/decisions/decision-integrity-check.d.ts.map +1 -0
- package/dist/decisions/decision-integrity-check.js +102 -0
- package/dist/decisions/decision-integrity-check.js.map +1 -0
- package/dist/decisions/decision-schema.d.ts +43 -0
- package/dist/decisions/decision-schema.d.ts.map +1 -0
- package/dist/decisions/decision-schema.js +76 -0
- package/dist/decisions/decision-schema.js.map +1 -0
- package/dist/decisions/decision-source.d.ts +2 -0
- package/dist/decisions/decision-source.d.ts.map +1 -0
- package/dist/decisions/decision-source.js +18 -0
- package/dist/decisions/decision-source.js.map +1 -0
- package/dist/decisions/exclusive-file-create.d.ts +3 -0
- package/dist/decisions/exclusive-file-create.d.ts.map +1 -0
- package/dist/decisions/exclusive-file-create.js +84 -0
- package/dist/decisions/exclusive-file-create.js.map +1 -0
- package/dist/decisions/git-head-trust.d.ts +19 -0
- package/dist/decisions/git-head-trust.d.ts.map +1 -0
- package/dist/decisions/git-head-trust.js +128 -0
- package/dist/decisions/git-head-trust.js.map +1 -0
- package/dist/decisions/human-confirmation.d.ts +35 -0
- package/dist/decisions/human-confirmation.d.ts.map +1 -0
- package/dist/decisions/human-confirmation.js +161 -0
- package/dist/decisions/human-confirmation.js.map +1 -0
- package/dist/decisions/legacy-waiver-migration.d.ts +38 -0
- package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
- package/dist/decisions/legacy-waiver-migration.js +179 -0
- package/dist/decisions/legacy-waiver-migration.js.map +1 -0
- package/dist/decisions/read-decisions.d.ts +19 -0
- package/dist/decisions/read-decisions.d.ts.map +1 -0
- package/dist/decisions/read-decisions.js +90 -0
- package/dist/decisions/read-decisions.js.map +1 -0
- package/dist/decisions/viewpoint-baseline-adapter.d.ts +7 -0
- package/dist/decisions/viewpoint-baseline-adapter.d.ts.map +1 -0
- package/dist/decisions/viewpoint-baseline-adapter.js +109 -0
- package/dist/decisions/viewpoint-baseline-adapter.js.map +1 -0
- package/dist/decisions/waiver-decisions.d.ts +24 -0
- package/dist/decisions/waiver-decisions.d.ts.map +1 -0
- package/dist/decisions/waiver-decisions.js +46 -0
- package/dist/decisions/waiver-decisions.js.map +1 -0
- package/dist/exporters/csv-exporter.d.ts +11 -2
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +8 -4
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/matrix/build.d.ts +3 -0
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +10 -9
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/export.d.ts +9 -1
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +13 -4
- package/dist/exporters/matrix/export.js.map +1 -1
- package/dist/exporters/matrix/gates.d.ts +6 -1
- package/dist/exporters/matrix/gates.d.ts.map +1 -1
- package/dist/exporters/matrix/gates.js +57 -128
- package/dist/exporters/matrix/gates.js.map +1 -1
- package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
- package/dist/exporters/matrix/render-csv.js +3 -0
- package/dist/exporters/matrix/render-csv.js.map +1 -1
- package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts +13 -0
- package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-gate-overrides-sheet.js +44 -0
- package/dist/exporters/matrix/render-gate-overrides-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-xlsx.d.ts +1 -0
- package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
- package/dist/exporters/matrix/render-xlsx.js +3 -0
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +4 -1
- package/dist/exporters/matrix/types.d.ts.map +1 -1
- package/dist/exporters/matrix/types.js.map +1 -1
- package/dist/exporters/matrix/xlsx-cells.js +1 -1
- package/dist/exporters/matrix/xlsx-cells.js.map +1 -1
- package/dist/exporters/test-data-resolver.d.ts.map +1 -1
- package/dist/exporters/test-data-resolver.js +2 -12
- package/dist/exporters/test-data-resolver.js.map +1 -1
- package/dist/exporters/xlsx-exporter.d.ts +3 -2
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +4 -4
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +0 -1
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +1 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -13
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -9
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +18 -4
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +3 -9
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +3 -9
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +0 -8
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +8 -52
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.d.ts +0 -16
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +0 -59
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +2 -10
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/graph/board-human-actions.d.ts +5 -0
- package/dist/graph/board-human-actions.d.ts.map +1 -0
- package/dist/graph/board-human-actions.js +83 -0
- package/dist/graph/board-human-actions.js.map +1 -0
- package/dist/graph/evaluate-graph.d.ts +41 -0
- package/dist/graph/evaluate-graph.d.ts.map +1 -0
- package/dist/graph/evaluate-graph.js +77 -0
- package/dist/graph/evaluate-graph.js.map +1 -0
- package/dist/graph/human-action.d.ts +21 -0
- package/dist/graph/human-action.d.ts.map +1 -0
- package/dist/graph/human-action.js +3 -0
- package/dist/graph/human-action.js.map +1 -0
- package/dist/graph/locate-finding.d.ts +20 -0
- package/dist/graph/locate-finding.d.ts.map +1 -0
- package/dist/graph/locate-finding.js +137 -0
- package/dist/graph/locate-finding.js.map +1 -0
- package/dist/graph/manual-triage-decisions.d.ts +19 -0
- package/dist/graph/manual-triage-decisions.d.ts.map +1 -0
- package/dist/graph/manual-triage-decisions.js +53 -0
- package/dist/graph/manual-triage-decisions.js.map +1 -0
- package/dist/graph/node-decl.d.ts +75 -0
- package/dist/graph/node-decl.d.ts.map +1 -0
- package/dist/graph/node-decl.js +11 -0
- package/dist/graph/node-decl.js.map +1 -0
- package/dist/graph/nodes/core-node-catalog.d.ts +7 -0
- package/dist/graph/nodes/core-node-catalog.d.ts.map +1 -0
- package/dist/graph/nodes/core-node-catalog.js +13 -0
- package/dist/graph/nodes/core-node-catalog.js.map +1 -0
- package/dist/graph/nodes/test-case-contract-nodes.d.ts +21 -0
- package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/test-case-contract-nodes.js +72 -0
- package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
- package/dist/graph/nodes/test-case-nodes.d.ts +29 -0
- package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/test-case-nodes.js +95 -0
- package/dist/graph/nodes/test-case-nodes.js.map +1 -0
- package/dist/graph/nodes/unit-audit-nodes.d.ts +18 -0
- package/dist/graph/nodes/unit-audit-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/unit-audit-nodes.js +96 -0
- package/dist/graph/nodes/unit-audit-nodes.js.map +1 -0
- package/dist/graph/nodes/unit-contract-node.d.ts +21 -0
- package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -0
- package/dist/graph/nodes/unit-contract-node.js +96 -0
- package/dist/graph/nodes/unit-contract-node.js.map +1 -0
- package/dist/graph/nodes/unit-design-nodes.d.ts +14 -0
- package/dist/graph/nodes/unit-design-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/unit-design-nodes.js +91 -0
- package/dist/graph/nodes/unit-design-nodes.js.map +1 -0
- package/dist/graph/nodes/unit-execution-nodes.d.ts +16 -0
- package/dist/graph/nodes/unit-execution-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/unit-execution-nodes.js +79 -0
- package/dist/graph/nodes/unit-execution-nodes.js.map +1 -0
- package/dist/graph/test-case-identity.d.ts +13 -0
- package/dist/graph/test-case-identity.d.ts.map +1 -0
- package/dist/graph/test-case-identity.js +106 -0
- package/dist/graph/test-case-identity.js.map +1 -0
- package/dist/graph/test-case-results.d.ts +12 -0
- package/dist/graph/test-case-results.d.ts.map +1 -0
- package/dist/graph/test-case-results.js +161 -0
- package/dist/graph/test-case-results.js.map +1 -0
- package/dist/graph/test-case-satisfied.d.ts +26 -0
- package/dist/graph/test-case-satisfied.d.ts.map +1 -0
- package/dist/graph/test-case-satisfied.js +55 -0
- package/dist/graph/test-case-satisfied.js.map +1 -0
- package/dist/graph/test-case-state-shape.d.ts +73 -0
- package/dist/graph/test-case-state-shape.d.ts.map +1 -0
- package/dist/graph/test-case-state-shape.js +3 -0
- package/dist/graph/test-case-state-shape.js.map +1 -0
- package/dist/graph/test-case-states.d.ts +9 -0
- package/dist/graph/test-case-states.d.ts.map +1 -0
- package/dist/graph/test-case-states.js +116 -0
- package/dist/graph/test-case-states.js.map +1 -0
- package/dist/graph/unit-artifact-probes.d.ts +19 -0
- package/dist/graph/unit-artifact-probes.d.ts.map +1 -0
- package/dist/graph/unit-artifact-probes.js +135 -0
- package/dist/graph/unit-artifact-probes.js.map +1 -0
- package/dist/graph/unit-results-freshness.d.ts +29 -0
- package/dist/graph/unit-results-freshness.d.ts.map +1 -0
- package/dist/graph/unit-results-freshness.js +213 -0
- package/dist/graph/unit-results-freshness.js.map +1 -0
- package/dist/graph/unit-state-facts.d.ts +43 -0
- package/dist/graph/unit-state-facts.d.ts.map +1 -0
- package/dist/graph/unit-state-facts.js +87 -0
- package/dist/graph/unit-state-facts.js.map +1 -0
- package/dist/harness/audit.d.ts +14 -2
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +20 -21
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/catalog/gate-rules.yaml +647 -0
- package/dist/harness/defect-draft.d.ts.map +1 -1
- package/dist/harness/defect-draft.js +12 -2
- package/dist/harness/defect-draft.js.map +1 -1
- package/dist/harness/defects.d.ts +14 -0
- package/dist/harness/defects.d.ts.map +1 -1
- package/dist/harness/defects.js +14 -10
- package/dist/harness/defects.js.map +1 -1
- package/dist/harness/flow-check.d.ts +9 -0
- package/dist/harness/flow-check.d.ts.map +1 -1
- package/dist/harness/flow-check.js +11 -0
- package/dist/harness/flow-check.js.map +1 -1
- package/dist/harness/gates/audit-gate-terms.d.ts +41 -0
- package/dist/harness/gates/audit-gate-terms.d.ts.map +1 -0
- package/dist/harness/gates/audit-gate-terms.js +72 -0
- package/dist/harness/gates/audit-gate-terms.js.map +1 -0
- package/dist/harness/gates/audit-gate-verdict.d.ts +31 -0
- package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -0
- package/dist/harness/gates/audit-gate-verdict.js +102 -0
- package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
- package/dist/harness/gates/audit-input-hash.d.ts +6 -0
- package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
- package/dist/harness/gates/audit-input-hash.js +124 -0
- package/dist/harness/gates/audit-input-hash.js.map +1 -0
- package/dist/harness/gates/audit-report-verification.d.ts +43 -0
- package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
- package/dist/harness/gates/audit-report-verification.js +241 -0
- package/dist/harness/gates/audit-report-verification.js.map +1 -0
- package/dist/harness/gates/decisions-hash.d.ts +13 -0
- package/dist/harness/gates/decisions-hash.d.ts.map +1 -0
- package/dist/harness/gates/decisions-hash.js +72 -0
- package/dist/harness/gates/decisions-hash.js.map +1 -0
- package/dist/harness/gates/delivery-checklist.d.ts +24 -0
- package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
- package/dist/harness/gates/delivery-checklist.js +161 -0
- package/dist/harness/gates/delivery-checklist.js.map +1 -0
- package/dist/harness/gates/delivery-decision-gate.d.ts +33 -0
- package/dist/harness/gates/delivery-decision-gate.d.ts.map +1 -0
- package/dist/harness/gates/delivery-decision-gate.js +116 -0
- package/dist/harness/gates/delivery-decision-gate.js.map +1 -0
- package/dist/harness/gates/delivery-decision-step.d.ts +19 -0
- package/dist/harness/gates/delivery-decision-step.d.ts.map +1 -0
- package/dist/harness/gates/delivery-decision-step.js +86 -0
- package/dist/harness/gates/delivery-decision-step.js.map +1 -0
- package/dist/harness/gates/gate-override-report.d.ts +59 -0
- package/dist/harness/gates/gate-override-report.d.ts.map +1 -0
- package/dist/harness/gates/gate-override-report.js +86 -0
- package/dist/harness/gates/gate-override-report.js.map +1 -0
- package/dist/harness/gates/gate-override-scope.d.ts +18 -0
- package/dist/harness/gates/gate-override-scope.d.ts.map +1 -0
- package/dist/harness/gates/gate-override-scope.js +98 -0
- package/dist/harness/gates/gate-override-scope.js.map +1 -0
- package/dist/harness/gates/gate-override-validation.d.ts +25 -0
- package/dist/harness/gates/gate-override-validation.d.ts.map +1 -0
- package/dist/harness/gates/gate-override-validation.js +81 -0
- package/dist/harness/gates/gate-override-validation.js.map +1 -0
- package/dist/harness/gates/gate-rule-catalog.d.ts +24 -0
- package/dist/harness/gates/gate-rule-catalog.d.ts.map +1 -0
- package/dist/harness/gates/gate-rule-catalog.js +112 -0
- package/dist/harness/gates/gate-rule-catalog.js.map +1 -0
- package/dist/harness/gates/gate-terms.d.ts +33 -0
- package/dist/harness/gates/gate-terms.d.ts.map +1 -0
- package/dist/harness/gates/gate-terms.js +41 -0
- package/dist/harness/gates/gate-terms.js.map +1 -0
- package/dist/harness/gates/in-run-decision-check.d.ts +21 -0
- package/dist/harness/gates/in-run-decision-check.d.ts.map +1 -0
- package/dist/harness/gates/in-run-decision-check.js +166 -0
- package/dist/harness/gates/in-run-decision-check.js.map +1 -0
- package/dist/harness/gates/journey-gate-inputs.d.ts +37 -0
- package/dist/harness/gates/journey-gate-inputs.d.ts.map +1 -0
- package/dist/harness/gates/journey-gate-inputs.js +81 -0
- package/dist/harness/gates/journey-gate-inputs.js.map +1 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.d.ts +13 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.d.ts.map +1 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.js +141 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.js.map +1 -0
- package/dist/harness/gates/migrated-waiver-review.d.ts +16 -0
- package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
- package/dist/harness/gates/migrated-waiver-review.js +114 -0
- package/dist/harness/gates/migrated-waiver-review.js.map +1 -0
- package/dist/harness/gates/resolve-rule.d.ts +59 -0
- package/dist/harness/gates/resolve-rule.d.ts.map +1 -0
- package/dist/harness/gates/resolve-rule.js +41 -0
- package/dist/harness/gates/resolve-rule.js.map +1 -0
- package/dist/harness/gates/unit-gate-overrides.d.ts +25 -0
- package/dist/harness/gates/unit-gate-overrides.d.ts.map +1 -0
- package/dist/harness/gates/unit-gate-overrides.js +122 -0
- package/dist/harness/gates/unit-gate-overrides.js.map +1 -0
- package/dist/harness/journey.d.ts +64 -1
- package/dist/harness/journey.d.ts.map +1 -1
- package/dist/harness/journey.js +258 -34
- package/dist/harness/journey.js.map +1 -1
- package/dist/harness/manifest.d.ts +12 -0
- package/dist/harness/manifest.d.ts.map +1 -1
- package/dist/harness/manifest.js +71 -43
- package/dist/harness/manifest.js.map +1 -1
- package/dist/harness/next-step.d.ts +58 -4
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +55 -204
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts +7 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +18 -2
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/provenance.d.ts +29 -0
- package/dist/harness/provenance.d.ts.map +1 -1
- package/dist/harness/provenance.js +109 -9
- package/dist/harness/provenance.js.map +1 -1
- package/dist/harness/script-check.d.ts.map +1 -1
- package/dist/harness/script-check.js +22 -5
- package/dist/harness/script-check.js.map +1 -1
- package/dist/harness/sensors.d.ts +14 -3
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +41 -38
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/triage-manual-classification.d.ts +32 -0
- package/dist/harness/triage-manual-classification.d.ts.map +1 -0
- package/dist/harness/triage-manual-classification.js +56 -0
- package/dist/harness/triage-manual-classification.js.map +1 -0
- package/dist/harness/unit-paths.d.ts +8 -0
- package/dist/harness/unit-paths.d.ts.map +1 -1
- package/dist/harness/unit-paths.js +23 -1
- package/dist/harness/unit-paths.js.map +1 -1
- package/dist/hooks/bash-read-only-segment.d.ts +9 -0
- package/dist/hooks/bash-read-only-segment.d.ts.map +1 -0
- package/dist/hooks/bash-read-only-segment.js +168 -0
- package/dist/hooks/bash-read-only-segment.js.map +1 -0
- package/dist/hooks/bash-write-targets.d.ts +25 -0
- package/dist/hooks/bash-write-targets.d.ts.map +1 -0
- package/dist/hooks/bash-write-targets.js +266 -0
- package/dist/hooks/bash-write-targets.js.map +1 -0
- package/dist/hooks/claude-hook-entry.d.ts +11 -0
- package/dist/hooks/claude-hook-entry.d.ts.map +1 -0
- package/dist/hooks/claude-hook-entry.js +137 -0
- package/dist/hooks/claude-hook-entry.js.map +1 -0
- package/dist/hooks/claude-transcript-turn.d.ts +11 -0
- package/dist/hooks/claude-transcript-turn.d.ts.map +1 -0
- package/dist/hooks/claude-transcript-turn.js +143 -0
- package/dist/hooks/claude-transcript-turn.js.map +1 -0
- package/dist/hooks/guard-delivery-contract-rule.d.ts +10 -0
- package/dist/hooks/guard-delivery-contract-rule.d.ts.map +1 -0
- package/dist/hooks/guard-delivery-contract-rule.js +152 -0
- package/dist/hooks/guard-delivery-contract-rule.js.map +1 -0
- package/dist/hooks/guard-protected-paths.d.ts +13 -0
- package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
- package/dist/hooks/guard-protected-paths.js +116 -0
- package/dist/hooks/guard-protected-paths.js.map +1 -0
- package/dist/hooks/guard-rules.d.ts +20 -0
- package/dist/hooks/guard-rules.d.ts.map +1 -0
- package/dist/hooks/guard-rules.js +130 -0
- package/dist/hooks/guard-rules.js.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts +17 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.js +22 -0
- package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
- package/dist/hooks/hand-back-core.d.ts +42 -0
- package/dist/hooks/hand-back-core.d.ts.map +1 -0
- package/dist/hooks/hand-back-core.js +128 -0
- package/dist/hooks/hand-back-core.js.map +1 -0
- package/dist/hooks/hook-cli-command.d.ts +9 -0
- package/dist/hooks/hook-cli-command.d.ts.map +1 -0
- package/dist/hooks/hook-cli-command.js +46 -0
- package/dist/hooks/hook-cli-command.js.map +1 -0
- package/dist/hooks/hook-registry.d.ts +10 -0
- package/dist/hooks/hook-registry.d.ts.map +1 -0
- package/dist/hooks/hook-registry.js +24 -0
- package/dist/hooks/hook-registry.js.map +1 -0
- package/dist/hooks/hook-result-contract.d.ts +50 -0
- package/dist/hooks/hook-result-contract.d.ts.map +1 -0
- package/dist/hooks/hook-result-contract.js +80 -0
- package/dist/hooks/hook-result-contract.js.map +1 -0
- package/dist/hooks/hook-toggles.d.ts +10 -0
- package/dist/hooks/hook-toggles.d.ts.map +1 -0
- package/dist/hooks/hook-toggles.js +107 -0
- package/dist/hooks/hook-toggles.js.map +1 -0
- package/dist/hooks/pre-tool-guard-hook.d.ts +10 -0
- package/dist/hooks/pre-tool-guard-hook.d.ts.map +1 -0
- package/dist/hooks/pre-tool-guard-hook.js +105 -0
- package/dist/hooks/pre-tool-guard-hook.js.map +1 -0
- package/dist/hooks/session-start-unit-state-hook.d.ts +14 -0
- package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
- package/dist/hooks/session-start-unit-state-hook.js +60 -0
- package/dist/hooks/session-start-unit-state-hook.js.map +1 -0
- package/dist/hooks/stop-next-step-hook.d.ts +16 -0
- package/dist/hooks/stop-next-step-hook.d.ts.map +1 -0
- package/dist/hooks/stop-next-step-hook.js +43 -0
- package/dist/hooks/stop-next-step-hook.js.map +1 -0
- package/dist/hooks/sungen-project-root.d.ts +19 -0
- package/dist/hooks/sungen-project-root.d.ts.map +1 -0
- package/dist/hooks/sungen-project-root.js +130 -0
- package/dist/hooks/sungen-project-root.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/ai-skills/command-partial-includes.d.ts +15 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.d.ts.map +1 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.js +106 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.js.map +1 -0
- package/dist/orchestrator/ai-skills/commands.d.ts +5 -1
- package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -1
- package/dist/orchestrator/ai-skills/commands.js +11 -3
- package/dist/orchestrator/ai-skills/commands.js.map +1 -1
- package/dist/orchestrator/assets-drift.d.ts +5 -0
- package/dist/orchestrator/assets-drift.d.ts.map +1 -1
- package/dist/orchestrator/assets-drift.js +12 -3
- package/dist/orchestrator/assets-drift.js.map +1 -1
- package/dist/orchestrator/claude-hooks-install.d.ts +27 -0
- package/dist/orchestrator/claude-hooks-install.d.ts.map +1 -0
- package/dist/orchestrator/claude-hooks-install.js +203 -0
- package/dist/orchestrator/claude-hooks-install.js.map +1 -0
- package/dist/orchestrator/lockstep-version.d.ts +15 -5
- package/dist/orchestrator/lockstep-version.d.ts.map +1 -1
- package/dist/orchestrator/lockstep-version.js +30 -17
- package/dist/orchestrator/lockstep-version.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +6 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +24 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/add-api.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/add-flow.md +6 -26
- package/dist/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +17 -16
- package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +4 -0
- package/dist/orchestrator/templates/ai-src/commands/design.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/feedback.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/locale.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/review.md +4 -0
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +6 -8
- package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
- package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
- package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
- package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.claude.md +11 -0
- package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.codex.md +11 -0
- package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.copilot.md +11 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +1 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +4 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +8 -24
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +3 -4
- package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- package/dist/orchestrator/templates/specs-test-data.ts +4 -22
- package/dist/utils/safe-yaml-parse.d.ts +8 -0
- package/dist/utils/safe-yaml-parse.d.ts.map +1 -0
- package/dist/utils/safe-yaml-parse.js +51 -0
- package/dist/utils/safe-yaml-parse.js.map +1 -0
- package/package.json +3 -3
- package/src/basis/anchor-grammar.ts +83 -0
- package/src/basis/anchor-hash.ts +27 -0
- package/src/basis/bm51-tables.ts +65 -0
- package/src/basis/extract-spec-anchors.ts +95 -0
- package/src/basis/migration-sentinel.ts +175 -0
- package/src/basis/spec-frontmatter.ts +68 -0
- package/src/basis/spec-sections.ts +35 -0
- package/src/basis/test-basis-schema.ts +81 -0
- package/src/capabilities/discover.ts +15 -2
- package/src/capabilities/registry.ts +16 -0
- package/src/cli/case-triage-hint.ts +31 -0
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/capability.ts +1 -1
- package/src/cli/commands/contract-derive-command.ts +72 -0
- package/src/cli/commands/contract.ts +164 -0
- package/src/cli/commands/delivery.ts +106 -19
- package/src/cli/commands/gate.ts +2 -1
- package/src/cli/commands/generate.ts +16 -3
- package/src/cli/commands/journey.ts +45 -18
- package/src/cli/commands/next.ts +92 -11
- package/src/cli/commands/triage.ts +41 -3
- package/src/cli/commands/update.ts +41 -10
- package/src/cli/index.ts +24 -30
- package/src/cli/json-error-on-exit.ts +72 -0
- package/src/cli/project-sungen-invocation.ts +59 -0
- package/src/cli/run-command-hints.ts +47 -0
- package/src/cli/waiting-for-you-block.ts +27 -0
- package/src/contract/accept-opaque-steps.ts +114 -0
- package/src/contract/approve-contract.ts +101 -0
- package/src/contract/assertion-matcher-vocabulary.ts +93 -0
- package/src/contract/build-contract-verdict.ts +108 -0
- package/src/contract/build-trace-conformance.ts +103 -0
- package/src/contract/classify-contract-change.ts +92 -0
- package/src/contract/classify-contract-unit.ts +47 -0
- package/src/contract/classify-trace-conformance.ts +170 -0
- package/src/contract/contract-git.ts +106 -0
- package/src/contract/contract-hash.ts +34 -0
- package/src/contract/contract-report-file.ts +25 -0
- package/src/contract/contract-schema.ts +166 -0
- package/src/contract/contract-units.ts +52 -0
- package/src/contract/contracts-lock-file.ts +98 -0
- package/src/contract/data-normalize.ts +66 -0
- package/src/contract/derive-free-feature.ts +153 -0
- package/src/contract/field-semantic-redaction.ts +80 -0
- package/src/contract/free-feature-blocks.ts +56 -0
- package/src/contract/lock-secret-guard.ts +95 -0
- package/src/contract/lock-vs-git-head.ts +38 -0
- package/src/contract/normalize-contract-step.ts +74 -0
- package/src/contract/normalize-feature-steps.ts +93 -0
- package/src/contract/oracle-strength.ts +80 -0
- package/src/contract/project-contract-blockers.ts +38 -0
- package/src/contract/record-contract-approval.ts +110 -0
- package/src/contract/render-free-gherkin.ts +118 -0
- package/src/contract/render-trace-sentence.ts +98 -0
- package/src/contract/render-trace-target.ts +68 -0
- package/src/contract/resolve-unit-trace-identities.ts +58 -0
- package/src/contract/trace/collect-trace-evidence.ts +178 -0
- package/src/contract/trace/identity-cache.ts +61 -0
- package/src/contract/trace/read-file-once.ts +95 -0
- package/src/contract/trace/scenario-id-claims.ts +60 -0
- package/src/contract/trace/select-final-attempt.ts +77 -0
- package/src/contract/trace/snapshot-html-renderer.ts +136 -0
- package/src/contract/trace/snapshot-resolver.ts +66 -0
- package/src/contract/trace/snapshot-store.ts +151 -0
- package/src/contract/trace/target-identity.ts +104 -0
- package/src/contract/trace/test-step-join.ts +104 -0
- package/src/contract/trace/trace-binding.ts +59 -0
- package/src/contract/trace/trace-file-reader.ts +144 -0
- package/src/contract/trace/trace-fingerprint.ts +68 -0
- package/src/contract/trace/trace-freshness.ts +110 -0
- package/src/contract/trace/trace-identity-resolution.ts +77 -0
- package/src/contract/trace/trace-run-record.ts +96 -0
- package/src/contract/trace/trace-steps.ts +88 -0
- package/src/contract/trace/trace-zip-limits.ts +77 -0
- package/src/contract/trace/trace-zip-reader.ts +127 -0
- package/src/contract/verify-approval-decisions.ts +64 -0
- package/src/decisions/append-decision.ts +35 -0
- package/src/decisions/confirmed-decision-commands.ts +149 -0
- package/src/decisions/decision-file.ts +93 -0
- package/src/decisions/decision-identity.ts +73 -0
- package/src/decisions/decision-integrity-check.ts +61 -0
- package/src/decisions/decision-schema.ts +77 -0
- package/src/decisions/decision-source.ts +14 -0
- package/src/decisions/exclusive-file-create.ts +42 -0
- package/src/decisions/git-head-trust.ts +76 -0
- package/src/decisions/human-confirmation.ts +119 -0
- package/src/decisions/legacy-waiver-migration.ts +146 -0
- package/src/decisions/read-decisions.ts +61 -0
- package/src/decisions/viewpoint-baseline-adapter.ts +74 -0
- package/src/decisions/waiver-decisions.ts +56 -0
- package/src/exporters/csv-exporter.ts +12 -4
- package/src/exporters/matrix/build.ts +11 -9
- package/src/exporters/matrix/export.ts +16 -4
- package/src/exporters/matrix/gates.ts +62 -129
- package/src/exporters/matrix/render-csv.ts +3 -0
- package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
- package/src/exporters/matrix/render-xlsx.ts +3 -0
- package/src/exporters/matrix/types.ts +4 -1
- package/src/exporters/matrix/xlsx-cells.ts +1 -1
- package/src/exporters/test-data-resolver.ts +2 -10
- package/src/exporters/xlsx-exporter.ts +5 -4
- package/src/generators/gherkin-parser/index.ts +0 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +1 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -13
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -9
- package/src/generators/test-generator/code-generator.ts +17 -4
- package/src/generators/test-generator/diagnostics.ts +3 -9
- package/src/generators/test-generator/patterns/types.ts +0 -2
- package/src/generators/test-generator/step-mapper.ts +9 -56
- package/src/generators/test-generator/utils/data-resolver.ts +0 -53
- package/src/generators/test-generator/utils/selector-resolver.ts +2 -10
- package/src/graph/board-human-actions.ts +80 -0
- package/src/graph/evaluate-graph.ts +97 -0
- package/src/graph/human-action.ts +33 -0
- package/src/graph/locate-finding.ts +93 -0
- package/src/graph/manual-triage-decisions.ts +55 -0
- package/src/graph/node-decl.ts +83 -0
- package/src/graph/nodes/core-node-catalog.ts +15 -0
- package/src/graph/nodes/test-case-contract-nodes.ts +90 -0
- package/src/graph/nodes/test-case-nodes.ts +100 -0
- package/src/graph/nodes/unit-audit-nodes.ts +96 -0
- package/src/graph/nodes/unit-contract-node.ts +100 -0
- package/src/graph/nodes/unit-design-nodes.ts +89 -0
- package/src/graph/nodes/unit-execution-nodes.ts +77 -0
- package/src/graph/test-case-identity.ts +75 -0
- package/src/graph/test-case-results.ts +111 -0
- package/src/graph/test-case-satisfied.ts +58 -0
- package/src/graph/test-case-state-shape.ts +67 -0
- package/src/graph/test-case-states.ts +84 -0
- package/src/graph/unit-artifact-probes.ts +79 -0
- package/src/graph/unit-results-freshness.ts +165 -0
- package/src/graph/unit-state-facts.ts +112 -0
- package/src/harness/audit.ts +31 -18
- package/src/harness/catalog/gate-rules.yaml +647 -0
- package/src/harness/defect-draft.ts +13 -2
- package/src/harness/defects.ts +22 -9
- package/src/harness/flow-check.ts +11 -0
- package/src/harness/gates/audit-gate-terms.ts +80 -0
- package/src/harness/gates/audit-gate-verdict.ts +79 -0
- package/src/harness/gates/audit-input-hash.ts +79 -0
- package/src/harness/gates/audit-report-verification.ts +206 -0
- package/src/harness/gates/decisions-hash.ts +42 -0
- package/src/harness/gates/delivery-checklist.ts +152 -0
- package/src/harness/gates/delivery-decision-gate.ts +139 -0
- package/src/harness/gates/delivery-decision-step.ts +89 -0
- package/src/harness/gates/gate-override-report.ts +114 -0
- package/src/harness/gates/gate-override-scope.ts +66 -0
- package/src/harness/gates/gate-override-validation.ts +75 -0
- package/src/harness/gates/gate-rule-catalog.ts +82 -0
- package/src/harness/gates/gate-terms.ts +59 -0
- package/src/harness/gates/in-run-decision-check.ts +124 -0
- package/src/harness/gates/journey-gate-inputs.ts +89 -0
- package/src/harness/gates/migrated-waiver-import-bounds.ts +103 -0
- package/src/harness/gates/migrated-waiver-review.ts +121 -0
- package/src/harness/gates/resolve-rule.ts +94 -0
- package/src/harness/gates/unit-gate-overrides.ts +99 -0
- package/src/harness/journey.ts +287 -39
- package/src/harness/manifest.ts +67 -43
- package/src/harness/next-step.ts +91 -193
- package/src/harness/parse.ts +20 -2
- package/src/harness/provenance.ts +99 -11
- package/src/harness/script-check.ts +20 -5
- package/src/harness/sensors.ts +47 -40
- package/src/harness/triage-manual-classification.ts +76 -0
- package/src/harness/unit-paths.ts +19 -0
- package/src/hooks/bash-read-only-segment.ts +130 -0
- package/src/hooks/bash-write-targets.ts +195 -0
- package/src/hooks/claude-hook-entry.ts +85 -0
- package/src/hooks/claude-transcript-turn.ts +96 -0
- package/src/hooks/guard-delivery-contract-rule.ts +110 -0
- package/src/hooks/guard-protected-paths.ts +80 -0
- package/src/hooks/guard-rules.ts +117 -0
- package/src/hooks/guard-tty-spoof-rule.ts +34 -0
- package/src/hooks/hand-back-core.ts +118 -0
- package/src/hooks/hook-cli-command.ts +17 -0
- package/src/hooks/hook-registry.ts +24 -0
- package/src/hooks/hook-result-contract.ts +114 -0
- package/src/hooks/hook-toggles.ts +66 -0
- package/src/hooks/pre-tool-guard-hook.ts +67 -0
- package/src/hooks/session-start-unit-state-hook.ts +59 -0
- package/src/hooks/stop-next-step-hook.ts +38 -0
- package/src/hooks/sungen-project-root.ts +80 -0
- package/src/index.ts +1 -2
- package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
- package/src/orchestrator/ai-skills/commands.ts +17 -5
- package/src/orchestrator/assets-drift.ts +12 -5
- package/src/orchestrator/claude-hooks-install.ts +168 -0
- package/src/orchestrator/lockstep-version.ts +28 -15
- package/src/orchestrator/project-initializer.ts +22 -0
- package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/add-flow.md +6 -26
- package/src/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
- package/src/orchestrator/templates/ai-src/commands/create-test.md +17 -16
- package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/delivery.md +4 -0
- package/src/orchestrator/templates/ai-src/commands/design.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/feedback.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/locale.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/review.md +4 -0
- package/src/orchestrator/templates/ai-src/commands/run-test.md +6 -8
- package/src/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
- package/src/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
- package/src/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
- package/src/orchestrator/templates/ai-src/partials/waiting-for-you.claude.md +11 -0
- package/src/orchestrator/templates/ai-src/partials/waiting-for-you.codex.md +11 -0
- package/src/orchestrator/templates/ai-src/partials/waiting-for-you.copilot.md +11 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +1 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +4 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +8 -24
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +3 -4
- package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- package/src/orchestrator/templates/specs-test-data.ts +4 -22
- package/src/utils/safe-yaml-parse.ts +44 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/expanded-assertion.hbs +0 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/date-picker-unsupported.hbs +0 -5
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/expanded-assertion.hbs +0 -12
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/expanded-assertion.hbs +0 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/date-picker-unsupported.hbs +0 -5
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/expanded-assertion.hbs +0 -12
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which paths the pre-tool guard protects, and how (P0.1 + review fixes H2, L5).
|
|
3
|
+
*
|
|
4
|
+
* approval records — DENY an agent write:
|
|
5
|
+
* qa/**\/contracts.lock.yaml
|
|
6
|
+
* qa/decisions/<id>.yaml (project decisions, phase 03)
|
|
7
|
+
* qa/<screens|flows|api[/flows]>/<unit>/decisions/<id>.yaml (unit decisions)
|
|
8
|
+
* .sungen/journey/<slug>.state.json (legacy waivers and their dual-written copy, DOS-13.5)
|
|
9
|
+
* .sungen/viewpoint-baseline.json (the accepted viewpoint declaration)
|
|
10
|
+
* — the CLI still writes the last two itself; the guard only judges an agent's tool calls
|
|
11
|
+
* trace evidence — DENY an agent write (phase 08; the verdict trusts it only as far as this guard and git review):
|
|
12
|
+
* .sungen/trace-identity/** (the driver's identity answers — written by the CLI)
|
|
13
|
+
* test-results[-free]/sungen-run-record*.json (run id + test-file hashes — written by the Playwright reporter;
|
|
14
|
+
* test-results-free/ is the free run's own outputDir, #941)
|
|
15
|
+
* hook control — ASK before an agent write (the agent must not switch its own guard off):
|
|
16
|
+
* qa/sungen.config.yaml · .claude/settings.json · .claude/settings.local.json ·
|
|
17
|
+
* .claude/hooks/sungen-hook.cjs
|
|
18
|
+
*
|
|
19
|
+
* The human path for the hook-control files is editing them by hand, then `sungen update`.
|
|
20
|
+
* A `decisions/` folder anywhere else (e.g. `requirements/decisions/notes.md`) is not a record.
|
|
21
|
+
*/
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
import { findSungenRoot } from './sungen-project-root';
|
|
24
|
+
|
|
25
|
+
const LOCK = /^qa\/(?:.+\/)?contracts\.lock\.yaml$/i;
|
|
26
|
+
const DECISION = /^qa\/(?:(?:screens|flows|api(?:\/flows)?)\/[^/]+\/)?decisions\/[^/]+/i;
|
|
27
|
+
const APPROVAL_STATE = /^\.sungen\/(?:journey\/[^/]+\.state\.json|viewpoint-baseline\.json)$/i;
|
|
28
|
+
/** Trace evidence (phase 08, DOS-14.6): the driver's identity answers and the run records. */
|
|
29
|
+
const TRACE_EVIDENCE = /^(?:\.sungen\/trace-identity\/.+|test-results(?:-free)?\/sungen-run-record(?:\.[A-Za-z0-9._-]+)?\.json)$/i;
|
|
30
|
+
const HOOK_CONTROL = ['qa/sungen.config.yaml', '.claude/settings.json', '.claude/settings.local.json', '.claude/hooks/sungen-hook.cjs'];
|
|
31
|
+
|
|
32
|
+
/** The same paths as they appear INSIDE a command string (e.g. in `bash -c "…"` or `python -c "…"`). */
|
|
33
|
+
const MENTIONS = [
|
|
34
|
+
/contracts\.lock\.yaml/i,
|
|
35
|
+
/(?:^|[^\w.-])qa\/(?:(?:screens|flows|api(?:\/flows)?)\/[^/\s'"]+\/)?decisions\//i,
|
|
36
|
+
/\.sungen\/(?:journey\/[^/\s'"]+\.state\.json|viewpoint-baseline\.json)/i,
|
|
37
|
+
/(?:^|[/\s'"=])(?:qa\/sungen\.config\.yaml|\.claude\/settings(?:\.local)?\.json|\.claude\/hooks\/sungen-hook\.cjs)/i,
|
|
38
|
+
/\.sungen\/trace-identity\//i,
|
|
39
|
+
/(?:^|[/\s'"=])sungen-run-record(?:\.[A-Za-z0-9._-]+)?\.json/i,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const posix = (p: string): string => p.split(path.sep).join('/');
|
|
43
|
+
|
|
44
|
+
/** `abs` relative to its sungen project, when it is an approval record (lock, decision, approval state); else null. */
|
|
45
|
+
export function approvalRecordPath(abs: string): string | null {
|
|
46
|
+
const root = findSungenRoot(path.dirname(abs));
|
|
47
|
+
if (!root) return null;
|
|
48
|
+
const rel = posix(path.relative(root, abs));
|
|
49
|
+
return LOCK.test(rel) || DECISION.test(rel) || APPROVAL_STATE.test(rel) ? rel : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** `abs` relative to its sungen project, when it is a trace evidence store; else null. */
|
|
53
|
+
export function traceEvidencePath(abs: string): string | null {
|
|
54
|
+
const root = findSungenRoot(path.dirname(abs));
|
|
55
|
+
if (!root) return null;
|
|
56
|
+
const rel = posix(path.relative(root, abs));
|
|
57
|
+
return TRACE_EVIDENCE.test(rel) ? rel : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The hook-control file `abs` is (as its project-relative name), or null. */
|
|
61
|
+
export function hookControlPath(abs: string): string | null {
|
|
62
|
+
const p = posix(path.resolve(abs)).toLowerCase();
|
|
63
|
+
return HOOK_CONTROL.find((rel) => p.endsWith(`/${rel}`)) ?? null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Names near a protected path — for a word the shell expands (`$Q/decisions/x`), whose real text is unknown. */
|
|
67
|
+
const NEAR_PROTECTED = /qa|\.sungen|\.claude|decision|contracts|lock\.ya?ml|run-record|trace-identity|viewpoint|journey|settings|sungen-hook|sungen\.config/i;
|
|
68
|
+
|
|
69
|
+
/** A word the shell rewrites at runtime (`$…`, a literal `\`) that sits near a protected name: the guard cannot read it. */
|
|
70
|
+
export const opaqueNearProtected = (word: string): boolean => /[$\\]/.test(word) && NEAR_PROTECTED.test(word);
|
|
71
|
+
|
|
72
|
+
/** Does this text (a command word, a heredoc, a whole command line) name a protected path as written? */
|
|
73
|
+
export const mentionsProtectedText = (text: string): boolean => MENTIONS.some((re) => re.test(text));
|
|
74
|
+
|
|
75
|
+
/** Does this command word name a protected path — as text, or once resolved against `dir`? */
|
|
76
|
+
export function mentionsProtectedPath(word: string, dir: string): boolean {
|
|
77
|
+
if (mentionsProtectedText(word)) return true;
|
|
78
|
+
const abs = path.resolve(dir, word);
|
|
79
|
+
return approvalRecordPath(abs) !== null || traceEvidencePath(abs) !== null || hookControlPath(abs) !== null;
|
|
80
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pre-tool guard's rules. Each looks at one normalized tool call and returns a decision or
|
|
3
|
+
* null; pre-tool-guard-hook.ts runs them in order (first deny wins, else first ask).
|
|
4
|
+
*
|
|
5
|
+
* protected-approval-paths DENY a precise write (Edit/Write/MultiEdit, or Bash `>`, `tee`,
|
|
6
|
+
* `sed -i`) to a contract lock, a decision record, approval state or
|
|
7
|
+
* trace evidence (.sungen/trace-identity, test-results[-free]/sungen-run-record*)
|
|
8
|
+
* protected-hook-control ASK the same writes to the files that control the hooks, so the
|
|
9
|
+
* agent cannot switch its own guard off unasked
|
|
10
|
+
* bash-protected-mention ASK any other Bash segment that names a protected path and is not
|
|
11
|
+
* read-only (cp, mv, ln, install, rm, bash -c, python -c, perl -i…);
|
|
12
|
+
* a read-only one too when its output is piped into a non-read-only
|
|
13
|
+
* command, or the line has command substitution or sets shell state
|
|
14
|
+
* (`X=…`, export, alias, a function…); a heredoc / here-string
|
|
15
|
+
* that names one; and a write target with a glob or brace (#968 — bash-read-only-segment.ts lists what counts
|
|
16
|
+
* as read-only, and the accepted floor)
|
|
17
|
+
*
|
|
18
|
+
* A floor, not a sandbox: a path built at runtime, a symlink, or a script file that writes without
|
|
19
|
+
* naming the path on the command line is out of reach by design (the full list of what the floor
|
|
20
|
+
* does not catch is in bash-read-only-segment.ts; release-time decision integrity is the backstop). Each rule only matches strings,
|
|
21
|
+
* so each fails open (`onError: 'no-op'`); a rule that READS project state must declare `'ask'`.
|
|
22
|
+
*/
|
|
23
|
+
import * as path from 'path';
|
|
24
|
+
import type { HookContext, HookResult } from './hook-result-contract';
|
|
25
|
+
import { BashSegment, bashSegments } from './bash-write-targets';
|
|
26
|
+
import { isReadOnlySegment, lineKeepsReadOnlyExemption, pipelineStaysReadOnly } from './bash-read-only-segment';
|
|
27
|
+
import {
|
|
28
|
+
approvalRecordPath, hookControlPath, mentionsProtectedPath, mentionsProtectedText, opaqueNearProtected, traceEvidencePath,
|
|
29
|
+
} from './guard-protected-paths';
|
|
30
|
+
|
|
31
|
+
/** One tool call, normalized from the untrusted payload. */
|
|
32
|
+
export interface GuardedCall {
|
|
33
|
+
tool: string;
|
|
34
|
+
/** Absolute target paths of a file tool (Edit/Write/MultiEdit). */
|
|
35
|
+
filePaths: string[];
|
|
36
|
+
bashCommand?: string;
|
|
37
|
+
cwd: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface GuardRule {
|
|
41
|
+
id: string;
|
|
42
|
+
/** What a throw inside `check` becomes: `ask` for a rule that reads state, `no-op` otherwise. */
|
|
43
|
+
onError: 'ask' | 'no-op';
|
|
44
|
+
/** A decision for this call, or null when the rule has nothing to say about it. */
|
|
45
|
+
check(call: GuardedCall, ctx: HookContext): HookResult | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The files a call writes precisely: a file tool's target, or Bash's `>` / `tee` / `sed -i` targets. */
|
|
49
|
+
const preciseWrites = (call: GuardedCall): string[] =>
|
|
50
|
+
call.tool === 'Bash' ? bashSegments(call.bashCommand ?? '', call.cwd).flatMap((s) => s.writes) : call.filePaths;
|
|
51
|
+
|
|
52
|
+
export const protectedApprovalPaths: GuardRule = {
|
|
53
|
+
id: 'protected-approval-paths',
|
|
54
|
+
onError: 'no-op',
|
|
55
|
+
check(call) {
|
|
56
|
+
const writes = preciseWrites(call);
|
|
57
|
+
const rel = writes.map(approvalRecordPath).find((r): r is string => r !== null);
|
|
58
|
+
if (rel) {
|
|
59
|
+
return {
|
|
60
|
+
status: 'deny',
|
|
61
|
+
denyReason: `sungen: \`${rel}\` is an approval record — contract locks, decisions, waivers and the accepted `
|
|
62
|
+
+ 'viewpoint baseline change only through a human at a terminal, never by an agent edit. If this change is '
|
|
63
|
+
+ 'intended, stop and ask the user to make it themselves.',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const evidence = writes.map(traceEvidencePath).find((r): r is string => r !== null);
|
|
67
|
+
return evidence ? {
|
|
68
|
+
status: 'deny',
|
|
69
|
+
denyReason: `sungen: \`${evidence}\` is trace evidence — only \`sungen contract check --trace\` / \`contract derive\` `
|
|
70
|
+
+ 'and the Playwright run-record reporter write it. Re-run the free tests or the CLI instead of editing it.',
|
|
71
|
+
} : null;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const protectedHookControl: GuardRule = {
|
|
76
|
+
id: 'protected-hook-control',
|
|
77
|
+
onError: 'no-op',
|
|
78
|
+
check(call) {
|
|
79
|
+
const rel = preciseWrites(call).map(hookControlPath).find((r): r is string => r !== null);
|
|
80
|
+
return rel ? {
|
|
81
|
+
status: 'ask',
|
|
82
|
+
askReason: `sungen: \`${rel}\` controls sungen's hooks and guard, so an agent does not change it on its own. `
|
|
83
|
+
+ 'Allow only if you asked for exactly this change — the usual path is to edit it by hand, then run `sungen update`.',
|
|
84
|
+
} : null;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/** A heredoc or here-string: its body is another program's input, which the segments do not see. */
|
|
89
|
+
const HEREDOC = /<</;
|
|
90
|
+
|
|
91
|
+
export const bashProtectedMention: GuardRule = {
|
|
92
|
+
id: 'bash-protected-mention',
|
|
93
|
+
onError: 'no-op',
|
|
94
|
+
check(call) {
|
|
95
|
+
const command = call.tool === 'Bash' ? call.bashCommand : undefined;
|
|
96
|
+
if (!command) return null;
|
|
97
|
+
const segs = bashSegments(command, call.cwd);
|
|
98
|
+
const lineExempt = lineKeepsReadOnlyExemption(command, segs);
|
|
99
|
+
// the command word too: zsh `for f (qa/decisions/*) …` puts the list where a command name goes
|
|
100
|
+
const names = (s: BashSegment): boolean => [s.cmdPath, ...s.words, ...s.assigns.map((a) => a.slice(a.indexOf('=') + 1))]
|
|
101
|
+
.some((w) => w !== '' && (mentionsProtectedPath(w, s.dir) || opaqueNearProtected(w)));
|
|
102
|
+
const seg = segs.find((s, i) => names(s) && !(lineExempt && isReadOnlySegment(s) && pipelineStaysReadOnly(segs, i)));
|
|
103
|
+
// a write target the shell expands (`> q*/decisions/x`, `--output=qa/{a,b}`, `> $Q/decisions/x`, a literal `\`),
|
|
104
|
+
// or a relative one after a `cd` the guard could not follow, cannot be judged by its text
|
|
105
|
+
const opaqueWrite = segs.some((s) => s.writesInUnknownDir || s.writes.some((w) => {
|
|
106
|
+
const rel = path.relative(s.dir, w);
|
|
107
|
+
return /[*?[{\\]/.test(rel) || opaqueNearProtected(rel);
|
|
108
|
+
}));
|
|
109
|
+
if (!seg && !opaqueWrite && !(HEREDOC.test(command) && mentionsProtectedText(command))) return null;
|
|
110
|
+
const what = seg ? (seg.cmd ? `\`${seg.cmd}\` command` : 'variable') : opaqueWrite ? 'write target the guard cannot resolve' : 'heredoc';
|
|
111
|
+
return {
|
|
112
|
+
status: 'ask',
|
|
113
|
+
askReason: `sungen: this ${what} names a contract lock, a decision record, approval state or a hook-control file, `
|
|
114
|
+
+ 'and the guard cannot tell whether it writes it. Allow only if you asked for exactly this.',
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guard rule `tty-spoof` (phase 06, M5): ASK before an agent runs a human-confirmed sungen command
|
|
3
|
+
* through a terminal emulator.
|
|
4
|
+
*
|
|
5
|
+
* `contract approve`, `journey --waive`, `journey --decide`, `journey --signoff`, `triage --as test-defect|environment`
|
|
6
|
+
* (#928: a red classified away from the product-defect path) and `delivery --accept-in-run` (delivering past
|
|
7
|
+
* decisions recorded during the run) refuse without a TTY (DOS-13.4), and
|
|
8
|
+
* an agent's Bash has none — unless it borrows one: `script`, `expect`, `unbuffer`, or a pty from
|
|
9
|
+
* Python/Perl/zsh. Then the prompt sees a "terminal" and a piped phrase reads as a human typing it.
|
|
10
|
+
* This rule spots the pair in the raw command (quotes included, so `bash -c "…"` and `expect -c '…'`
|
|
11
|
+
* are covered) and hands the call to the user. A floor, not a sandbox: a script file that does the
|
|
12
|
+
* same without naming either on the command line is out of reach by design. String match only, so
|
|
13
|
+
* it fails open.
|
|
14
|
+
*/
|
|
15
|
+
import type { GuardRule } from './guard-rules';
|
|
16
|
+
|
|
17
|
+
const TTY_EMULATOR = /\b(?:script|unbuffer|expect|pty|zpty|openpty|forkpty|ptyprocess)\b/i;
|
|
18
|
+
const HUMAN_GATED = /\bsungen\b[\s\S]*?\b(?:contract['",\s]+approve\b|journey\b[\s\S]*?--(?:waive|decide|signoff)\b|triage\b[\s\S]*?--as[\s='",]+(?:test-defect|environment)\b|delivery\b[\s\S]*?--accept-in-run\b)/i;
|
|
19
|
+
|
|
20
|
+
export const ttySpoof: GuardRule = {
|
|
21
|
+
id: 'tty-spoof',
|
|
22
|
+
onError: 'no-op',
|
|
23
|
+
check(call) {
|
|
24
|
+
const cmd = call.tool === 'Bash' ? call.bashCommand : undefined;
|
|
25
|
+
if (!cmd || !TTY_EMULATOR.test(cmd) || !HUMAN_GATED.test(cmd)) return null;
|
|
26
|
+
return {
|
|
27
|
+
status: 'ask',
|
|
28
|
+
askReason: 'sungen: this runs a human-confirmed sungen action (`contract approve` / `journey --waive|--decide|--signoff` / '
|
|
29
|
+
+ '`triage --as test-defect|environment` / `delivery --accept-in-run`) through a terminal '
|
|
30
|
+
+ 'emulator (script / expect / unbuffer / pty). Those confirmations exist so a PERSON at a terminal decides — allow only if you '
|
|
31
|
+
+ 'are that person and asked for exactly this; otherwise run the command yourself in your own terminal.',
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hand-back, written once (P0.1, takumi T1). Two callers share it:
|
|
3
|
+
*
|
|
4
|
+
* - the CLI exit (cli/index.ts only wires it): the one-line `Next: sungen next <unit>` every
|
|
5
|
+
* command ends with — on stdout after a success, on STDERR after a failure or under --json/--grep;
|
|
6
|
+
* - the Claude Code Stop hook (stop-next-step-hook.ts): the same pointer plus what `sungen next`
|
|
7
|
+
* derives right now, so a session that ended without a hand-back gets one injected.
|
|
8
|
+
*
|
|
9
|
+
* #597 skipped exit code 1 ("the error is the actionable thing") and set the hand-back only in
|
|
10
|
+
* commander's preAction, so a usage error — which never reaches an action — and `main().catch`
|
|
11
|
+
* ended in silence. The state now starts the moment parsing does; the action only refines it.
|
|
12
|
+
*/
|
|
13
|
+
import { sungenInvocation, withProjectInvocationInText } from '../cli/project-sungen-invocation';
|
|
14
|
+
import { waitingForYouLines } from '../cli/waiting-for-you-block';
|
|
15
|
+
import { resolveUnitCandidates } from '../cli/resolve-unit';
|
|
16
|
+
import { readUnitState, NextStep, UnitState } from '../harness/next-step';
|
|
17
|
+
import { NO_OP, HookContext, HookInput, HookResult } from './hook-result-contract';
|
|
18
|
+
import { findSungenRoot, findUnit, nearestUnit, UnitRef } from './sungen-project-root';
|
|
19
|
+
|
|
20
|
+
/** `hook` speaks JSON to Claude Code on stdout: it never prints a hand-back. */
|
|
21
|
+
const SILENT_COMMANDS = new Set(['hook']);
|
|
22
|
+
/** `next` IS the answer when it succeeds; a failed `next` answered nothing and hands back like any failure (#937). */
|
|
23
|
+
const ANSWER_COMMANDS = new Set(['next']);
|
|
24
|
+
const UNIT_FLAG = /^(?:--screen|-s|--flow|--area|--api)$/;
|
|
25
|
+
const UNIT_FLAG_EQ = /^--(?:screen|flow|area|api)=(.+)$/;
|
|
26
|
+
/** A unit id is a name or `flows/<name>` — anything else is not offered back to the operator. */
|
|
27
|
+
const SAFE_UNIT = /^[\w.-]+(?:\/[\w.-]+)?$/;
|
|
28
|
+
|
|
29
|
+
/** `invocation`: how the project runs sungen (`npx sungen` with a local install, #939). */
|
|
30
|
+
export function handBackLine(unit?: string, invocation = 'sungen'): string {
|
|
31
|
+
return `Next: ${invocation} next${unit ? ` ${unit}` : ''}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CliHandBackState { unit?: string; machine: boolean; silent: boolean; answer: boolean; actionRan: boolean }
|
|
35
|
+
|
|
36
|
+
/** The default, set before commander parses — what a usage error or a fatal error hands back. */
|
|
37
|
+
export function initialHandBackState(argv: string[]): CliHandBackState {
|
|
38
|
+
const args = argv.slice(2);
|
|
39
|
+
let unit: string | undefined;
|
|
40
|
+
for (let i = 0; i < args.length; i++) {
|
|
41
|
+
const eq = args[i].match(UNIT_FLAG_EQ);
|
|
42
|
+
if (eq) unit ??= eq[1];
|
|
43
|
+
else if (UNIT_FLAG.test(args[i]) && args[i + 1] && !args[i + 1].startsWith('-')) unit ??= args[++i];
|
|
44
|
+
}
|
|
45
|
+
const command = args.find((a) => !a.startsWith('-')) ?? '';
|
|
46
|
+
return { unit, machine: args.includes('--json') || args.includes('--grep'), silent: SILENT_COMMANDS.has(command), answer: ANSWER_COMMANDS.has(command), actionRan: false };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** commander's preAction: the action now KNOWS its unit and output mode. */
|
|
50
|
+
export function onCliAction(state: CliHandBackState, name: string, opts: Record<string, unknown>, args: unknown[]): void {
|
|
51
|
+
state.actionRan = true;
|
|
52
|
+
if (SILENT_COMMANDS.has(name)) { state.silent = true; return; }
|
|
53
|
+
state.answer = ANSWER_COMMANDS.has(name);
|
|
54
|
+
const flagged = [opts.screen, opts.flow, opts.area, opts.api].find((v) => typeof v === 'string' && v) as string | undefined;
|
|
55
|
+
state.unit = flagged ?? (typeof args?.[0] === 'string' ? args[0] : undefined);
|
|
56
|
+
// `--grep` is machine output too: callers capture it with `$(…)` (#836).
|
|
57
|
+
state.machine = opts.json === true || opts.grep === true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* What to print as the process exits, or null for nothing. A positional that is not a unit on
|
|
62
|
+
* disk (`capability add mock` → `mock`) is dropped rather than offered as `sungen next mock`.
|
|
63
|
+
*/
|
|
64
|
+
export function exitHandBack(state: CliHandBackState, code: number, cwd: string): { stream: 'stdout' | 'stderr'; text: string } | null {
|
|
65
|
+
if (state.silent) return null;
|
|
66
|
+
if (state.answer && code === 0) return null; // a successful `next` is the hand-back itself
|
|
67
|
+
if (!state.actionRan && code === 0) return null; // --help / --version printed what was asked
|
|
68
|
+
const unit = state.unit && SAFE_UNIT.test(state.unit) && !state.unit.split('/').includes('..')
|
|
69
|
+
&& resolveUnitCandidates(state.unit, cwd).length > 0 ? state.unit : undefined;
|
|
70
|
+
const line = handBackLine(unit, sungenInvocation(findSungenRoot(cwd) ?? cwd));
|
|
71
|
+
if (code === 1) return { stream: 'stderr', text: `${line}\n` }; // after the error, one line
|
|
72
|
+
return { stream: state.machine ? 'stderr' : 'stdout', text: `\n${line}\n` };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Wire the hand-back to process exit; returns the preAction callback. Never masks the real exit. */
|
|
76
|
+
export function attachCliHandBack(argv: string[], cwd: string): { onAction: (name: string, opts: Record<string, unknown>, args: unknown[]) => void } {
|
|
77
|
+
const state = initialHandBackState(argv);
|
|
78
|
+
process.on('exit', (code) => {
|
|
79
|
+
try {
|
|
80
|
+
const out = exitHandBack(state, code, cwd);
|
|
81
|
+
if (out) process[out.stream].write(out.text);
|
|
82
|
+
} catch { /* the hand-back is advice; the exit code is the contract */ }
|
|
83
|
+
});
|
|
84
|
+
return { onAction: (name, opts, args) => onCliAction(state, name, opts, args) };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// --- the hook-shaped core -----------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
const MARK: Record<NextStep['kind'], string> = { do: '→', blocked: '⛔', optional: '○', decide: '⚖' };
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* `sungen next` rendered compactly for an agent's context; commands — the step's own and those quoted in
|
|
93
|
+
* its reason (#966) — as `root` runs sungen, the way `sungen next` prints them.
|
|
94
|
+
*/
|
|
95
|
+
export function formatSteps(steps: NextStep[], max = 4, root?: string): string[] {
|
|
96
|
+
const asRun = (text: string): string => (root ? withProjectInvocationInText(text, root) : text);
|
|
97
|
+
return steps.slice(0, max).map((st) =>
|
|
98
|
+
` ${MARK[st.kind]} ${asRun(st.command)}${st.kind === 'decide' ? ' (a QA decision — ask, do not run)' : ''}\n ${asRun(st.because)}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The unit a session is on (a named one when it exists, else the nearest) and its derived state. */
|
|
102
|
+
export function locateUnitState(ctx: HookContext, preferred?: string): { root: string; unit: UnitRef; state: UnitState } | null {
|
|
103
|
+
const root = findSungenRoot(ctx.cwd);
|
|
104
|
+
if (!root) return null;
|
|
105
|
+
const unit = (preferred && SAFE_UNIT.test(preferred) ? findUnit(root, preferred) : null) ?? nearestUnit(root, ctx.cwd);
|
|
106
|
+
return unit ? { root, unit, state: readUnitState(root, unit.dir, unit.name, unit.kind) } : null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Hook core: the next steps for the session's unit (input.unit, else the nearest) as context, then what waits for a human (#954). */
|
|
110
|
+
export function run(input: HookInput, ctx: HookContext): HookResult {
|
|
111
|
+
const found = locateUnitState(ctx, typeof input.unit === 'string' ? input.unit : undefined);
|
|
112
|
+
if (!found || found.state.steps.length === 0) return NO_OP;
|
|
113
|
+
return {
|
|
114
|
+
status: 'context',
|
|
115
|
+
output: [`${handBackLine(found.unit.name, sungenInvocation(found.root))} — derived from what the project holds now:`, ...formatSteps(found.state.steps, 4, found.root),
|
|
116
|
+
...waitingForYouLines(found.state.humanActions, found.root, found.state.humanActionsHeldBy, found.state.humanActionsHeldByCommand)].join('\n'),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sungen hook <name>` — hidden: runs one Claude Code hook through the normal CLI (event JSON on
|
|
3
|
+
* stdin, decision JSON on stdout). The installed hooks call the dist entry directly for speed; this
|
|
4
|
+
* path is for debugging a hook by hand and for dev checkouts that run sungen from source.
|
|
5
|
+
* The CLI hand-back skips it, so stdout stays pure JSON.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
|
|
9
|
+
export function registerHookCommand(program: Command): void {
|
|
10
|
+
program
|
|
11
|
+
.command('hook <name>', { hidden: true })
|
|
12
|
+
.description('Run one Claude Code hook: event JSON on stdin, decision JSON on stdout (internal)')
|
|
13
|
+
.action(async (name: string) => {
|
|
14
|
+
const { main } = await import('./claude-hook-entry');
|
|
15
|
+
await main(name);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every sungen Claude Code hook, by name. Modules load ON DEMAND: the guard runs on every Edit,
|
|
3
|
+
* Write and Bash call and must not pay for the next-step reader the Stop hook needs.
|
|
4
|
+
*/
|
|
5
|
+
import { HOOK_NAMES, HookName, SungenHook } from './hook-result-contract';
|
|
6
|
+
|
|
7
|
+
const LOADERS: Record<HookName, () => SungenHook> = {
|
|
8
|
+
'stop-next-step': () => (require('./stop-next-step-hook') as typeof import('./stop-next-step-hook')).stopNextStepHook,
|
|
9
|
+
'session-start-unit-state': () =>
|
|
10
|
+
(require('./session-start-unit-state-hook') as typeof import('./session-start-unit-state-hook')).sessionStartUnitStateHook,
|
|
11
|
+
'pre-tool-guard': () => (require('./pre-tool-guard-hook') as typeof import('./pre-tool-guard-hook')).preToolGuardHook,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const isHookName = (name: string): name is HookName => (HOOK_NAMES as readonly string[]).includes(name);
|
|
15
|
+
|
|
16
|
+
/** The hook with this name, or null for anything else (an unknown name is never an error). */
|
|
17
|
+
export function loadHook(name: string): SungenHook | null {
|
|
18
|
+
return isHookName(name) ? LOADERS[name]() : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** All hooks — for the installer, which reads their `meta`. */
|
|
22
|
+
export function allHooks(): SungenHook[] {
|
|
23
|
+
return HOOK_NAMES.map((n) => LOADERS[n]());
|
|
24
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hook contract (P0.1, takumi T1 dual-mode): one PURE core per hook, many callers.
|
|
3
|
+
*
|
|
4
|
+
* run(input, ctx) → HookResult no stdin read, no stdout write, no process.exit
|
|
5
|
+
* meta { events, matchers?, timeout? } read once at INSTALL time, never at run time
|
|
6
|
+
*
|
|
7
|
+
* The same `run` serves the Claude Code entry (claude-hook-entry.ts), the hidden `sungen hook`
|
|
8
|
+
* command, and later MCP / VS Code — so the decision logic is written once. Every caller goes
|
|
9
|
+
* through `runHook`, which owns the three properties no hook may get wrong on its own:
|
|
10
|
+
*
|
|
11
|
+
* - kill-switch / per-hook toggle → `no-op` before the hook runs at all
|
|
12
|
+
* - FAIL OPEN → a throw becomes the hook's `onError` result (default no-op)
|
|
13
|
+
* - shape check → a malformed result is `no-op`; a deny/ask without a reason
|
|
14
|
+
* never blocks anyone
|
|
15
|
+
*/
|
|
16
|
+
import { isHookEnabled } from './hook-toggles';
|
|
17
|
+
|
|
18
|
+
export const HOOK_NAMES = ['stop-next-step', 'session-start-unit-state', 'pre-tool-guard'] as const;
|
|
19
|
+
export type HookName = (typeof HOOK_NAMES)[number];
|
|
20
|
+
|
|
21
|
+
export type HookStatus = 'ok' | 'deny' | 'ask' | 'context' | 'no-op';
|
|
22
|
+
export interface HookResult {
|
|
23
|
+
status: HookStatus;
|
|
24
|
+
/** Context text for the agent (status `context`). */
|
|
25
|
+
output?: string;
|
|
26
|
+
/** Why a tool call is refused (status `deny`). */
|
|
27
|
+
denyReason?: string;
|
|
28
|
+
/** Why the user must confirm a tool call (status `ask`). */
|
|
29
|
+
askReason?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ClaudeHookEvent = 'SessionStart' | 'Stop' | 'PreToolUse';
|
|
33
|
+
export interface HookMeta {
|
|
34
|
+
events: ClaudeHookEvent[];
|
|
35
|
+
/** Per-event matcher written into settings.json (e.g. `{ PreToolUse: 'Edit|Write' }`). */
|
|
36
|
+
matchers?: Partial<Record<ClaudeHookEvent, string>>;
|
|
37
|
+
/** settings.json timeout in SECONDS — Claude Code kills the hook after it (and fails open). */
|
|
38
|
+
timeout?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The event payload: untrusted JSON from stdin. Each hook checks every field where it reads it. */
|
|
42
|
+
export type HookInput = Record<string, unknown>;
|
|
43
|
+
|
|
44
|
+
export interface HookContext {
|
|
45
|
+
/** The environment the hook sees — injected, so tests never mutate process.env. */
|
|
46
|
+
env: Record<string, string | undefined>;
|
|
47
|
+
/** Where the session runs: the payload `cwd`, else CLAUDE_PROJECT_DIR, else process.cwd(). */
|
|
48
|
+
cwd: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface SungenHook {
|
|
52
|
+
name: HookName;
|
|
53
|
+
meta: HookMeta;
|
|
54
|
+
/** What a throw inside `run` becomes. A hook that READS state to decide must not deny blind. */
|
|
55
|
+
onError?: HookResult;
|
|
56
|
+
/** What a payload too large to read becomes (default no-op). A guard must not treat it as a pass. */
|
|
57
|
+
onUnreadableInput?: HookResult;
|
|
58
|
+
run(input: HookInput, ctx: HookContext): HookResult;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const NO_OP: HookResult = { status: 'no-op' };
|
|
62
|
+
|
|
63
|
+
const nonEmpty = (v: unknown): v is string => typeof v === 'string' && v.trim().length > 0;
|
|
64
|
+
|
|
65
|
+
/** A result Claude Code can act on safely; anything else is treated as `no-op`. */
|
|
66
|
+
export function isValidHookResult(r: unknown): r is HookResult {
|
|
67
|
+
if (!r || typeof r !== 'object') return false;
|
|
68
|
+
const { status, output, denyReason, askReason } = r as HookResult;
|
|
69
|
+
switch (status) {
|
|
70
|
+
case 'ok': case 'no-op': return true;
|
|
71
|
+
case 'context': return nonEmpty(output);
|
|
72
|
+
case 'deny': return nonEmpty(denyReason);
|
|
73
|
+
case 'ask': return nonEmpty(askReason);
|
|
74
|
+
default: return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The one way to call a hook: toggles first, then the hook, failing open on any error. */
|
|
79
|
+
export function runHook(hook: SungenHook, input: HookInput, ctx: HookContext): HookResult {
|
|
80
|
+
try {
|
|
81
|
+
if (!isHookEnabled(hook.name, ctx)) return NO_OP;
|
|
82
|
+
const r = hook.run(input, ctx);
|
|
83
|
+
return isValidHookResult(r) ? r : NO_OP;
|
|
84
|
+
} catch {
|
|
85
|
+
return hook.onError && isValidHookResult(hook.onError) ? hook.onError : NO_OP;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The payload could not be read (too large): the hook's declared answer, toggles still first. */
|
|
90
|
+
export function runHookOnUnreadableInput(hook: SungenHook, ctx: HookContext): HookResult {
|
|
91
|
+
try {
|
|
92
|
+
if (!isHookEnabled(hook.name, ctx)) return NO_OP;
|
|
93
|
+
return hook.onUnreadableInput && isValidHookResult(hook.onUnreadableInput) ? hook.onUnreadableInput : NO_OP;
|
|
94
|
+
} catch {
|
|
95
|
+
return NO_OP;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* HookResult → the stdout Claude Code reads (exit code is always 0). `deny`/`ask` exist only for
|
|
101
|
+
* PreToolUse; on any other event they degrade to no decision rather than to an invented one.
|
|
102
|
+
*/
|
|
103
|
+
export function toClaudeStdout(event: string, r: HookResult): string {
|
|
104
|
+
if (r.status === 'context' && nonEmpty(r.output)) {
|
|
105
|
+
return JSON.stringify({ hookSpecificOutput: { hookEventName: event, additionalContext: r.output } });
|
|
106
|
+
}
|
|
107
|
+
if (event === 'PreToolUse' && (r.status === 'deny' || r.status === 'ask')) {
|
|
108
|
+
const reason = r.status === 'deny' ? r.denyReason : r.askReason;
|
|
109
|
+
if (nonEmpty(reason)) {
|
|
110
|
+
return JSON.stringify({ hookSpecificOutput: { hookEventName: event, permissionDecision: r.status, permissionDecisionReason: reason } });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return '{}';
|
|
114
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning sungen's Claude Code hooks off — all of them, or one at a time.
|
|
3
|
+
*
|
|
4
|
+
* SUNGEN_HOOKS=off kill-switch, every hook is a no-op
|
|
5
|
+
* SUNGEN_HOOK_<NAME>=off one hook (e.g. SUNGEN_HOOK_PRE_TOOL_GUARD=off)
|
|
6
|
+
* qa/sungen.config.yaml hooks: false persistent, for the whole project
|
|
7
|
+
* hooks: { stop-next-step: false } persistent, one hook
|
|
8
|
+
*
|
|
9
|
+
* The env forms can live in the shell or in `.claude/settings.json` → `"env": { … }` (Claude Code
|
|
10
|
+
* applies that block to the session, so hook processes inherit it). The config file is also what
|
|
11
|
+
* `sungen init` / `sungen update` read: a hook it turns off is removed from settings.json.
|
|
12
|
+
*
|
|
13
|
+
* At RUN time the config file only switches off the advisory hooks. The guard ignores it — an
|
|
14
|
+
* agent could otherwise write `hooks: false` and walk past the guard it is subject to; only the real
|
|
15
|
+
* environment switches the guard off. (An agent edit of the config file or settings.json asks the
|
|
16
|
+
* user first — see guard-rules.ts.) The human path is editing these files by hand, then
|
|
17
|
+
* `sungen update`, which removes a turned-off hook's entry from settings.json.
|
|
18
|
+
*
|
|
19
|
+
* An unreadable config file disables nothing — the hooks fail open, they never fail silent-off.
|
|
20
|
+
*/
|
|
21
|
+
import * as fs from 'fs';
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
import type { HookContext, HookName } from './hook-result-contract';
|
|
24
|
+
import { findSungenRoot } from './sungen-project-root';
|
|
25
|
+
|
|
26
|
+
const OFF = /^(?:off|0|false|no|disabled?)$/i;
|
|
27
|
+
export const KILL_SWITCH_ENV = 'SUNGEN_HOOKS';
|
|
28
|
+
export const PROJECT_CONFIG = path.join('qa', 'sungen.config.yaml');
|
|
29
|
+
|
|
30
|
+
/** `stop-next-step` → `SUNGEN_HOOK_STOP_NEXT_STEP`. */
|
|
31
|
+
export const hookEnvVar = (name: HookName): string => `SUNGEN_HOOK_${name.toUpperCase().replace(/-/g, '_')}`;
|
|
32
|
+
|
|
33
|
+
const isOff = (v: unknown): boolean => v === false || (typeof v === 'string' && OFF.test(v.trim()));
|
|
34
|
+
|
|
35
|
+
export function hooksKilled(env: Record<string, string | undefined>): boolean {
|
|
36
|
+
return isOff(env[KILL_SWITCH_ENV] ?? '');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Hooks the project config turns off: `'all'`, or the names set to false. */
|
|
40
|
+
export function configDisabledHooks(root: string | null): 'all' | Set<string> {
|
|
41
|
+
const off = new Set<string>();
|
|
42
|
+
if (!root) return off;
|
|
43
|
+
const file = path.join(root, PROJECT_CONFIG);
|
|
44
|
+
if (!fs.existsSync(file)) return off;
|
|
45
|
+
try {
|
|
46
|
+
// Loaded lazily: the guard runs on every tool call and most projects have no config file.
|
|
47
|
+
const { parse } = require('yaml') as typeof import('yaml');
|
|
48
|
+
const doc = parse(fs.readFileSync(file, 'utf8')) as { hooks?: unknown } | null;
|
|
49
|
+
const hooks = doc && typeof doc === 'object' ? doc.hooks : undefined;
|
|
50
|
+
if (isOff(hooks)) return 'all';
|
|
51
|
+
if (hooks && typeof hooks === 'object') {
|
|
52
|
+
for (const [name, value] of Object.entries(hooks as Record<string, unknown>)) if (isOff(value)) off.add(name);
|
|
53
|
+
}
|
|
54
|
+
} catch { /* unreadable → nothing disabled (fail open) */ }
|
|
55
|
+
return off;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Hooks the project config may switch off at run time — never the guard. */
|
|
59
|
+
const CONFIG_TOGGLEABLE: ReadonlySet<HookName> = new Set<HookName>(['stop-next-step', 'session-start-unit-state']);
|
|
60
|
+
|
|
61
|
+
export function isHookEnabled(name: HookName, ctx: HookContext): boolean {
|
|
62
|
+
if (hooksKilled(ctx.env) || isOff(ctx.env[hookEnvVar(name)] ?? '')) return false;
|
|
63
|
+
if (!CONFIG_TOGGLEABLE.has(name)) return true;
|
|
64
|
+
const off = configDisabledHooks(findSungenRoot(ctx.cwd));
|
|
65
|
+
return off !== 'all' && !off.has(name);
|
|
66
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code PreToolUse guard — approval artifacts are not the agent's to write (P0.1, takumi T4).
|
|
3
|
+
*
|
|
4
|
+
* `qa/**\/contracts.lock.yaml` and the decision records record what a HUMAN approved; they change
|
|
5
|
+
* only through the TTY path. For other assistants that is a soft barrier (git HEAD comparison,
|
|
6
|
+
* in-run flags). In Claude Code this hook makes it a hard floor — see guard-rules.ts for what each
|
|
7
|
+
* rule denies or asks, and guard-protected-paths.ts for the paths.
|
|
8
|
+
*
|
|
9
|
+
* The guard cannot be switched off by the agent it guards: `qa/sungen.config.yaml` does not disable
|
|
10
|
+
* it (only SUNGEN_HOOKS / SUNGEN_HOOK_PRE_TOOL_GUARD in the real environment do), and an agent edit
|
|
11
|
+
* of the files that control the hooks asks the user. The human path is editing those files by
|
|
12
|
+
* hand, then `sungen update`.
|
|
13
|
+
*
|
|
14
|
+
* The decision is a list of rules — the extension point. Phase 06 added two: no `sungen delivery`
|
|
15
|
+
* while the Test Contract verdict rebuilt from .feature + lock + git HEAD is not pass
|
|
16
|
+
* (guard-delivery-contract-rule.ts — it READS project state, so it declares `onError: 'ask'` and a
|
|
17
|
+
* read failure asks instead of denying blind), and ask before a human-confirmed command is run
|
|
18
|
+
* through a terminal emulator (guard-tty-spoof-rule.ts).
|
|
19
|
+
*/
|
|
20
|
+
import * as path from 'path';
|
|
21
|
+
import { HookContext, HookMeta, HookResult, NO_OP, SungenHook } from './hook-result-contract';
|
|
22
|
+
import { GuardedCall, GuardRule, bashProtectedMention, protectedApprovalPaths, protectedHookControl } from './guard-rules';
|
|
23
|
+
import { deliveryContractVerdict } from './guard-delivery-contract-rule';
|
|
24
|
+
import { ttySpoof } from './guard-tty-spoof-rule';
|
|
25
|
+
|
|
26
|
+
export type { GuardedCall, GuardRule } from './guard-rules';
|
|
27
|
+
|
|
28
|
+
export const meta: HookMeta = {
|
|
29
|
+
events: ['PreToolUse'],
|
|
30
|
+
matchers: { PreToolUse: 'Edit|Write|MultiEdit|Bash' },
|
|
31
|
+
timeout: 5,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** The rules, in order (first deny wins, else first ask). */
|
|
35
|
+
export const GUARD_RULES: GuardRule[] = [protectedApprovalPaths, protectedHookControl, bashProtectedMention, ttySpoof, deliveryContractVerdict];
|
|
36
|
+
|
|
37
|
+
/** First deny wins; otherwise the first ask; otherwise ok. */
|
|
38
|
+
export function evaluateGuard(call: GuardedCall, ctx: HookContext, rules: GuardRule[] = GUARD_RULES): HookResult {
|
|
39
|
+
let ask: HookResult | null = null;
|
|
40
|
+
for (const rule of rules) {
|
|
41
|
+
let r: HookResult | null;
|
|
42
|
+
try { r = rule.check(call, ctx); } catch {
|
|
43
|
+
if (rule.onError === 'ask') ask ??= { status: 'ask', askReason: `sungen guard (${rule.id}) could not read the project state it decides on — confirm this call yourself.` };
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (r?.status === 'deny') return r;
|
|
47
|
+
if (r?.status === 'ask') ask ??= r;
|
|
48
|
+
}
|
|
49
|
+
return ask ?? { status: 'ok' };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const GUARDED_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'Bash']);
|
|
53
|
+
|
|
54
|
+
export const preToolGuardHook: SungenHook = {
|
|
55
|
+
name: 'pre-tool-guard',
|
|
56
|
+
meta,
|
|
57
|
+
// A payload too large to read (an agent can build one with a huge Write) is not a free pass.
|
|
58
|
+
onUnreadableInput: { status: 'ask', askReason: 'sungen guard: this tool call is too large to inspect — confirm it yourself.' },
|
|
59
|
+
run(input, ctx) {
|
|
60
|
+
const tool = typeof input.tool_name === 'string' ? input.tool_name : '';
|
|
61
|
+
if (!GUARDED_TOOLS.has(tool)) return NO_OP;
|
|
62
|
+
const ti = input.tool_input && typeof input.tool_input === 'object' ? input.tool_input as Record<string, unknown> : {};
|
|
63
|
+
const filePaths = tool !== 'Bash' && typeof ti.file_path === 'string' && ti.file_path ? [path.resolve(ctx.cwd, ti.file_path)] : [];
|
|
64
|
+
const bashCommand = tool === 'Bash' && typeof ti.command === 'string' ? ti.command : undefined;
|
|
65
|
+
return evaluateGuard({ tool, filePaths, bashCommand, cwd: ctx.cwd }, ctx);
|
|
66
|
+
},
|
|
67
|
+
};
|