@sun-asterisk/sungen 3.2.34 → 3.3.0-alpha.2
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/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +10 -5
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +6 -2
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/contract-derive-command.d.ts +11 -0
- package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
- package/dist/cli/commands/contract-derive-command.js +99 -0
- package/dist/cli/commands/contract-derive-command.js.map +1 -0
- package/dist/cli/commands/contract.d.ts +17 -0
- package/dist/cli/commands/contract.d.ts.map +1 -0
- package/dist/cli/commands/contract.js +162 -0
- package/dist/cli/commands/contract.js.map +1 -0
- package/dist/cli/commands/defect.d.ts +7 -0
- package/dist/cli/commands/defect.d.ts.map +1 -0
- package/dist/cli/commands/defect.js +68 -0
- package/dist/cli/commands/defect.js.map +1 -0
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +96 -12
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +3 -1
- 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 +43 -15
- package/dist/cli/commands/journey.js.map +1 -1
- package/dist/cli/commands/next.d.ts +8 -0
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +68 -8
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/triage.d.ts +7 -0
- package/dist/cli/commands/triage.d.ts.map +1 -0
- package/dist/cli/commands/triage.js +98 -0
- package/dist/cli/commands/triage.js.map +1 -0
- 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 +31 -6
- 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/project-sungen-invocation.d.ts +7 -0
- package/dist/cli/project-sungen-invocation.d.ts.map +1 -0
- package/dist/cli/project-sungen-invocation.js +76 -0
- package/dist/cli/project-sungen-invocation.js.map +1 -0
- package/dist/contract/approve-contract.d.ts +31 -0
- package/dist/contract/approve-contract.d.ts.map +1 -0
- package/dist/contract/approve-contract.js +87 -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 +18 -0
- package/dist/contract/build-trace-conformance.d.ts.map +1 -0
- package/dist/contract/build-trace-conformance.js +153 -0
- package/dist/contract/build-trace-conformance.js.map +1 -0
- package/dist/contract/classify-contract-change.d.ts +23 -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 +32 -0
- package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
- package/dist/contract/classify-trace-conformance.js +117 -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 +71 -0
- package/dist/contract/contract-schema.d.ts.map +1 -0
- package/dist/contract/contract-schema.js +102 -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 +140 -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 +38 -0
- package/dist/contract/derive-free-feature.d.ts.map +1 -0
- package/dist/contract/derive-free-feature.js +154 -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 +73 -0
- package/dist/contract/project-contract-blockers.js.map +1 -0
- package/dist/contract/record-contract-approval.d.ts +21 -0
- package/dist/contract/record-contract-approval.d.ts.map +1 -0
- package/dist/contract/record-contract-approval.js +98 -0
- package/dist/contract/record-contract-approval.js.map +1 -0
- package/dist/contract/render-free-gherkin.d.ts +40 -0
- package/dist/contract/render-free-gherkin.d.ts.map +1 -0
- package/dist/contract/render-free-gherkin.js +141 -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 +8 -0
- package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
- package/dist/contract/resolve-unit-trace-identities.js +78 -0
- package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts +33 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.js +161 -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/select-final-attempt.d.ts +27 -0
- package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
- package/dist/contract/trace/select-final-attempt.js +116 -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 +21 -0
- package/dist/contract/trace/trace-binding.d.ts.map +1 -0
- package/dist/contract/trace/trace-binding.js +88 -0
- package/dist/contract/trace/trace-binding.js.map +1 -0
- package/dist/contract/trace/trace-file-reader.d.ts +41 -0
- package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
- package/dist/contract/trace/trace-file-reader.js +161 -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-identity-resolution.d.ts +26 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
- package/dist/contract/trace/trace-identity-resolution.js +84 -0
- package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
- package/dist/contract/trace/trace-run-record.d.ts +30 -0
- package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
- package/dist/contract/trace/trace-run-record.js +121 -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 +104 -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 +42 -0
- package/dist/decisions/decision-schema.d.ts.map +1 -0
- package/dist/decisions/decision-schema.js +75 -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 +7 -0
- package/dist/decisions/git-head-trust.d.ts.map +1 -0
- package/dist/decisions/git-head-trust.js +76 -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 +31 -0
- package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
- package/dist/decisions/legacy-waiver-migration.js +170 -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 +4 -0
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +1 -1
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +8 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +12 -0
- package/dist/exporters/feature-parser.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 +59 -19
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.d.ts +19 -0
- package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.js +76 -6
- package/dist/exporters/matrix/carry-manual-results.js.map +1 -1
- package/dist/exporters/matrix/export.d.ts +8 -2
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +16 -3
- 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 +4 -1
- 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 +6 -0
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +9 -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/playwright-report-parser.d.ts.map +1 -1
- package/dist/exporters/playwright-report-parser.js +1 -0
- package/dist/exporters/playwright-report-parser.js.map +1 -1
- package/dist/exporters/scenario-merger.d.ts.map +1 -1
- package/dist/exporters/scenario-merger.js +2 -0
- package/dist/exporters/scenario-merger.js.map +1 -1
- package/dist/exporters/types.d.ts +5 -0
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +13 -0
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/graph/evaluate-graph.d.ts +23 -0
- package/dist/graph/evaluate-graph.d.ts.map +1 -0
- package/dist/graph/evaluate-graph.js +52 -0
- package/dist/graph/evaluate-graph.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/node-decl.d.ts +66 -0
- package/dist/graph/node-decl.d.ts.map +1 -0
- package/dist/graph/node-decl.js +8 -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 +59 -0
- package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
- package/dist/graph/nodes/test-case-nodes.d.ts +26 -0
- package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/test-case-nodes.js +66 -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 +95 -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 +88 -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 +11 -0
- package/dist/graph/test-case-results.d.ts.map +1 -0
- package/dist/graph/test-case-results.js +137 -0
- package/dist/graph/test-case-results.js.map +1 -0
- package/dist/graph/test-case-satisfied.d.ts +25 -0
- package/dist/graph/test-case-satisfied.d.ts.map +1 -0
- package/dist/graph/test-case-satisfied.js +51 -0
- package/dist/graph/test-case-satisfied.js.map +1 -0
- package/dist/graph/test-case-state-shape.d.ts +67 -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 +133 -0
- package/dist/graph/unit-artifact-probes.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 +11 -0
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +34 -9
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/catalog/gate-rules.yaml +647 -0
- package/dist/harness/counted-assertions.d.ts +2 -0
- package/dist/harness/counted-assertions.d.ts.map +1 -0
- package/dist/harness/counted-assertions.js +38 -0
- package/dist/harness/counted-assertions.js.map +1 -0
- package/dist/harness/defect-draft.d.ts +14 -0
- package/dist/harness/defect-draft.d.ts.map +1 -0
- package/dist/harness/defect-draft.js +143 -0
- package/dist/harness/defect-draft.js.map +1 -0
- package/dist/harness/defects.d.ts +24 -0
- package/dist/harness/defects.d.ts.map +1 -0
- package/dist/harness/defects.js +118 -0
- package/dist/harness/defects.js.map +1 -0
- 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 +101 -0
- package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
- package/dist/harness/gates/audit-input-hash.d.ts +4 -0
- package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
- package/dist/harness/gates/audit-input-hash.js +96 -0
- package/dist/harness/gates/audit-input-hash.js.map +1 -0
- package/dist/harness/gates/audit-report-verification.d.ts +41 -0
- package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
- package/dist/harness/gates/audit-report-verification.js +236 -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 +21 -0
- package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
- package/dist/harness/gates/delivery-checklist.js +128 -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 +138 -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-review.d.ts +6 -0
- package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
- package/dist/harness/gates/migrated-waiver-review.js +116 -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 +43 -1
- package/dist/harness/journey.d.ts.map +1 -1
- package/dist/harness/journey.js +207 -33
- 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 +73 -45
- package/dist/harness/manifest.js.map +1 -1
- package/dist/harness/next-step.d.ts +37 -6
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +34 -174
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts +25 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +38 -13
- 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 +29 -6
- 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 +64 -44
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/triage-signals.d.ts +20 -0
- package/dist/harness/triage-signals.d.ts.map +1 -0
- package/dist/harness/triage-signals.js +30 -0
- package/dist/harness/triage-signals.js.map +1 -0
- package/dist/harness/triage-unit.d.ts +23 -0
- package/dist/harness/triage-unit.d.ts.map +1 -0
- package/dist/harness/triage-unit.js +142 -0
- package/dist/harness/triage-unit.js.map +1 -0
- package/dist/harness/triage.d.ts +28 -0
- package/dist/harness/triage.d.ts.map +1 -0
- package/dist/harness/triage.js +99 -0
- package/dist/harness/triage.js.map +1 -0
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +2 -1
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/hooks/bash-write-targets.d.ts +15 -0
- package/dist/hooks/bash-write-targets.d.ts.map +1 -0
- package/dist/hooks/bash-write-targets.js +173 -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 +9 -0
- package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
- package/dist/hooks/guard-protected-paths.js +107 -0
- package/dist/hooks/guard-protected-paths.js.map +1 -0
- package/dist/hooks/guard-rules.d.ts +37 -0
- package/dist/hooks/guard-rules.d.ts.map +1 -0
- package/dist/hooks/guard-rules.js +61 -0
- package/dist/hooks/guard-rules.js.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts +15 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.js +21 -0
- package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
- package/dist/hooks/hand-back-core.d.ts +39 -0
- package/dist/hooks/hand-back-core.d.ts.map +1 -0
- package/dist/hooks/hand-back-core.js +122 -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 +13 -0
- package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
- package/dist/hooks/session-start-unit-state-hook.js +57 -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 +42 -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/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 +16 -6
- package/dist/orchestrator/ai-skills/commands.js.map +1 -1
- package/dist/orchestrator/ai-skills/golden-skills.test.js +29 -0
- package/dist/orchestrator/ai-skills/golden-skills.test.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/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +4 -2
- package/dist/orchestrator/screen-manager.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 +2 -0
- 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 +14 -16
- package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +2 -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 +2 -0
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +14 -10
- package/dist/orchestrator/templates/ai-src/config/claude.md +20 -7
- package/dist/orchestrator/templates/ai-src/config/copilot.md +20 -7
- 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/skills/sungen-api-coverage-model/SKILL.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
- package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- 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/commands/audit.ts +10 -5
- package/src/cli/commands/capability.ts +4 -2
- package/src/cli/commands/contract-derive-command.ts +61 -0
- package/src/cli/commands/contract.ts +130 -0
- package/src/cli/commands/defect.ts +49 -0
- package/src/cli/commands/delivery.ts +92 -15
- package/src/cli/commands/generate.ts +3 -1
- package/src/cli/commands/journey.ts +41 -16
- package/src/cli/commands/next.ts +64 -10
- package/src/cli/commands/triage.ts +57 -0
- package/src/cli/commands/update.ts +35 -8
- package/src/cli/index.ts +24 -30
- package/src/cli/project-sungen-invocation.ts +39 -0
- package/src/contract/approve-contract.ts +94 -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 +100 -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 +116 -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 +137 -0
- package/src/contract/contract-units.ts +52 -0
- package/src/contract/contracts-lock-file.ts +97 -0
- package/src/contract/data-normalize.ts +66 -0
- package/src/contract/derive-free-feature.ts +120 -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 +36 -0
- package/src/contract/record-contract-approval.ts +70 -0
- package/src/contract/render-free-gherkin.ts +113 -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 +35 -0
- package/src/contract/trace/collect-trace-evidence.ts +109 -0
- package/src/contract/trace/identity-cache.ts +61 -0
- package/src/contract/trace/select-final-attempt.ts +70 -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 +56 -0
- package/src/contract/trace/trace-file-reader.ts +133 -0
- package/src/contract/trace/trace-fingerprint.ts +68 -0
- package/src/contract/trace/trace-identity-resolution.ts +74 -0
- package/src/contract/trace/trace-run-record.ts +80 -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 +60 -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 +76 -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 +32 -0
- package/src/decisions/human-confirmation.ts +119 -0
- package/src/decisions/legacy-waiver-migration.ts +135 -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 +5 -1
- package/src/exporters/feature-parser.ts +12 -0
- package/src/exporters/matrix/build.ts +62 -18
- package/src/exporters/matrix/carry-manual-results.ts +70 -7
- package/src/exporters/matrix/export.ts +17 -2
- package/src/exporters/matrix/gates.ts +62 -129
- package/src/exporters/matrix/render-csv.ts +4 -1
- package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
- package/src/exporters/matrix/render-xlsx.ts +6 -0
- package/src/exporters/matrix/types.ts +9 -1
- package/src/exporters/matrix/xlsx-cells.ts +1 -1
- package/src/exporters/playwright-report-parser.ts +1 -0
- package/src/exporters/scenario-merger.ts +2 -1
- package/src/exporters/types.ts +5 -0
- package/src/generators/test-generator/code-generator.ts +13 -0
- package/src/graph/evaluate-graph.ts +66 -0
- package/src/graph/locate-finding.ts +93 -0
- package/src/graph/node-decl.ts +72 -0
- package/src/graph/nodes/core-node-catalog.ts +15 -0
- package/src/graph/nodes/test-case-contract-nodes.ts +74 -0
- package/src/graph/nodes/test-case-nodes.ts +68 -0
- package/src/graph/nodes/unit-audit-nodes.ts +95 -0
- package/src/graph/nodes/unit-contract-node.ts +89 -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 +90 -0
- package/src/graph/test-case-satisfied.ts +55 -0
- package/src/graph/test-case-state-shape.ts +62 -0
- package/src/graph/test-case-states.ts +84 -0
- package/src/graph/unit-artifact-probes.ts +77 -0
- package/src/graph/unit-state-facts.ts +112 -0
- package/src/harness/audit.ts +46 -11
- package/src/harness/catalog/gate-rules.yaml +647 -0
- package/src/harness/counted-assertions.ts +33 -0
- package/src/harness/defect-draft.ts +115 -0
- package/src/harness/defects.ts +94 -0
- package/src/harness/gates/audit-gate-terms.ts +80 -0
- package/src/harness/gates/audit-gate-verdict.ts +78 -0
- package/src/harness/gates/audit-input-hash.ts +55 -0
- package/src/harness/gates/audit-report-verification.ts +201 -0
- package/src/harness/gates/decisions-hash.ts +42 -0
- package/src/harness/gates/delivery-checklist.ts +120 -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 +99 -0
- package/src/harness/gates/journey-gate-inputs.ts +89 -0
- package/src/harness/gates/migrated-waiver-review.ts +68 -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 +215 -38
- package/src/harness/manifest.ts +69 -45
- package/src/harness/next-step.ts +60 -170
- package/src/harness/parse.ts +40 -13
- package/src/harness/provenance.ts +99 -11
- package/src/harness/script-check.ts +27 -6
- package/src/harness/sensors.ts +73 -47
- package/src/harness/triage-signals.ts +46 -0
- package/src/harness/triage-unit.ts +113 -0
- package/src/harness/triage.ts +127 -0
- package/src/harness/viewpoint-ledger.ts +2 -2
- package/src/hooks/bash-write-targets.ts +109 -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 +71 -0
- package/src/hooks/guard-rules.ts +98 -0
- package/src/hooks/guard-tty-spoof-rule.ts +31 -0
- package/src/hooks/hand-back-core.ts +112 -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 +56 -0
- package/src/hooks/stop-next-step-hook.ts +37 -0
- package/src/hooks/sungen-project-root.ts +80 -0
- package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
- package/src/orchestrator/ai-skills/commands.ts +22 -8
- package/src/orchestrator/ai-skills/golden-skills.test.ts +23 -0
- 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/screen-manager.ts +3 -1
- package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
- 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 +14 -16
- package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/delivery.md +2 -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 +2 -0
- package/src/orchestrator/templates/ai-src/commands/run-test.md +14 -10
- package/src/orchestrator/templates/ai-src/config/claude.md +20 -7
- package/src/orchestrator/templates/ai-src/config/copilot.md +20 -7
- 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/skills/sungen-api-coverage-model/SKILL.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
- package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- package/src/utils/safe-yaml-parse.ts +44 -0
|
@@ -0,0 +1,112 @@
|
|
|
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, withProjectInvocation } from '../cli/project-sungen-invocation';
|
|
14
|
+
import { resolveUnitCandidates } from '../cli/resolve-unit';
|
|
15
|
+
import { readUnitState, NextStep, UnitState } from '../harness/next-step';
|
|
16
|
+
import { NO_OP, HookContext, HookInput, HookResult } from './hook-result-contract';
|
|
17
|
+
import { findSungenRoot, findUnit, nearestUnit, UnitRef } from './sungen-project-root';
|
|
18
|
+
|
|
19
|
+
/** `hook` speaks JSON to Claude Code on stdout: it never prints a hand-back. */
|
|
20
|
+
const SILENT_COMMANDS = new Set(['hook']);
|
|
21
|
+
/** `next` IS the answer when it succeeds; a failed `next` answered nothing and hands back like any failure (#937). */
|
|
22
|
+
const ANSWER_COMMANDS = new Set(['next']);
|
|
23
|
+
const UNIT_FLAG = /^(?:--screen|-s|--flow|--area|--api)$/;
|
|
24
|
+
const UNIT_FLAG_EQ = /^--(?:screen|flow|area|api)=(.+)$/;
|
|
25
|
+
/** A unit id is a name or `flows/<name>` — anything else is not offered back to the operator. */
|
|
26
|
+
const SAFE_UNIT = /^[\w.-]+(?:\/[\w.-]+)?$/;
|
|
27
|
+
|
|
28
|
+
/** `invocation`: how the project runs sungen (`npx sungen` with a local install, #939). */
|
|
29
|
+
export function handBackLine(unit?: string, invocation = 'sungen'): string {
|
|
30
|
+
return `Next: ${invocation} next${unit ? ` ${unit}` : ''}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface CliHandBackState { unit?: string; machine: boolean; silent: boolean; answer: boolean; actionRan: boolean }
|
|
34
|
+
|
|
35
|
+
/** The default, set before commander parses — what a usage error or a fatal error hands back. */
|
|
36
|
+
export function initialHandBackState(argv: string[]): CliHandBackState {
|
|
37
|
+
const args = argv.slice(2);
|
|
38
|
+
let unit: string | undefined;
|
|
39
|
+
for (let i = 0; i < args.length; i++) {
|
|
40
|
+
const eq = args[i].match(UNIT_FLAG_EQ);
|
|
41
|
+
if (eq) unit ??= eq[1];
|
|
42
|
+
else if (UNIT_FLAG.test(args[i]) && args[i + 1] && !args[i + 1].startsWith('-')) unit ??= args[++i];
|
|
43
|
+
}
|
|
44
|
+
const command = args.find((a) => !a.startsWith('-')) ?? '';
|
|
45
|
+
return { unit, machine: args.includes('--json') || args.includes('--grep'), silent: SILENT_COMMANDS.has(command), answer: ANSWER_COMMANDS.has(command), actionRan: false };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** commander's preAction: the action now KNOWS its unit and output mode. */
|
|
49
|
+
export function onCliAction(state: CliHandBackState, name: string, opts: Record<string, unknown>, args: unknown[]): void {
|
|
50
|
+
state.actionRan = true;
|
|
51
|
+
if (SILENT_COMMANDS.has(name)) { state.silent = true; return; }
|
|
52
|
+
state.answer = ANSWER_COMMANDS.has(name);
|
|
53
|
+
const flagged = [opts.screen, opts.flow, opts.area, opts.api].find((v) => typeof v === 'string' && v) as string | undefined;
|
|
54
|
+
state.unit = flagged ?? (typeof args?.[0] === 'string' ? args[0] : undefined);
|
|
55
|
+
// `--grep` is machine output too: callers capture it with `$(…)` (#836).
|
|
56
|
+
state.machine = opts.json === true || opts.grep === true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* What to print as the process exits, or null for nothing. A positional that is not a unit on
|
|
61
|
+
* disk (`capability add mock` → `mock`) is dropped rather than offered as `sungen next mock`.
|
|
62
|
+
*/
|
|
63
|
+
export function exitHandBack(state: CliHandBackState, code: number, cwd: string): { stream: 'stdout' | 'stderr'; text: string } | null {
|
|
64
|
+
if (state.silent) return null;
|
|
65
|
+
if (state.answer && code === 0) return null; // a successful `next` is the hand-back itself
|
|
66
|
+
if (!state.actionRan && code === 0) return null; // --help / --version printed what was asked
|
|
67
|
+
const unit = state.unit && SAFE_UNIT.test(state.unit) && !state.unit.split('/').includes('..')
|
|
68
|
+
&& resolveUnitCandidates(state.unit, cwd).length > 0 ? state.unit : undefined;
|
|
69
|
+
const line = handBackLine(unit, sungenInvocation(findSungenRoot(cwd) ?? cwd));
|
|
70
|
+
if (code === 1) return { stream: 'stderr', text: `${line}\n` }; // after the error, one line
|
|
71
|
+
return { stream: state.machine ? 'stderr' : 'stdout', text: `\n${line}\n` };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Wire the hand-back to process exit; returns the preAction callback. Never masks the real exit. */
|
|
75
|
+
export function attachCliHandBack(argv: string[], cwd: string): { onAction: (name: string, opts: Record<string, unknown>, args: unknown[]) => void } {
|
|
76
|
+
const state = initialHandBackState(argv);
|
|
77
|
+
process.on('exit', (code) => {
|
|
78
|
+
try {
|
|
79
|
+
const out = exitHandBack(state, code, cwd);
|
|
80
|
+
if (out) process[out.stream].write(out.text);
|
|
81
|
+
} catch { /* the hand-back is advice; the exit code is the contract */ }
|
|
82
|
+
});
|
|
83
|
+
return { onAction: (name, opts, args) => onCliAction(state, name, opts, args) };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// --- the hook-shaped core -----------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
const MARK: Record<NextStep['kind'], string> = { do: '→', blocked: '⛔', optional: '○', decide: '⚖' };
|
|
89
|
+
|
|
90
|
+
/** `sungen next` rendered compactly for an agent's context; commands as `root` runs sungen. */
|
|
91
|
+
export function formatSteps(steps: NextStep[], max = 4, root?: string): string[] {
|
|
92
|
+
return steps.slice(0, max).map((st) =>
|
|
93
|
+
` ${MARK[st.kind]} ${root ? withProjectInvocation(st.command, root) : st.command}${st.kind === 'decide' ? ' (a QA decision — ask, do not run)' : ''}\n ${st.because}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The unit a session is on (a named one when it exists, else the nearest) and its derived state. */
|
|
97
|
+
export function locateUnitState(ctx: HookContext, preferred?: string): { root: string; unit: UnitRef; state: UnitState } | null {
|
|
98
|
+
const root = findSungenRoot(ctx.cwd);
|
|
99
|
+
if (!root) return null;
|
|
100
|
+
const unit = (preferred && SAFE_UNIT.test(preferred) ? findUnit(root, preferred) : null) ?? nearestUnit(root, ctx.cwd);
|
|
101
|
+
return unit ? { root, unit, state: readUnitState(root, unit.dir, unit.name, unit.kind) } : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Hook core: the next steps for the session's unit (input.unit, else the nearest) as context. */
|
|
105
|
+
export function run(input: HookInput, ctx: HookContext): HookResult {
|
|
106
|
+
const found = locateUnitState(ctx, typeof input.unit === 'string' ? input.unit : undefined);
|
|
107
|
+
if (!found || found.state.steps.length === 0) return NO_OP;
|
|
108
|
+
return {
|
|
109
|
+
status: 'context',
|
|
110
|
+
output: [`${handBackLine(found.unit.name, sungenInvocation(found.root))} — derived from what the project holds now:`, ...formatSteps(found.state.steps, 4, found.root)].join('\n'),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code SessionStart hook — a new, resumed or compacted session starts knowing where the unit
|
|
3
|
+
* stands (P0.1, takumi T1).
|
|
4
|
+
*
|
|
5
|
+
* After `/clear` or a context compaction the agent has lost the thread: it re-derives the state
|
|
6
|
+
* from memory, or worse, re-runs work that is already done. This injects what `sungen next`
|
|
7
|
+
* derives for the session's unit (the one cwd sits in, else the most recently active): what the
|
|
8
|
+
* project already holds — stated as "do not redo" — the audit verdict, and the next steps.
|
|
9
|
+
*/
|
|
10
|
+
import { NO_OP, HookMeta, SungenHook } from './hook-result-contract';
|
|
11
|
+
import { formatSteps, locateUnitState } from './hand-back-core';
|
|
12
|
+
import { sungenInvocation } from '../cli/project-sungen-invocation';
|
|
13
|
+
import { listUnits } from './sungen-project-root';
|
|
14
|
+
import type { UnitState } from '../harness/next-step';
|
|
15
|
+
|
|
16
|
+
export const meta: HookMeta = {
|
|
17
|
+
events: ['SessionStart'],
|
|
18
|
+
matchers: { SessionStart: 'startup|resume|clear|compact' },
|
|
19
|
+
timeout: 10,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** What the unit already holds, in workflow order. */
|
|
23
|
+
function held(s: UnitState): string {
|
|
24
|
+
const items = [
|
|
25
|
+
s.hasSpec && 'spec', s.hasViewpoint && 'test-viewpoint',
|
|
26
|
+
s.kind === 'flow' && s.hasContract && (s.hasFlowInventory ? 'flow contract + inventory' : 'flow contract'),
|
|
27
|
+
s.scenarioCount > 0 && `${s.scenarioCount} scenario(s)`,
|
|
28
|
+
s.hasSelectors && !s.selectorsArePlaceholder && 'selectors',
|
|
29
|
+
s.compiled && 'compiled spec',
|
|
30
|
+
s.hasResults && `run results${s.failedCount ? ` (${s.failedCount} failed)` : ''}`,
|
|
31
|
+
s.hasDeliverable && 'deliverable',
|
|
32
|
+
].filter(Boolean);
|
|
33
|
+
return items.length ? items.join(' · ') : 'nothing yet';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const sessionStartUnitStateHook: SungenHook = {
|
|
37
|
+
name: 'session-start-unit-state',
|
|
38
|
+
meta,
|
|
39
|
+
run(input, ctx) {
|
|
40
|
+
const found = locateUnitState(ctx);
|
|
41
|
+
if (!found) return NO_OP;
|
|
42
|
+
const { root, unit, state } = found;
|
|
43
|
+
const others = listUnits(root).length - 1;
|
|
44
|
+
const sungen = sungenInvocation(root);
|
|
45
|
+
const lines = [
|
|
46
|
+
`[sungen] Unit in progress: ${unit.name} (${unit.kind})${others > 0 ? ` — the project holds ${others} other unit(s)` : ''}.`,
|
|
47
|
+
`Already done — do not redo: ${held(state)}.`,
|
|
48
|
+
state.audit ? `Audit: ${state.audit.overall}/10 [${state.audit.gateStatus}] · ${state.audit.findings.length} finding(s).` : '',
|
|
49
|
+
`Next (\`${sungen} next ${unit.name}\`, derived from the project state):`,
|
|
50
|
+
...formatSteps(state.steps, 4, root),
|
|
51
|
+
input.source === 'compact' ? 'Context was compacted: re-read the unit\'s files before acting on them.' : '',
|
|
52
|
+
others > 0 ? `Working on another unit? Ask \`${sungen} next <unit>\` (or \`${sungen} next\` for all).` : '',
|
|
53
|
+
];
|
|
54
|
+
return { status: 'context', output: lines.filter(Boolean).join('\n') };
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Stop hook — a turn that ran sungen work never ends without the next step (P0.1).
|
|
3
|
+
*
|
|
4
|
+
* A slash command has no CLI exit, so the CLI's `Next: sungen next …` footer cannot reach a session
|
|
5
|
+
* that ends inside `/sungen:create-test`; the hand-back rule in the command template is only an
|
|
6
|
+
* instruction. This makes it deterministic for Claude Code: when the turn that just ended ran
|
|
7
|
+
* sungen and did not hand back (no AskUserQuestion after the last sungen call), the hand-back core's
|
|
8
|
+
* answer is injected as context and Claude continues once to present it.
|
|
9
|
+
*
|
|
10
|
+
* Loop-safe: `stop_hook_active` (Claude Code is already continuing because of a stop hook) is a
|
|
11
|
+
* no-op, and so is anything the hook cannot read — a missing transcript, a non-sungen directory.
|
|
12
|
+
*/
|
|
13
|
+
import { NO_OP, HookMeta, SungenHook } from './hook-result-contract';
|
|
14
|
+
import { run as handBackCore } from './hand-back-core';
|
|
15
|
+
import { summarizeTurn } from './claude-transcript-turn';
|
|
16
|
+
|
|
17
|
+
export const meta: HookMeta = { events: ['Stop'], timeout: 10 };
|
|
18
|
+
|
|
19
|
+
export const stopNextStepHook: SungenHook = {
|
|
20
|
+
name: 'stop-next-step',
|
|
21
|
+
meta,
|
|
22
|
+
run(input, ctx) {
|
|
23
|
+
if (input.stop_hook_active === true) return NO_OP;
|
|
24
|
+
const turn = summarizeTurn(input.transcript_path);
|
|
25
|
+
if (!turn || !turn.sungenActivity || turn.askedAfter) return NO_OP;
|
|
26
|
+
const core = handBackCore({ unit: turn.lastUnit }, ctx);
|
|
27
|
+
if (core.status !== 'context') return NO_OP;
|
|
28
|
+
return {
|
|
29
|
+
status: 'context',
|
|
30
|
+
output: [
|
|
31
|
+
'[sungen] This turn ran sungen work but ended without the next-step hand-back.',
|
|
32
|
+
'Hand back now: present these steps to the user with AskUserQuestion and stop — do not run them yourself.',
|
|
33
|
+
core.output,
|
|
34
|
+
].join('\n'),
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the sungen project is, and which unit a session is working on — read cheaply off disk.
|
|
3
|
+
*
|
|
4
|
+
* A hook runs from wherever Claude Code's shell happens to be, and in a monorepo the sungen project
|
|
5
|
+
* can sit below the repository root. So a project root is found by walking UP from a start path to
|
|
6
|
+
* the nearest directory holding `qa/` plus a sungen marker (`.sungen/` or `qa/capabilities.yaml`);
|
|
7
|
+
* the marker keeps an unrelated `~/qa/` folder higher up from being mistaken for a project.
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { reportSlug } from '../harness/unit-paths';
|
|
12
|
+
|
|
13
|
+
export type UnitKind = 'screen' | 'flow' | 'api';
|
|
14
|
+
export interface UnitRef { name: string; kind: UnitKind; dir: string }
|
|
15
|
+
|
|
16
|
+
const KIND_DIRS: Array<[string, UnitKind]> = [['screens', 'screen'], ['flows', 'flow'], ['api', 'api']];
|
|
17
|
+
const isDir = (p: string): boolean => { try { return fs.statSync(p).isDirectory(); } catch { return false; } };
|
|
18
|
+
const mtime = (p: string): number => { try { return fs.statSync(p).mtimeMs; } catch { return 0; } };
|
|
19
|
+
|
|
20
|
+
export function isSungenRoot(dir: string): boolean {
|
|
21
|
+
return isDir(path.join(dir, 'qa'))
|
|
22
|
+
&& (isDir(path.join(dir, '.sungen')) || fs.existsSync(path.join(dir, 'qa', 'capabilities.yaml')));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The nearest sungen project at or above `start`, or null outside one. */
|
|
26
|
+
export function findSungenRoot(start: string): string | null {
|
|
27
|
+
let dir = path.resolve(start);
|
|
28
|
+
for (let i = 0; i < 40; i++) {
|
|
29
|
+
if (isSungenRoot(dir)) return dir;
|
|
30
|
+
const parent = path.dirname(dir);
|
|
31
|
+
if (parent === dir) return null;
|
|
32
|
+
dir = parent;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Every unit directory the project holds (`qa/api/flows` is a container, not a unit). */
|
|
38
|
+
export function listUnits(root: string): UnitRef[] {
|
|
39
|
+
const out: UnitRef[] = [];
|
|
40
|
+
for (const [sub, kind] of KIND_DIRS) {
|
|
41
|
+
let entries: fs.Dirent[] = [];
|
|
42
|
+
try { entries = fs.readdirSync(path.join(root, 'qa', sub), { withFileTypes: true }); } catch { continue; }
|
|
43
|
+
for (const e of entries) {
|
|
44
|
+
if (!e.isDirectory() || (kind === 'api' && e.name === 'flows')) continue;
|
|
45
|
+
out.push({ name: e.name, kind, dir: path.join(root, 'qa', sub, e.name) });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The unit with this name, when exactly one directory holds it (an ambiguous name is no answer). */
|
|
52
|
+
export function findUnit(root: string, name: string): UnitRef | null {
|
|
53
|
+
const hits = listUnits(root).filter((u) => u.name === name);
|
|
54
|
+
return hits.length === 1 ? hits[0] : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The unit a session is most likely on: the one `cwd` sits inside, else the one with the most
|
|
59
|
+
* recent activity — its ledger, its audit report, or its directory, whichever moved last.
|
|
60
|
+
*/
|
|
61
|
+
export function nearestUnit(root: string, cwd: string): UnitRef | null {
|
|
62
|
+
const units = listUnits(root);
|
|
63
|
+
const rel = path.relative(root, path.resolve(cwd)).split(path.sep);
|
|
64
|
+
if (rel[0] === 'qa' && rel.length >= 3) {
|
|
65
|
+
const inside = units.find((u) => u.dir === path.join(root, 'qa', rel[1], rel[2]));
|
|
66
|
+
if (inside) return inside;
|
|
67
|
+
}
|
|
68
|
+
let best: UnitRef | null = null;
|
|
69
|
+
let bestAt = -1;
|
|
70
|
+
for (const u of units) {
|
|
71
|
+
const slug = reportSlug(u.name);
|
|
72
|
+
const at = Math.max(
|
|
73
|
+
mtime(path.join(root, '.sungen', 'ledger', `${slug}.jsonl`)),
|
|
74
|
+
mtime(path.join(root, '.sungen', 'reports', `${slug}-audit.json`)),
|
|
75
|
+
mtime(u.dir),
|
|
76
|
+
);
|
|
77
|
+
if (at > bestAt) { best = u; bestAt = at; }
|
|
78
|
+
}
|
|
79
|
+
return best;
|
|
80
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-assistant partials for command templates (P0.1, root cause c).
|
|
3
|
+
*
|
|
4
|
+
* A command pulls a shared block in with an explicit directive on its own line:
|
|
5
|
+
*
|
|
6
|
+
* <!-- include: hand-back-rule -->
|
|
7
|
+
*
|
|
8
|
+
* and each assistant gets its OWN twin file — `templates/ai-src/partials/<name>.<adapter>.md` —
|
|
9
|
+
* chosen here by file name, never by branching inside the content (Claude asks with
|
|
10
|
+
* AskUserQuestion; Copilot/Codex have no such tool and end with a numbered list instead).
|
|
11
|
+
*
|
|
12
|
+
* Fails loud, like cap-fences: an unknown partial, a twin missing for one assistant, or a command
|
|
13
|
+
* without a REQUIRED include throws — a typo can never ship a command that silently lacks the rule.
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import type { AdapterKey } from './adapters';
|
|
18
|
+
|
|
19
|
+
const PARTIALS_DIR = path.resolve(__dirname, '..', 'templates', 'ai-src', 'partials');
|
|
20
|
+
|
|
21
|
+
/** Every command must include these. The hand-back rule is the reason this module exists. */
|
|
22
|
+
export const REQUIRED_INCLUDES = ['hand-back-rule'] as const;
|
|
23
|
+
|
|
24
|
+
const DIRECTIVE = /^[ \t]*<!--\s*include:\s*([a-z0-9-]+)\s*-->[ \t]*$/;
|
|
25
|
+
|
|
26
|
+
export class PartialError extends Error {}
|
|
27
|
+
|
|
28
|
+
/** Partial names a body includes, in order of appearance. */
|
|
29
|
+
export function includesOf(body: string): string[] {
|
|
30
|
+
const out: string[] = [];
|
|
31
|
+
for (const line of body.split('\n')) {
|
|
32
|
+
const m = line.match(DIRECTIVE);
|
|
33
|
+
if (m) out.push(m[1]);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Throws when a command lacks a required include — the build-time gate. */
|
|
39
|
+
export function assertRequiredIncludes(body: string, commandId: string): void {
|
|
40
|
+
const have = new Set(includesOf(body));
|
|
41
|
+
const missing = REQUIRED_INCLUDES.filter((name) => !have.has(name));
|
|
42
|
+
if (missing.length > 0) {
|
|
43
|
+
throw new PartialError(
|
|
44
|
+
`commands/${commandId}.md: missing required <!-- include: ${missing.join(' -->, <!-- include: ')} --> `
|
|
45
|
+
+ '(every command carries the next-step hand-back rule; see ai-skills/command-partial-includes.ts)',
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function readPartial(name: string, adapter: AdapterKey, commandId: string): string {
|
|
51
|
+
const file = path.join(PARTIALS_DIR, `${name}.${adapter}.md`);
|
|
52
|
+
if (!fs.existsSync(file)) {
|
|
53
|
+
throw new PartialError(`commands/${commandId}.md: <!-- include: ${name} --> has no partials/${name}.${adapter}.md`);
|
|
54
|
+
}
|
|
55
|
+
// A trailing newline is the file's, not the block's: the directive line's own break follows it.
|
|
56
|
+
return fs.readFileSync(file, 'utf8').replace(/\n+$/, '');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Replace each directive line with the adapter's partial, substituting `{{var}}` placeholders from
|
|
61
|
+
* `vars`. A placeholder with no value is an authoring error and fails loud.
|
|
62
|
+
*/
|
|
63
|
+
export function expandIncludes(body: string, adapter: AdapterKey, commandId: string, vars: Record<string, string>): string {
|
|
64
|
+
return body.split('\n').map((line) => {
|
|
65
|
+
const m = line.match(DIRECTIVE);
|
|
66
|
+
if (!m) return line;
|
|
67
|
+
return readPartial(m[1], adapter, commandId).replace(/\{\{(\w+)\}\}/g, (_all, key: string) => {
|
|
68
|
+
if (!(key in vars)) throw new PartialError(`partials/${m[1]}.${adapter}.md: unknown placeholder {{${key}}}`);
|
|
69
|
+
return vars[key];
|
|
70
|
+
});
|
|
71
|
+
}).join('\n');
|
|
72
|
+
}
|