dorfl 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advance-ci-template.d.ts +73 -0
- package/dist/advance-ci-template.d.ts.map +1 -0
- package/dist/advance-ci-template.js +104 -0
- package/dist/advance-ci-template.js.map +1 -0
- package/dist/advance-classify.d.ts +132 -0
- package/dist/advance-classify.d.ts.map +1 -0
- package/dist/advance-classify.js +120 -0
- package/dist/advance-classify.js.map +1 -0
- package/dist/advance-drivers.d.ts +182 -0
- package/dist/advance-drivers.d.ts.map +1 -0
- package/dist/advance-drivers.js +231 -0
- package/dist/advance-drivers.js.map +1 -0
- package/dist/advance-isolated.d.ts +156 -0
- package/dist/advance-isolated.d.ts.map +1 -0
- package/dist/advance-isolated.js +256 -0
- package/dist/advance-isolated.js.map +1 -0
- package/dist/advance-lifecycle-template.d.ts +107 -0
- package/dist/advance-lifecycle-template.d.ts.map +1 -0
- package/dist/advance-lifecycle-template.js +668 -0
- package/dist/advance-lifecycle-template.js.map +1 -0
- package/dist/advance-loop-driver.d.ts +325 -0
- package/dist/advance-loop-driver.d.ts.map +1 -0
- package/dist/advance-loop-driver.js +437 -0
- package/dist/advance-loop-driver.js.map +1 -0
- package/dist/advance-treeless-publish.d.ts +108 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -0
- package/dist/advance-treeless-publish.js +71 -0
- package/dist/advance-treeless-publish.js.map +1 -0
- package/dist/advance.d.ts +340 -0
- package/dist/advance.d.ts.map +1 -0
- package/dist/advance.js +1122 -0
- package/dist/advance.js.map +1 -0
- package/dist/advancing-lock.d.ts +294 -0
- package/dist/advancing-lock.d.ts.map +1 -0
- package/dist/advancing-lock.js +594 -0
- package/dist/advancing-lock.js.map +1 -0
- package/dist/agent-launch.d.ts +79 -0
- package/dist/agent-launch.d.ts.map +1 -0
- package/dist/agent-launch.js +61 -0
- package/dist/agent-launch.js.map +1 -0
- package/dist/agent-stop.d.ts +149 -0
- package/dist/agent-stop.d.ts.map +1 -0
- package/dist/agent-stop.js +307 -0
- package/dist/agent-stop.js.map +1 -0
- package/dist/apply-decide.d.ts +127 -0
- package/dist/apply-decide.d.ts.map +1 -0
- package/dist/apply-decide.js +176 -0
- package/dist/apply-decide.js.map +1 -0
- package/dist/apply-merge-action.d.ts +206 -0
- package/dist/apply-merge-action.d.ts.map +1 -0
- package/dist/apply-merge-action.js +307 -0
- package/dist/apply-merge-action.js.map +1 -0
- package/dist/apply-persist.d.ts +174 -0
- package/dist/apply-persist.d.ts.map +1 -0
- package/dist/apply-persist.js +359 -0
- package/dist/apply-persist.js.map +1 -0
- package/dist/arbiter.d.ts +120 -0
- package/dist/arbiter.d.ts.map +1 -0
- package/dist/arbiter.js +255 -0
- package/dist/arbiter.js.map +1 -0
- package/dist/brand.d.ts +70 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +84 -0
- package/dist/brand.js.map +1 -0
- package/dist/buildable-body.d.ts +132 -0
- package/dist/buildable-body.d.ts.map +1 -0
- package/dist/buildable-body.js +131 -0
- package/dist/buildable-body.js.map +1 -0
- package/dist/categorise.d.ts +66 -0
- package/dist/categorise.d.ts.map +1 -0
- package/dist/categorise.js +106 -0
- package/dist/categorise.js.map +1 -0
- package/dist/claim-cas.d.ts +117 -0
- package/dist/claim-cas.d.ts.map +1 -0
- package/dist/claim-cas.js +312 -0
- package/dist/claim-cas.js.map +1 -0
- package/dist/cli-spinner.d.ts +112 -0
- package/dist/cli-spinner.d.ts.map +1 -0
- package/dist/cli-spinner.js +157 -0
- package/dist/cli-spinner.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3094 -0
- package/dist/cli.js.map +1 -0
- package/dist/close-job-template.d.ts +70 -0
- package/dist/close-job-template.d.ts.map +1 -0
- package/dist/close-job-template.js +180 -0
- package/dist/close-job-template.js.map +1 -0
- package/dist/close-job.d.ts +95 -0
- package/dist/close-job.d.ts.map +1 -0
- package/dist/close-job.js +226 -0
- package/dist/close-job.js.map +1 -0
- package/dist/complete.d.ts +361 -0
- package/dist/complete.d.ts.map +1 -0
- package/dist/complete.js +885 -0
- package/dist/complete.js.map +1 -0
- package/dist/concurrency.d.ts +68 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +112 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/config-override.d.ts +76 -0
- package/dist/config-override.d.ts.map +1 -0
- package/dist/config-override.js +50 -0
- package/dist/config-override.js.map +1 -0
- package/dist/config.d.ts +668 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +241 -0
- package/dist/config.js.map +1 -0
- package/dist/continue-branch.d.ts +249 -0
- package/dist/continue-branch.d.ts.map +1 -0
- package/dist/continue-branch.js +389 -0
- package/dist/continue-branch.js.map +1 -0
- package/dist/cwd-section.d.ts +186 -0
- package/dist/cwd-section.d.ts.map +1 -0
- package/dist/cwd-section.js +209 -0
- package/dist/cwd-section.js.map +1 -0
- package/dist/decision-engine.d.ts +170 -0
- package/dist/decision-engine.d.ts.map +1 -0
- package/dist/decision-engine.js +136 -0
- package/dist/decision-engine.js.map +1 -0
- package/dist/detect.d.ts +17 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +118 -0
- package/dist/detect.js.map +1 -0
- package/dist/do-autopick.d.ts +85 -0
- package/dist/do-autopick.d.ts.map +1 -0
- package/dist/do-autopick.js +112 -0
- package/dist/do-autopick.js.map +1 -0
- package/dist/do-config.d.ts +312 -0
- package/dist/do-config.d.ts.map +1 -0
- package/dist/do-config.js +358 -0
- package/dist/do-config.js.map +1 -0
- package/dist/do-remote-auto.d.ts +75 -0
- package/dist/do-remote-auto.d.ts.map +1 -0
- package/dist/do-remote-auto.js +111 -0
- package/dist/do-remote-auto.js.map +1 -0
- package/dist/do.d.ts +621 -0
- package/dist/do.d.ts.map +1 -0
- package/dist/do.js +1882 -0
- package/dist/do.js.map +1 -0
- package/dist/drop-source.d.ts +96 -0
- package/dist/drop-source.d.ts.map +1 -0
- package/dist/drop-source.js +91 -0
- package/dist/drop-source.js.map +1 -0
- package/dist/eligibility.d.ts +46 -0
- package/dist/eligibility.d.ts.map +1 -0
- package/dist/eligibility.js +34 -0
- package/dist/eligibility.js.map +1 -0
- package/dist/env-config.d.ts +51 -0
- package/dist/env-config.d.ts.map +1 -0
- package/dist/env-config.js +272 -0
- package/dist/env-config.js.map +1 -0
- package/dist/failure-cause.d.ts +70 -0
- package/dist/failure-cause.d.ts.map +1 -0
- package/dist/failure-cause.js +126 -0
- package/dist/failure-cause.js.map +1 -0
- package/dist/format.d.ts +43 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +256 -0
- package/dist/format.js.map +1 -0
- package/dist/frontmatter.d.ts +215 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +345 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/gate-readiness.d.ts +84 -0
- package/dist/gate-readiness.d.ts.map +1 -0
- package/dist/gate-readiness.js +103 -0
- package/dist/gate-readiness.js.map +1 -0
- package/dist/gc.d.ts +165 -0
- package/dist/gc.d.ts.map +1 -0
- package/dist/gc.js +313 -0
- package/dist/gc.js.map +1 -0
- package/dist/gh-failure.d.ts +42 -0
- package/dist/gh-failure.d.ts.map +1 -0
- package/dist/gh-failure.js +49 -0
- package/dist/gh-failure.js.map +1 -0
- package/dist/git.d.ts +75 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +130 -0
- package/dist/git.js.map +1 -0
- package/dist/github.d.ts +187 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js +343 -0
- package/dist/github.js.map +1 -0
- package/dist/harness.d.ts +242 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +157 -0
- package/dist/harness.js.map +1 -0
- package/dist/identity.d.ts +167 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +231 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +147 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/install-ci-branch-protection.d.ts +147 -0
- package/dist/install-ci-branch-protection.d.ts.map +1 -0
- package/dist/install-ci-branch-protection.js +166 -0
- package/dist/install-ci-branch-protection.js.map +1 -0
- package/dist/install-ci-capabilities/advance-lifecycle.d.ts +15 -0
- package/dist/install-ci-capabilities/advance-lifecycle.d.ts.map +1 -0
- package/dist/install-ci-capabilities/advance-lifecycle.js +28 -0
- package/dist/install-ci-capabilities/advance-lifecycle.js.map +1 -0
- package/dist/install-ci-capabilities/close-job.d.ts +13 -0
- package/dist/install-ci-capabilities/close-job.d.ts.map +1 -0
- package/dist/install-ci-capabilities/close-job.js +26 -0
- package/dist/install-ci-capabilities/close-job.js.map +1 -0
- package/dist/install-ci-capabilities/example-noop.d.ts +16 -0
- package/dist/install-ci-capabilities/example-noop.d.ts.map +1 -0
- package/dist/install-ci-capabilities/example-noop.js +23 -0
- package/dist/install-ci-capabilities/example-noop.js.map +1 -0
- package/dist/install-ci-capabilities/intake.d.ts +15 -0
- package/dist/install-ci-capabilities/intake.d.ts.map +1 -0
- package/dist/install-ci-capabilities/intake.js +28 -0
- package/dist/install-ci-capabilities/intake.js.map +1 -0
- package/dist/install-ci-capabilities/verify.d.ts +14 -0
- package/dist/install-ci-capabilities/verify.d.ts.map +1 -0
- package/dist/install-ci-capabilities/verify.js +27 -0
- package/dist/install-ci-capabilities/verify.js.map +1 -0
- package/dist/install-ci-core.d.ts +446 -0
- package/dist/install-ci-core.d.ts.map +1 -0
- package/dist/install-ci-core.js +760 -0
- package/dist/install-ci-core.js.map +1 -0
- package/dist/install-ci-github.d.ts +167 -0
- package/dist/install-ci-github.d.ts.map +1 -0
- package/dist/install-ci-github.js +315 -0
- package/dist/install-ci-github.js.map +1 -0
- package/dist/install-ci.d.ts +105 -0
- package/dist/install-ci.d.ts.map +1 -0
- package/dist/install-ci.js +363 -0
- package/dist/install-ci.js.map +1 -0
- package/dist/intake-event.d.ts +88 -0
- package/dist/intake-event.d.ts.map +1 -0
- package/dist/intake-event.js +66 -0
- package/dist/intake-event.js.map +1 -0
- package/dist/intake-marker.d.ts +95 -0
- package/dist/intake-marker.d.ts.map +1 -0
- package/dist/intake-marker.js +127 -0
- package/dist/intake-marker.js.map +1 -0
- package/dist/intake-triage.d.ts +48 -0
- package/dist/intake-triage.d.ts.map +1 -0
- package/dist/intake-triage.js +95 -0
- package/dist/intake-triage.js.map +1 -0
- package/dist/intake-trigger-template.d.ts +185 -0
- package/dist/intake-trigger-template.d.ts.map +1 -0
- package/dist/intake-trigger-template.js +449 -0
- package/dist/intake-trigger-template.js.map +1 -0
- package/dist/intake.d.ts +569 -0
- package/dist/intake.d.ts.map +1 -0
- package/dist/intake.js +1628 -0
- package/dist/intake.js.map +1 -0
- package/dist/integration-core.d.ts +539 -0
- package/dist/integration-core.d.ts.map +1 -0
- package/dist/integration-core.js +2195 -0
- package/dist/integration-core.js.map +1 -0
- package/dist/integrator.d.ts +343 -0
- package/dist/integrator.d.ts.map +1 -0
- package/dist/integrator.js +400 -0
- package/dist/integrator.js.map +1 -0
- package/dist/isolation.d.ts +219 -0
- package/dist/isolation.d.ts.map +1 -0
- package/dist/isolation.js +261 -0
- package/dist/isolation.js.map +1 -0
- package/dist/issue-provider.d.ts +349 -0
- package/dist/issue-provider.d.ts.map +1 -0
- package/dist/issue-provider.js +360 -0
- package/dist/issue-provider.js.map +1 -0
- package/dist/item-lock.d.ts +626 -0
- package/dist/item-lock.d.ts.map +1 -0
- package/dist/item-lock.js +1381 -0
- package/dist/item-lock.js.map +1 -0
- package/dist/item-path.d.ts +49 -0
- package/dist/item-path.d.ts.map +1 -0
- package/dist/item-path.js +66 -0
- package/dist/item-path.js.map +1 -0
- package/dist/ledger-lint.d.ts +129 -0
- package/dist/ledger-lint.d.ts.map +1 -0
- package/dist/ledger-lint.js +249 -0
- package/dist/ledger-lint.js.map +1 -0
- package/dist/ledger-read.d.ts +357 -0
- package/dist/ledger-read.d.ts.map +1 -0
- package/dist/ledger-read.js +442 -0
- package/dist/ledger-read.js.map +1 -0
- package/dist/ledger-write.d.ts +330 -0
- package/dist/ledger-write.d.ts.map +1 -0
- package/dist/ledger-write.js +411 -0
- package/dist/ledger-write.js.map +1 -0
- package/dist/lifecycle-gather.d.ts +30 -0
- package/dist/lifecycle-gather.d.ts.map +1 -0
- package/dist/lifecycle-gather.js +205 -0
- package/dist/lifecycle-gather.js.map +1 -0
- package/dist/lifecycle-pools.d.ts +180 -0
- package/dist/lifecycle-pools.d.ts.map +1 -0
- package/dist/lifecycle-pools.js +78 -0
- package/dist/lifecycle-pools.js.map +1 -0
- package/dist/merge-question-surfacer.d.ts +166 -0
- package/dist/merge-question-surfacer.d.ts.map +1 -0
- package/dist/merge-question-surfacer.js +297 -0
- package/dist/merge-question-surfacer.js.map +1 -0
- package/dist/mint-adr.d.ts +126 -0
- package/dist/mint-adr.d.ts.map +1 -0
- package/dist/mint-adr.js +257 -0
- package/dist/mint-adr.js.map +1 -0
- package/dist/mirror-pool-scan.d.ts +125 -0
- package/dist/mirror-pool-scan.d.ts.map +1 -0
- package/dist/mirror-pool-scan.js +104 -0
- package/dist/mirror-pool-scan.js.map +1 -0
- package/dist/needs-attention.d.ts +341 -0
- package/dist/needs-attention.d.ts.map +1 -0
- package/dist/needs-attention.js +900 -0
- package/dist/needs-attention.js.map +1 -0
- package/dist/orphan-sidecar.d.ts +79 -0
- package/dist/orphan-sidecar.d.ts.map +1 -0
- package/dist/orphan-sidecar.js +71 -0
- package/dist/orphan-sidecar.js.map +1 -0
- package/dist/output.d.ts +48 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +66 -0
- package/dist/output.js.map +1 -0
- package/dist/pi-harness.d.ts +179 -0
- package/dist/pi-harness.d.ts.map +1 -0
- package/dist/pi-harness.js +342 -0
- package/dist/pi-harness.js.map +1 -0
- package/dist/placement.d.ts +99 -0
- package/dist/placement.d.ts.map +1 -0
- package/dist/placement.js +67 -0
- package/dist/placement.js.map +1 -0
- package/dist/prd-to-spec.d.ts +315 -0
- package/dist/prd-to-spec.d.ts.map +1 -0
- package/dist/prd-to-spec.js +684 -0
- package/dist/prd-to-spec.js.map +1 -0
- package/dist/prepare.d.ts +121 -0
- package/dist/prepare.d.ts.map +1 -0
- package/dist/prepare.js +140 -0
- package/dist/prepare.js.map +1 -0
- package/dist/prompt.d.ts +360 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +499 -0
- package/dist/prompt.js.map +1 -0
- package/dist/protocol/ADR-FORMAT.md +47 -0
- package/dist/protocol/CLAIM-PROTOCOL.md +217 -0
- package/dist/protocol/REVIEW-PROTOCOL.md +119 -0
- package/dist/protocol/SURFACE-PROTOCOL.md +121 -0
- package/dist/protocol/TASKING-PROTOCOL.md +122 -0
- package/dist/protocol/WORK-CONTRACT.md +276 -0
- package/dist/protocol/spec-template.md +71 -0
- package/dist/protocol/task-template.md +65 -0
- package/dist/readiness.d.ts +66 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +36 -0
- package/dist/readiness.js.map +1 -0
- package/dist/reap-branches.d.ts +102 -0
- package/dist/reap-branches.d.ts.map +1 -0
- package/dist/reap-branches.js +149 -0
- package/dist/reap-branches.js.map +1 -0
- package/dist/recover-isolated.d.ts +72 -0
- package/dist/recover-isolated.d.ts.map +1 -0
- package/dist/recover-isolated.js +188 -0
- package/dist/recover-isolated.js.map +1 -0
- package/dist/registry.d.ts +172 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +296 -0
- package/dist/registry.js.map +1 -0
- package/dist/repo-config.d.ts +201 -0
- package/dist/repo-config.d.ts.map +1 -0
- package/dist/repo-config.js +414 -0
- package/dist/repo-config.js.map +1 -0
- package/dist/repo-key.d.ts +20 -0
- package/dist/repo-key.d.ts.map +1 -0
- package/dist/repo-key.js +68 -0
- package/dist/repo-key.js.map +1 -0
- package/dist/repo-mirror.d.ts +177 -0
- package/dist/repo-mirror.d.ts.map +1 -0
- package/dist/repo-mirror.js +271 -0
- package/dist/repo-mirror.js.map +1 -0
- package/dist/retry-backoff.d.ts +90 -0
- package/dist/retry-backoff.d.ts.map +1 -0
- package/dist/retry-backoff.js +98 -0
- package/dist/retry-backoff.js.map +1 -0
- package/dist/review-gate.d.ts +173 -0
- package/dist/review-gate.d.ts.map +1 -0
- package/dist/review-gate.js +261 -0
- package/dist/review-gate.js.map +1 -0
- package/dist/review-verdict.d.ts +149 -0
- package/dist/review-verdict.d.ts.map +1 -0
- package/dist/review-verdict.js +332 -0
- package/dist/review-verdict.js.map +1 -0
- package/dist/run.d.ts +221 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +963 -0
- package/dist/run.js.map +1 -0
- package/dist/scan.d.ts +308 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +374 -0
- package/dist/scan.js.map +1 -0
- package/dist/select-order.d.ts +75 -0
- package/dist/select-order.d.ts.map +1 -0
- package/dist/select-order.js +108 -0
- package/dist/select-order.js.map +1 -0
- package/dist/select-priority.d.ts +188 -0
- package/dist/select-priority.d.ts.map +1 -0
- package/dist/select-priority.js +80 -0
- package/dist/select-priority.js.map +1 -0
- package/dist/select.d.ts +25 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +43 -0
- package/dist/select.js.map +1 -0
- package/dist/session-path.d.ts +36 -0
- package/dist/session-path.d.ts.map +1 -0
- package/dist/session-path.js +129 -0
- package/dist/session-path.js.map +1 -0
- package/dist/sidecar-apply.d.ts +83 -0
- package/dist/sidecar-apply.d.ts.map +1 -0
- package/dist/sidecar-apply.js +111 -0
- package/dist/sidecar-apply.js.map +1 -0
- package/dist/sidecar.d.ts +245 -0
- package/dist/sidecar.d.ts.map +1 -0
- package/dist/sidecar.js +481 -0
- package/dist/sidecar.js.map +1 -0
- package/dist/slug-namespace.d.ts +204 -0
- package/dist/slug-namespace.d.ts.map +1 -0
- package/dist/slug-namespace.js +229 -0
- package/dist/slug-namespace.js.map +1 -0
- package/dist/spec-complete.d.ts +44 -0
- package/dist/spec-complete.d.ts.map +1 -0
- package/dist/spec-complete.js +69 -0
- package/dist/spec-complete.js.map +1 -0
- package/dist/start.d.ts +97 -0
- package/dist/start.d.ts.map +1 -0
- package/dist/start.js +633 -0
- package/dist/start.js.map +1 -0
- package/dist/status.d.ts +199 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +228 -0
- package/dist/status.js.map +1 -0
- package/dist/surface-gate.d.ts +162 -0
- package/dist/surface-gate.d.ts.map +1 -0
- package/dist/surface-gate.js +206 -0
- package/dist/surface-gate.js.map +1 -0
- package/dist/surface-persist.d.ts +86 -0
- package/dist/surface-persist.d.ts.map +1 -0
- package/dist/surface-persist.js +129 -0
- package/dist/surface-persist.js.map +1 -0
- package/dist/tasker-review-loop.d.ts +249 -0
- package/dist/tasker-review-loop.d.ts.map +1 -0
- package/dist/tasker-review-loop.js +369 -0
- package/dist/tasker-review-loop.js.map +1 -0
- package/dist/tasking-eligibility.d.ts +74 -0
- package/dist/tasking-eligibility.d.ts.map +1 -0
- package/dist/tasking-eligibility.js +52 -0
- package/dist/tasking-eligibility.js.map +1 -0
- package/dist/tasking-lock.d.ts +111 -0
- package/dist/tasking-lock.d.ts.map +1 -0
- package/dist/tasking-lock.js +256 -0
- package/dist/tasking-lock.js.map +1 -0
- package/dist/tasking.d.ts +275 -0
- package/dist/tasking.d.ts.map +1 -0
- package/dist/tasking.js +952 -0
- package/dist/tasking.js.map +1 -0
- package/dist/triage-gate.d.ts +127 -0
- package/dist/triage-gate.d.ts.map +1 -0
- package/dist/triage-gate.js +139 -0
- package/dist/triage-gate.js.map +1 -0
- package/dist/triage-persist.d.ts +163 -0
- package/dist/triage-persist.d.ts.map +1 -0
- package/dist/triage-persist.js +387 -0
- package/dist/triage-persist.js.map +1 -0
- package/dist/verdict-json.d.ts +32 -0
- package/dist/verdict-json.d.ts.map +1 -0
- package/dist/verdict-json.js +74 -0
- package/dist/verdict-json.js.map +1 -0
- package/dist/verify-workflow-template.d.ts +60 -0
- package/dist/verify-workflow-template.d.ts.map +1 -0
- package/dist/verify-workflow-template.js +126 -0
- package/dist/verify-workflow-template.js.map +1 -0
- package/dist/verify.d.ts +60 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +62 -0
- package/dist/verify.js.map +1 -0
- package/dist/watch-session.d.ts +112 -0
- package/dist/watch-session.d.ts.map +1 -0
- package/dist/watch-session.js +347 -0
- package/dist/watch-session.js.map +1 -0
- package/dist/work-layout.d.ts +198 -0
- package/dist/work-layout.d.ts.map +1 -0
- package/dist/work-layout.js +217 -0
- package/dist/work-layout.js.map +1 -0
- package/dist/work-on.d.ts +154 -0
- package/dist/work-on.d.ts.map +1 -0
- package/dist/work-on.js +387 -0
- package/dist/work-on.js.map +1 -0
- package/dist/workspace.d.ts +224 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +325 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +46 -2
- package/src/advance-ci-template.ts +203 -0
- package/src/advance-classify.ts +197 -0
- package/src/advance-drivers.ts +414 -0
- package/src/advance-isolated.ts +432 -0
- package/src/advance-lifecycle-template.ts +791 -0
- package/src/advance-loop-driver.ts +745 -0
- package/src/advance-treeless-publish.ts +177 -0
- package/src/advance.ts +1564 -0
- package/src/advancing-lock.ts +988 -0
- package/src/agent-launch.ts +137 -0
- package/src/agent-stop.ts +361 -0
- package/src/apply-decide.ts +242 -0
- package/src/apply-merge-action.ts +502 -0
- package/src/apply-persist.ts +518 -0
- package/src/arbiter.ts +372 -0
- package/src/brand.ts +111 -0
- package/src/buildable-body.ts +196 -0
- package/src/categorise.ts +158 -0
- package/src/claim-cas.ts +513 -0
- package/src/cli-spinner.ts +225 -0
- package/src/cli.ts +4369 -0
- package/src/close-job-template.ts +236 -0
- package/src/close-job.ts +319 -0
- package/src/complete.ts +1379 -0
- package/src/concurrency.ts +151 -0
- package/src/config-override.ts +116 -0
- package/src/config.ts +883 -0
- package/src/continue-branch.ts +542 -0
- package/src/cwd-section.ts +392 -0
- package/src/decision-engine.ts +272 -0
- package/src/detect.ts +124 -0
- package/src/do-autopick.ts +223 -0
- package/src/do-config.ts +589 -0
- package/src/do-remote-auto.ts +197 -0
- package/src/do.ts +2623 -0
- package/src/drop-source.ts +194 -0
- package/src/eligibility.ts +79 -0
- package/src/env-config.ts +305 -0
- package/src/failure-cause.ts +142 -0
- package/src/format.ts +313 -0
- package/src/frontmatter.ts +485 -0
- package/src/gate-readiness.ts +147 -0
- package/src/gc.ts +510 -0
- package/src/gh-failure.ts +53 -0
- package/src/git.ts +186 -0
- package/src/github.ts +468 -0
- package/src/harness.ts +355 -0
- package/src/identity.ts +322 -0
- package/src/index.ts +785 -0
- package/src/install-ci-branch-protection.ts +255 -0
- package/src/install-ci-capabilities/advance-lifecycle.ts +34 -0
- package/src/install-ci-capabilities/close-job.ts +32 -0
- package/src/install-ci-capabilities/example-noop.ts +24 -0
- package/src/install-ci-capabilities/intake.ts +34 -0
- package/src/install-ci-capabilities/verify.ts +33 -0
- package/src/install-ci-core.ts +1088 -0
- package/src/install-ci-github.ts +376 -0
- package/src/install-ci.ts +552 -0
- package/src/intake-event.ts +102 -0
- package/src/intake-marker.ts +195 -0
- package/src/intake-triage.ts +138 -0
- package/src/intake-trigger-template.ts +591 -0
- package/src/intake.ts +2445 -0
- package/src/integration-core.ts +3065 -0
- package/src/integrator.ts +771 -0
- package/src/isolation.ts +484 -0
- package/src/issue-provider.ts +733 -0
- package/src/item-lock.ts +1858 -0
- package/src/item-path.ts +75 -0
- package/src/ledger-lint.ts +332 -0
- package/src/ledger-read.ts +924 -0
- package/src/ledger-write.ts +865 -0
- package/src/lifecycle-gather.ts +298 -0
- package/src/lifecycle-pools.ts +250 -0
- package/src/merge-question-surfacer.ts +496 -0
- package/src/mint-adr.ts +362 -0
- package/src/mirror-pool-scan.ts +240 -0
- package/src/needs-attention.ts +1506 -0
- package/src/orphan-sidecar.ts +150 -0
- package/src/output.ts +89 -0
- package/src/pi-harness.ts +403 -0
- package/src/placement.ts +131 -0
- package/src/prd-to-spec.ts +1023 -0
- package/src/prepare.ts +230 -0
- package/src/prompt.ts +760 -0
- package/src/readiness.ts +98 -0
- package/src/reap-branches.ts +278 -0
- package/src/recover-isolated.ts +276 -0
- package/src/registry.ts +475 -0
- package/src/repo-config.ts +550 -0
- package/src/repo-key.ts +74 -0
- package/src/repo-mirror.ts +367 -0
- package/src/retry-backoff.ts +130 -0
- package/src/review-gate.ts +389 -0
- package/src/review-verdict.ts +422 -0
- package/src/run.ts +1430 -0
- package/src/scan.ts +611 -0
- package/src/select-order.ts +143 -0
- package/src/select-priority.ts +266 -0
- package/src/select.ts +62 -0
- package/src/session-path.ts +153 -0
- package/src/sidecar-apply.ts +216 -0
- package/src/sidecar.ts +700 -0
- package/src/slug-namespace.ts +367 -0
- package/src/spec-complete.ts +118 -0
- package/src/start.ts +974 -0
- package/src/status.ts +441 -0
- package/src/surface-gate.ts +337 -0
- package/src/surface-persist.ts +241 -0
- package/src/tasker-review-loop.ts +671 -0
- package/src/tasking-eligibility.ts +114 -0
- package/src/tasking-lock.ts +416 -0
- package/src/tasking.ts +1438 -0
- package/src/triage-gate.ts +248 -0
- package/src/triage-persist.ts +570 -0
- package/src/verdict-json.ts +73 -0
- package/src/verify-workflow-template.ts +159 -0
- package/src/verify.ts +123 -0
- package/src/watch-session.ts +397 -0
- package/src/work-layout.ts +262 -0
- package/src/work-on.ts +660 -0
- package/src/workspace.ts +502 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `install-ci` VERIFY-WORKFLOW capability (prd
|
|
3
|
+
* `land-time-reverify-and-parallel-merge-ceiling`, task
|
|
4
|
+
* `install-ci-tier1-branch-protection`; Story 11 — the Tier-1 GitHub ceiling).
|
|
5
|
+
* Emits a small `verify.yml` workflow whose ONLY job is `verify` and runs on
|
|
6
|
+
* `pull_request` (and `merge_group`, the Tier-2 forward seam). It produces the
|
|
7
|
+
* GitHub check named exactly {@link VERIFY_CHECK_CONTEXT}, which the
|
|
8
|
+
* branch-protection step ({@link installCIBranchProtectionStep}) names as the
|
|
9
|
+
* required-status check `context`. Co-locating the two strings — the EMITTED
|
|
10
|
+
* job/check name AND the REQUIRED context — in one constant pins the
|
|
11
|
+
* acceptance-criterion "context matches the workflow's job name" by construction
|
|
12
|
+
* (a single test diffs the two).
|
|
13
|
+
*
|
|
14
|
+
* The workflow is deliberately thin: it wires the same composite setup action
|
|
15
|
+
* the other capabilities use, then runs `dorfl verify` — the deterministic shell
|
|
16
|
+
* gate the repo declares in `.dorfl.json`. No agent, no provider secrets, no
|
|
17
|
+
* `gh` mutation; it is read-only with respect to `work/`.
|
|
18
|
+
*
|
|
19
|
+
* `merge_group` is present (with no extra wiring) so a follow-on Tier-2 task
|
|
20
|
+
* (GitHub Merge Queue) can flip the ruleset on without changing this workflow's
|
|
21
|
+
* trigger surface — the forward seam the spec's Applied Answers q3 names.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type {ResolvedCIConfig} from './install-ci-core.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The required-status-check CONTEXT install-ci sets via branch protection AND the
|
|
28
|
+
* job/check name the emitted verify workflow produces. ONE source of truth so
|
|
29
|
+
* the two strings can never drift (the "context matches the workflow's job name"
|
|
30
|
+
* acceptance criterion is enforced by both reading this constant).
|
|
31
|
+
*/
|
|
32
|
+
export const VERIFY_CHECK_CONTEXT = 'verify';
|
|
33
|
+
|
|
34
|
+
/** The capability id (the registry key + the emitted workflow file stem). */
|
|
35
|
+
export const VERIFY_CAPABILITY_ID = 'verify';
|
|
36
|
+
|
|
37
|
+
/** The wizard-facing label for the verify capability. */
|
|
38
|
+
export const VERIFY_CAPABILITY_LABEL =
|
|
39
|
+
'Run the repo `verify` gate on every PR (the Tier-1 GitHub ceiling: produces the required-status check `verify` that branch protection requires)';
|
|
40
|
+
|
|
41
|
+
/** The repo-relative path (under the output base) of the emitted workflow. */
|
|
42
|
+
export const VERIFY_WORKFLOW_PATH = 'workflows/verify.yml';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate the verify workflow YAML. Deterministic: the same config produces
|
|
46
|
+
* byte-identical output. The job is named exactly {@link VERIFY_CHECK_CONTEXT}
|
|
47
|
+
* so the GitHub check it produces matches the required-status `context` install-ci
|
|
48
|
+
* configures on `main` — by construction, not by hand-typed agreement.
|
|
49
|
+
*/
|
|
50
|
+
export function generateVerifyWorkflow(_config: ResolvedCIConfig): string {
|
|
51
|
+
return `\
|
|
52
|
+
# dorfl — the VERIFY workflow (Tier-1 GitHub ceiling; prd
|
|
53
|
+
# land-time-reverify-and-parallel-merge-ceiling, task
|
|
54
|
+
# install-ci-tier1-branch-protection). EMITTED by \`dorfl install-ci\`; the
|
|
55
|
+
# human commits it. DO NOT hand-edit a copy — re-run install-ci to upgrade.
|
|
56
|
+
#
|
|
57
|
+
# This workflow exists SOLELY to produce a GitHub check named \`${VERIFY_CHECK_CONTEXT}\`
|
|
58
|
+
# on every pull request, which install-ci's branch-protection step requires
|
|
59
|
+
# (\`required_status_checks.strict: true\` + a required \`${VERIFY_CHECK_CONTEXT}\` context)
|
|
60
|
+
# so a PR cannot be merged stale. The merge button is disabled until \`${VERIFY_CHECK_CONTEXT}\`
|
|
61
|
+
# reports success on the rebased PR head — closing the PR-merge-time drift window
|
|
62
|
+
# story 7 names.
|
|
63
|
+
#
|
|
64
|
+
# \`merge_group\` is listed so a follow-on Tier-2 task (GitHub Merge Queue) can
|
|
65
|
+
# enable speculative-rebase merging by flipping the ruleset, WITHOUT changing
|
|
66
|
+
# this workflow's trigger surface (the Applied Answer q3 forward seam).
|
|
67
|
+
#
|
|
68
|
+
# CONTEXT NAMING — the job is named EXACTLY \`${VERIFY_CHECK_CONTEXT}\`. install-ci
|
|
69
|
+
# names the same string as the required-context, so they cannot drift. If you
|
|
70
|
+
# rename the job here you MUST re-run install-ci (or the protection's required
|
|
71
|
+
# context will not match this workflow and every PR will block forever).
|
|
72
|
+
#
|
|
73
|
+
# SAFETY (US #9): the running job requests NO \`workflows\` permission and runs
|
|
74
|
+
# only \`dorfl verify\` (a deterministic shell gate; \`pnpm format:check && pnpm
|
|
75
|
+
# build && pnpm test\` for the dorfl repo). It cannot edit \`.github/workflows/**\`
|
|
76
|
+
# or mutate \`work/\`.
|
|
77
|
+
|
|
78
|
+
name: ${VERIFY_CHECK_CONTEXT}
|
|
79
|
+
|
|
80
|
+
on:
|
|
81
|
+
pull_request:
|
|
82
|
+
# Tier-2 forward seam: a follow-on task enables GitHub Merge Queue and the
|
|
83
|
+
# same job re-runs on the speculative-rebase merge-group head, no template edit.
|
|
84
|
+
merge_group:
|
|
85
|
+
|
|
86
|
+
# Serialise overlapping verify runs on the same ref (the latest commit wins —
|
|
87
|
+
# stale runs are cancelled, since \`verify\` is a pure pass/fail gate).
|
|
88
|
+
concurrency:
|
|
89
|
+
group: ${VERIFY_CHECK_CONTEXT}-\${{ github.ref }}
|
|
90
|
+
cancel-in-progress: true
|
|
91
|
+
|
|
92
|
+
# NO \`workflows\` permission (US #9). \`contents: read\` is all the gate needs.
|
|
93
|
+
permissions:
|
|
94
|
+
contents: read
|
|
95
|
+
|
|
96
|
+
jobs:
|
|
97
|
+
# The job name IS the GitHub check context — it must equal VERIFY_CHECK_CONTEXT
|
|
98
|
+
# (\`${VERIFY_CHECK_CONTEXT}\`) or install-ci's branch protection will require a
|
|
99
|
+
# context this workflow does not produce.
|
|
100
|
+
${VERIFY_CHECK_CONTEXT}:
|
|
101
|
+
runs-on: ubuntu-latest
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@v4
|
|
104
|
+
with:
|
|
105
|
+
fetch-depth: 0
|
|
106
|
+
- uses: ./.github/actions/dorfl-setup
|
|
107
|
+
- name: run the repo verify gate
|
|
108
|
+
run: dorfl verify
|
|
109
|
+
`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** A single structural problem found in the generated verify workflow. */
|
|
113
|
+
export interface VerifyWorkflowProblem {
|
|
114
|
+
id: string;
|
|
115
|
+
message: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Result of {@link validateVerifyWorkflow}. */
|
|
119
|
+
export interface VerifyWorkflowValidation {
|
|
120
|
+
ok: boolean;
|
|
121
|
+
problems: VerifyWorkflowProblem[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Structurally validate the verify workflow against the task's acceptance
|
|
126
|
+
* criteria (job name = required context, runs on `pull_request`, ships the
|
|
127
|
+
* `merge_group` seam, no `workflows:` permission).
|
|
128
|
+
*/
|
|
129
|
+
export function validateVerifyWorkflow(text: string): VerifyWorkflowValidation {
|
|
130
|
+
const problems: VerifyWorkflowProblem[] = [];
|
|
131
|
+
const require = (id: string, present: boolean, message: string): void => {
|
|
132
|
+
if (!present) problems.push({id, message});
|
|
133
|
+
};
|
|
134
|
+
const operative = text
|
|
135
|
+
.split('\n')
|
|
136
|
+
.filter((line) => !/^\s*#/.test(line))
|
|
137
|
+
.join('\n');
|
|
138
|
+
|
|
139
|
+
require('job-named-verify-context', new RegExp(
|
|
140
|
+
`^\\s{2}${VERIFY_CHECK_CONTEXT}:\\s*$`,
|
|
141
|
+
'm',
|
|
142
|
+
).test(
|
|
143
|
+
operative,
|
|
144
|
+
), `the workflow must declare a job named exactly \`${VERIFY_CHECK_CONTEXT}\` ` +
|
|
145
|
+
'(its name IS the GitHub check context install-ci requires).');
|
|
146
|
+
require('trigger-pull-request', /\bon:\s*[\s\S]*?\bpull_request\s*:/.test(
|
|
147
|
+
operative,
|
|
148
|
+
), 'must trigger on `pull_request` (the check must run on every PR).');
|
|
149
|
+
require('trigger-merge-group-seam', /\bmerge_group\s*:/.test(
|
|
150
|
+
operative,
|
|
151
|
+
), 'must list `merge_group` as a trigger (the Tier-2 GitHub Merge Queue forward seam).');
|
|
152
|
+
require('no-workflows-permission', !/\bworkflows\s*:\s*write\b/.test(
|
|
153
|
+
operative,
|
|
154
|
+
), 'must request NO `workflows` permission (US #9).');
|
|
155
|
+
require('runs-dorfl-verify', /dorfl verify\b/.test(
|
|
156
|
+
operative,
|
|
157
|
+
), 'the job must run `dorfl verify` (the deterministic shell gate the repo declares).');
|
|
158
|
+
return {ok: problems.length === 0, problems};
|
|
159
|
+
}
|
package/src/verify.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import {spawn} from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The per-repo acceptance gate, run as `dorfl verify`. It is a
|
|
5
|
+
* deterministic shell gate — a declared, per-repo command (or ordered list of
|
|
6
|
+
* commands) — NOT prose interpreted by a model. There is no LLM in this loop:
|
|
7
|
+
* the gate is known by reading config, and it either passes (exit 0) or it does
|
|
8
|
+
* not. This same mechanism is consumed by the human `complete` command (where it
|
|
9
|
+
* is a default-on, `--skip-verify`-able safety-net) and by the autonomous
|
|
10
|
+
* `run-once`/`watch` (where it is the authoritative, non-skippable trust
|
|
11
|
+
* boundary). `verify` itself just runs the gate; callers decide authority.
|
|
12
|
+
*
|
|
13
|
+
* It is read-only with respect to `work/`: it runs the declared check and never
|
|
14
|
+
* moves or commits anything.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The per-repo gate, as declared in config. A single shell command, or an
|
|
19
|
+
* ordered list of commands run in sequence (each must pass for the gate to
|
|
20
|
+
* pass). `undefined` (unset) ⇒ {@link DEFAULT_VERIFY_COMMAND} is used.
|
|
21
|
+
*/
|
|
22
|
+
export type VerifyConfig = string | string[];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The sensible default when no `verify` is configured: build, then test, then
|
|
26
|
+
* check formatting across the workspace. Deterministic, auditable, no model.
|
|
27
|
+
*/
|
|
28
|
+
export const DEFAULT_VERIFY_COMMAND =
|
|
29
|
+
'pnpm -r build && pnpm -r test && pnpm -r format:check';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the declared gate into an ordered, non-empty list of shell commands.
|
|
33
|
+
* Unset ⇒ the default. A string ⇒ a single command. A list ⇒ the list, with
|
|
34
|
+
* blank/whitespace-only entries dropped (and the default substituted if that
|
|
35
|
+
* leaves nothing). Each command is run in sequence; all must pass.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveVerifyCommands(
|
|
38
|
+
verify: VerifyConfig | undefined,
|
|
39
|
+
): string[] {
|
|
40
|
+
if (verify === undefined) {
|
|
41
|
+
return [DEFAULT_VERIFY_COMMAND];
|
|
42
|
+
}
|
|
43
|
+
const list = Array.isArray(verify) ? verify : [verify];
|
|
44
|
+
const commands = list.filter((command) => command.trim() !== '');
|
|
45
|
+
return commands.length > 0 ? commands : [DEFAULT_VERIFY_COMMAND];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface RunVerifyOptions {
|
|
49
|
+
/** The repo to run the gate in (its working directory). */
|
|
50
|
+
cwd: string;
|
|
51
|
+
/** The declared gate (string | list). Unset ⇒ the default command. */
|
|
52
|
+
verify?: VerifyConfig;
|
|
53
|
+
/** Environment for the gate's child processes. */
|
|
54
|
+
env?: NodeJS.ProcessEnv;
|
|
55
|
+
/** Sink for streamed stdout chunks. Defaults to `process.stdout.write`. */
|
|
56
|
+
onStdout?: (chunk: string) => void;
|
|
57
|
+
/** Sink for streamed stderr chunks. Defaults to `process.stderr.write`. */
|
|
58
|
+
onStderr?: (chunk: string) => void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface RunVerifyResult {
|
|
62
|
+
/** The exit code of the gate: 0 iff every command passed. */
|
|
63
|
+
exitCode: number;
|
|
64
|
+
/** The ordered commands that were run (resolved from config). */
|
|
65
|
+
commands: string[];
|
|
66
|
+
/** Whether the gate passed (exitCode === 0). */
|
|
67
|
+
passed: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Run the resolved gate command(s) in `cwd`, streaming output, and resolve with
|
|
72
|
+
* the gate's status: exit 0 iff every command passed. Commands run in sequence;
|
|
73
|
+
* the first non-zero exit short-circuits the rest and becomes the result
|
|
74
|
+
* (mirroring `&&` semantics). Each command is run through `bash -c` so that
|
|
75
|
+
* shell operators in the declared gate (`&&`, pipes, etc.) behave as written.
|
|
76
|
+
*/
|
|
77
|
+
export async function runVerify(
|
|
78
|
+
options: RunVerifyOptions,
|
|
79
|
+
): Promise<RunVerifyResult> {
|
|
80
|
+
const commands = resolveVerifyCommands(options.verify);
|
|
81
|
+
const onStdout =
|
|
82
|
+
options.onStdout ?? ((chunk: string) => process.stdout.write(chunk));
|
|
83
|
+
const onStderr =
|
|
84
|
+
options.onStderr ?? ((chunk: string) => process.stderr.write(chunk));
|
|
85
|
+
|
|
86
|
+
for (const command of commands) {
|
|
87
|
+
const exitCode = await runOne(command, options.cwd, options.env, {
|
|
88
|
+
onStdout,
|
|
89
|
+
onStderr,
|
|
90
|
+
});
|
|
91
|
+
if (exitCode !== 0) {
|
|
92
|
+
return {exitCode, commands, passed: false};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {exitCode: 0, commands, passed: true};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Spawn one command via `bash -c`, streaming its output, resolving its code. */
|
|
99
|
+
function runOne(
|
|
100
|
+
command: string,
|
|
101
|
+
cwd: string,
|
|
102
|
+
env: NodeJS.ProcessEnv | undefined,
|
|
103
|
+
sinks: {onStdout: (chunk: string) => void; onStderr: (chunk: string) => void},
|
|
104
|
+
): Promise<number> {
|
|
105
|
+
return new Promise((resolvePromise, reject) => {
|
|
106
|
+
const child = spawn('bash', ['-c', command], {
|
|
107
|
+
cwd,
|
|
108
|
+
env: env ?? process.env,
|
|
109
|
+
});
|
|
110
|
+
child.stdout.on('data', (chunk: Buffer) => {
|
|
111
|
+
sinks.onStdout(chunk.toString('utf8'));
|
|
112
|
+
});
|
|
113
|
+
child.stderr.on('data', (chunk: Buffer) => {
|
|
114
|
+
sinks.onStderr(chunk.toString('utf8'));
|
|
115
|
+
});
|
|
116
|
+
child.on('error', (err) =>
|
|
117
|
+
reject(new Error(`failed to spawn gate command: ${err.message}`)),
|
|
118
|
+
);
|
|
119
|
+
child.on('close', (code) => {
|
|
120
|
+
resolvePromise(code ?? -1);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import {open, type FileHandle} from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The **`do --watch` observer** (tasks `do-watch` + `do-watch-session-log-format`)
|
|
5
|
+
* — a READ-ONLY concurrent tail of the pi session `.jsonl` event log, restoring
|
|
6
|
+
* for `do` the live agent-conversation view `ar-run.sh --watch` gave.
|
|
7
|
+
*
|
|
8
|
+
* `ar-run.sh --watch` piped `pi -p --mode json | jq` to surface the high-signal
|
|
9
|
+
* events; `do` runs the agent CAPTURED through the harness seam, so that live
|
|
10
|
+
* view was lost. The pi adapter writes a session `.jsonl` at the KNOWN file path
|
|
11
|
+
* the caller generated (`--session <path>`, task `session-path-pi-default`) —
|
|
12
|
+
* but that is pi's SESSION-PERSISTENCE log, a DIFFERENT format from the
|
|
13
|
+
* `--mode json` STREAM `ar-run.sh` piped. The tailer is pointed at that EXACT
|
|
14
|
+
* path (no newest-by-mtime dir guessing), so the stale-sibling race is gone.
|
|
15
|
+
*
|
|
16
|
+
* A session-log record is `{"type":"message", "message":{role, content[]}}` (or a
|
|
17
|
+
* `session`/`model_change`/`thinking_level_change` preamble record). The earlier
|
|
18
|
+
* classifier matched the STREAM vocabulary (`tool_start`/`message_end`/`agent_end`),
|
|
19
|
+
* none of which occur in the session log — so every line fell through to skip and
|
|
20
|
+
* `do --watch` was silently a no-op. This module tails the growing session log and
|
|
21
|
+
* surfaces the same high-signal level `ar-run.sh --watch` gave, read off the
|
|
22
|
+
* SESSION-LOG shape:
|
|
23
|
+
*
|
|
24
|
+
* - an `assistant` `message` → its `content[]` `text` parts, then `▶ <name>`
|
|
25
|
+
* (cyan) per `content[]` `toolCall` part;
|
|
26
|
+
* - `✓ agent finished` (green) on PROCESS EXIT — the session log has no
|
|
27
|
+
* `agent_end` event, so the tailer emits it once on `stop()`;
|
|
28
|
+
* - everything else (`session`/`model_change`/user messages/tool results/…) →
|
|
29
|
+
* skipped.
|
|
30
|
+
*
|
|
31
|
+
* The classifier mirrors pi-remote's `session-pool.ts` reference parser (`type` →
|
|
32
|
+
* `role` → `content[]` block walk over `thinking`/`text`/`toolCall`, incl.
|
|
33
|
+
* `tc.name||tc.toolName` and content-as-string). The JSONL is parsed in TS (NO
|
|
34
|
+
* `jq` — that bash dependency is part of what `do` replaces). It is a PURE
|
|
35
|
+
* OBSERVER: it only READS the log; it never changes the run's outcome, gate,
|
|
36
|
+
* integration, git, or exit code. Colour is honoured ONLY on a TTY / when
|
|
37
|
+
* `NO_COLOR` is unset (the same rule as `output.ts`), threaded in by the caller
|
|
38
|
+
* as a boolean.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
// The same conventional ANSI codes `ar-run.sh`'s jq filter emitted (cyan tool
|
|
42
|
+
// marker, green finished marker, reset). Colour is applied ONLY when the caller
|
|
43
|
+
// passes `color: true` (its TTY/NO_COLOR decision); plain otherwise.
|
|
44
|
+
const CYAN = '\u001b[36m';
|
|
45
|
+
const GREEN = '\u001b[32m';
|
|
46
|
+
const RESET = '\u001b[0m';
|
|
47
|
+
|
|
48
|
+
/** Wrap `text` in `code` + reset when `color`, else return it unchanged. */
|
|
49
|
+
function paint(text: string, color: boolean, code: string): string {
|
|
50
|
+
return color ? `${code}${text}${RESET}` : text;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A structural view of the pi SESSION-LOG records we classify. It mirrors the
|
|
55
|
+
* `@earendil-works/pi-coding-agent` `SessionEntry` / `SessionMessageEntry`
|
|
56
|
+
* shapes (`type:"message"` with a nested `message.role` + `message.content`) but
|
|
57
|
+
* is declared locally so the observer needs no runtime dependency on that
|
|
58
|
+
* package's (heavy) type tree — dorfl does not otherwise depend on it.
|
|
59
|
+
* The content blocks are read defensively (like pi-remote's reference parser,
|
|
60
|
+
* which casts them via `as any`): a content part may be a `text`, a `thinking`,
|
|
61
|
+
* or a `toolCall` (whose name is `name` OR `toolName`), or the whole content may
|
|
62
|
+
* be a plain string.
|
|
63
|
+
*/
|
|
64
|
+
interface SessionLogRecord {
|
|
65
|
+
type?: unknown;
|
|
66
|
+
message?: {
|
|
67
|
+
role?: unknown;
|
|
68
|
+
content?: unknown;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The green `✓ agent finished` line, emitted on PROCESS EXIT (not a log event). */
|
|
73
|
+
export function finishedLine(color: boolean): string {
|
|
74
|
+
return paint('✓ agent finished', color, GREEN);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A one-line VISUAL BOUNDARY banner the caller prints between two tailed agent
|
|
79
|
+
* streams (e.g. the build stream and the Gate-2 review stream — task
|
|
80
|
+
* `watch-review-session`), so a watching human knows the first stream ended and
|
|
81
|
+
* the next began. Reuses this module's colour rule (cyan, the same marker hue as
|
|
82
|
+
* the `▶ <tool>` lines) so it reads as part of the same live view. Pure: returns
|
|
83
|
+
* the string; the caller routes it to the watch sink.
|
|
84
|
+
*/
|
|
85
|
+
export function boundaryLine(label: string, color: boolean): string {
|
|
86
|
+
return paint(`▶ ${label}`, color, CYAN);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Parse ONE pi SESSION-LOG `.jsonl` line and return the high-signal lines to
|
|
91
|
+
* surface — or `[]` to SKIP it. This is the pure, unit-testable classifier,
|
|
92
|
+
* mirroring pi-remote's `session-pool.ts` block walk:
|
|
93
|
+
*
|
|
94
|
+
* - a `{type:"message", message:{role:"assistant", content}}` record → for each
|
|
95
|
+
* `content[]` part: a `text` part yields its text (parts concatenated into
|
|
96
|
+
* ONE line, like the old `jq` `.content[]` select); a `toolCall` part yields
|
|
97
|
+
* `▶ <name>` (cyan; `name` OR `toolName`, defaulting to `tool`); a `thinking`
|
|
98
|
+
* part (and anything else) is skipped. A plain-string `content` yields one
|
|
99
|
+
* text line.
|
|
100
|
+
* - everything else (`session`/`model_change`/`thinking_level_change`, user
|
|
101
|
+
* messages, tool results, blank/malformed lines) → `[]` (skipped).
|
|
102
|
+
*
|
|
103
|
+
* "Finished" is NOT a record here — the session log has no `agent_end`; the
|
|
104
|
+
* tailer emits {@link finishedLine} on process exit instead.
|
|
105
|
+
*
|
|
106
|
+
* Malformed JSON is skipped (not thrown): a partially-written trailing line in a
|
|
107
|
+
* growing log must not crash the observer.
|
|
108
|
+
*/
|
|
109
|
+
export function formatWatchEvent(line: string, color: boolean): string[] {
|
|
110
|
+
const trimmed = line.trim();
|
|
111
|
+
if (trimmed === '') {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
let event: unknown;
|
|
115
|
+
try {
|
|
116
|
+
event = JSON.parse(trimmed);
|
|
117
|
+
} catch {
|
|
118
|
+
// A half-written trailing line in a growing log — skip, never throw.
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
if (typeof event !== 'object' || event === null) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
const record = event as SessionLogRecord;
|
|
125
|
+
if (record.type !== 'message') {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
const message = record.message;
|
|
129
|
+
if (!message || message.role !== 'assistant') {
|
|
130
|
+
// Skip user turns, tool results, and any non-assistant message.
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
return assistantLines(message.content, color);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Walk an assistant `message.content` into the lines to surface, mirroring
|
|
138
|
+
* pi-remote's `session-pool.ts` reference parser. The `text` parts are
|
|
139
|
+
* concatenated into a SINGLE leading line (so a multi-part assistant turn reads
|
|
140
|
+
* as one sentence, as `ar-run.sh --watch` showed it); each `toolCall` part adds a
|
|
141
|
+
* `▶ <name>` line after it. A plain-string content yields the single text line.
|
|
142
|
+
* `thinking` parts and any other block type are skipped.
|
|
143
|
+
*/
|
|
144
|
+
function assistantLines(content: unknown, color: boolean): string[] {
|
|
145
|
+
if (typeof content === 'string') {
|
|
146
|
+
return content === '' ? [] : [content];
|
|
147
|
+
}
|
|
148
|
+
if (!Array.isArray(content)) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
const lines: string[] = [];
|
|
152
|
+
const text = assistantContentText(content);
|
|
153
|
+
for (const part of content) {
|
|
154
|
+
if (typeof part !== 'object' || part === null) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const p = part as Record<string, unknown>;
|
|
158
|
+
if (p.type === 'toolCall') {
|
|
159
|
+
const name =
|
|
160
|
+
(typeof p.name === 'string' && p.name !== '' && p.name) ||
|
|
161
|
+
(typeof p.toolName === 'string' && p.toolName !== '' && p.toolName) ||
|
|
162
|
+
'tool';
|
|
163
|
+
lines.push(paint(`▶ ${name}`, color, CYAN));
|
|
164
|
+
}
|
|
165
|
+
// `text` is collected by assistantContentText; `thinking` and any other
|
|
166
|
+
// block type are skipped.
|
|
167
|
+
}
|
|
168
|
+
return text === '' ? lines : [text, ...lines];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Concatenate the `text` parts of ONE assistant `message.content` (the answer
|
|
173
|
+
* text, with tool/thinking blocks dropped) — the single source of truth for
|
|
174
|
+
* "what did the assistant SAY" shared by the {@link assistantLines} watch view
|
|
175
|
+
* and the {@link lastAssistantText} output reader (task `harness-agent-output`).
|
|
176
|
+
* A plain-string content is itself the text. Returns `''` when there is no text.
|
|
177
|
+
*/
|
|
178
|
+
function assistantContentText(content: unknown): string {
|
|
179
|
+
if (typeof content === 'string') {
|
|
180
|
+
return content;
|
|
181
|
+
}
|
|
182
|
+
if (!Array.isArray(content)) {
|
|
183
|
+
return '';
|
|
184
|
+
}
|
|
185
|
+
const textParts: string[] = [];
|
|
186
|
+
for (const part of content) {
|
|
187
|
+
if (typeof part !== 'object' || part === null) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const p = part as Record<string, unknown>;
|
|
191
|
+
if (p.type === 'text' && typeof p.text === 'string') {
|
|
192
|
+
textParts.push(p.text);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return textParts.join('');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Extract the **last assistant message's text** from a pi session `.jsonl`
|
|
200
|
+
* (task `harness-agent-output`) — the agent's final ANSWER, surfaced through
|
|
201
|
+
* the harness seam as `LaunchResult.output`. It REUSES this module's session-log
|
|
202
|
+
* shape walk (one parser, not two): it scans the `{type:"message",
|
|
203
|
+
* message:{role:"assistant", content[]}}` records, takes the LAST one carrying
|
|
204
|
+
* non-empty `text`, and returns its concatenated text.
|
|
205
|
+
*
|
|
206
|
+
* Returns `undefined` when the log has no assistant text at all — an absent log,
|
|
207
|
+
* a malformed/short log, or a run whose only assistant turn was tool-calls (no
|
|
208
|
+
* `text` part). A tool-only assistant turn is NOT a final answer, so a LATER
|
|
209
|
+
* text turn (if any) wins; with no text turn anywhere the result is `undefined`.
|
|
210
|
+
*
|
|
211
|
+
* Malformed/blank lines are skipped (never thrown): a partially-written trailing
|
|
212
|
+
* line in a just-closed log must not crash the reader.
|
|
213
|
+
*/
|
|
214
|
+
export function lastAssistantText(jsonl: string): string | undefined {
|
|
215
|
+
let last: string | undefined;
|
|
216
|
+
for (const line of jsonl.split('\n')) {
|
|
217
|
+
const trimmed = line.trim();
|
|
218
|
+
if (trimmed === '') {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
let event: unknown;
|
|
222
|
+
try {
|
|
223
|
+
event = JSON.parse(trimmed);
|
|
224
|
+
} catch {
|
|
225
|
+
continue; // a half-written / malformed line — skip, never throw.
|
|
226
|
+
}
|
|
227
|
+
if (typeof event !== 'object' || event === null) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const record = event as SessionLogRecord;
|
|
231
|
+
if (record.type !== 'message') {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const message = record.message;
|
|
235
|
+
if (!message || message.role !== 'assistant') {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const text = assistantContentText(message.content);
|
|
239
|
+
if (text !== '') {
|
|
240
|
+
last = text; // a later text turn supersedes an earlier one.
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return last;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export interface SessionTailerOptions {
|
|
247
|
+
/**
|
|
248
|
+
* The EXACT pi session `.jsonl` file path to tail. The caller generated this
|
|
249
|
+
* BEFORE pi launched (`--session <path>`, task `session-path-pi-default`) and
|
|
250
|
+
* passed the SAME path to the adapter, so the tailer reads the run's own log
|
|
251
|
+
* — no newest-by-mtime dir guessing, no stale-sibling race. The file does NOT
|
|
252
|
+
* yet exist when the tailer starts (pi creates it asynchronously after spawn);
|
|
253
|
+
* the tailer polls until it appears (ENOENT is tolerated as "not yet").
|
|
254
|
+
*/
|
|
255
|
+
sessionFile: string;
|
|
256
|
+
/** Emit ANSI colour (the caller's TTY/`NO_COLOR` decision). */
|
|
257
|
+
color: boolean;
|
|
258
|
+
/** Where formatted high-signal lines are written (defaults to stderr). */
|
|
259
|
+
sink?: (line: string) => void;
|
|
260
|
+
/** How often (ms) to poll for new bytes / the log's appearance. */
|
|
261
|
+
pollIntervalMs?: number;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const DEFAULT_POLL_INTERVAL_MS = 100;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* A concurrent, READ-ONLY tail of the pi session `.jsonl`. It polls the KNOWN
|
|
268
|
+
* session-file path (which the pi adapter creates only once pi starts) until it
|
|
269
|
+
* appears, then reads newly-appended bytes, splits them into complete lines, and
|
|
270
|
+
* surfaces each via {@link formatWatchEvent}. A partial trailing line is
|
|
271
|
+
* buffered until its newline arrives, so an event split across two reads is
|
|
272
|
+
* never mis-parsed.
|
|
273
|
+
*
|
|
274
|
+
* It is a pure observer: nothing it does feeds back into the run. `start()`
|
|
275
|
+
* begins polling; `stop()` performs one final drain (to catch events written
|
|
276
|
+
* just before the agent exited), emits the `✓ agent finished` line ONCE (the
|
|
277
|
+
* session log has no `agent_end` — process exit IS the finish signal), and
|
|
278
|
+
* releases the file handle. Created and driven by `do --watch` ALONGSIDE the
|
|
279
|
+
* (async) agent launch; `stop()` runs when that launch returns (i.e. the agent
|
|
280
|
+
* process exited).
|
|
281
|
+
*/
|
|
282
|
+
export class SessionTailer {
|
|
283
|
+
private readonly sessionFile: string;
|
|
284
|
+
private readonly color: boolean;
|
|
285
|
+
private readonly sink: (line: string) => void;
|
|
286
|
+
private readonly pollIntervalMs: number;
|
|
287
|
+
|
|
288
|
+
private handle: FileHandle | undefined;
|
|
289
|
+
private offset = 0;
|
|
290
|
+
private pending = '';
|
|
291
|
+
private timer: NodeJS.Timeout | undefined;
|
|
292
|
+
private polling = false;
|
|
293
|
+
private stopped = false;
|
|
294
|
+
private finishedEmitted = false;
|
|
295
|
+
|
|
296
|
+
constructor(options: SessionTailerOptions) {
|
|
297
|
+
this.sessionFile = options.sessionFile;
|
|
298
|
+
this.color = options.color;
|
|
299
|
+
this.sink = options.sink ?? ((line) => process.stderr.write(`${line}\n`));
|
|
300
|
+
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Begin polling for the known session file and tailing its new bytes. */
|
|
304
|
+
start(): void {
|
|
305
|
+
if (this.timer !== undefined || this.stopped) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
this.timer = setInterval(() => {
|
|
309
|
+
void this.poll();
|
|
310
|
+
}, this.pollIntervalMs);
|
|
311
|
+
// `unref` so an orphaned timer never keeps the process alive past the run.
|
|
312
|
+
this.timer.unref?.();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Stop tailing: one final drain (events written right before the agent exited
|
|
317
|
+
* must still be surfaced), emit the `✓ agent finished` line ONCE (process exit
|
|
318
|
+
* is the finish signal — the session log carries no `agent_end`), then release
|
|
319
|
+
* the handle. Idempotent: a second `stop()` re-emits nothing.
|
|
320
|
+
*/
|
|
321
|
+
async stop(): Promise<void> {
|
|
322
|
+
const wasStopped = this.stopped;
|
|
323
|
+
this.stopped = true;
|
|
324
|
+
if (this.timer !== undefined) {
|
|
325
|
+
clearInterval(this.timer);
|
|
326
|
+
this.timer = undefined;
|
|
327
|
+
}
|
|
328
|
+
if (!wasStopped) {
|
|
329
|
+
await this.drain();
|
|
330
|
+
}
|
|
331
|
+
if (this.handle !== undefined) {
|
|
332
|
+
await this.handle.close();
|
|
333
|
+
this.handle = undefined;
|
|
334
|
+
}
|
|
335
|
+
if (!this.finishedEmitted) {
|
|
336
|
+
this.finishedEmitted = true;
|
|
337
|
+
this.sink(finishedLine(this.color));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** One poll tick: ensure the handle is open, then drain new bytes. */
|
|
342
|
+
private async poll(): Promise<void> {
|
|
343
|
+
if (this.polling) {
|
|
344
|
+
return; // a slow read must not overlap the next tick.
|
|
345
|
+
}
|
|
346
|
+
this.polling = true;
|
|
347
|
+
try {
|
|
348
|
+
await this.drain();
|
|
349
|
+
} catch {
|
|
350
|
+
// A transient read error (the log being rotated/created) — try next tick.
|
|
351
|
+
} finally {
|
|
352
|
+
this.polling = false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** Open the log if not yet open, read appended bytes, surface whole lines. */
|
|
357
|
+
private async drain(): Promise<void> {
|
|
358
|
+
if (this.handle === undefined) {
|
|
359
|
+
// Open the KNOWN path. pi creates it ASYNCHRONOUSLY after spawn, so until
|
|
360
|
+
// it exists `open` throws ENOENT — treat that as "not yet" and retry next
|
|
361
|
+
// tick (NEVER open once at start(), which would race the file into being).
|
|
362
|
+
try {
|
|
363
|
+
this.handle = await open(this.sessionFile, 'r');
|
|
364
|
+
} catch (err) {
|
|
365
|
+
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
366
|
+
return; // file not written yet — wait for it.
|
|
367
|
+
}
|
|
368
|
+
throw err; // a real error — bubble to poll's try/catch (next-tick retry).
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const stats = await this.handle.stat();
|
|
372
|
+
if (stats.size <= this.offset) {
|
|
373
|
+
return; // no new bytes.
|
|
374
|
+
}
|
|
375
|
+
const length = stats.size - this.offset;
|
|
376
|
+
const buffer = Buffer.alloc(length);
|
|
377
|
+
const {bytesRead} = await this.handle.read(buffer, 0, length, this.offset);
|
|
378
|
+
this.offset += bytesRead;
|
|
379
|
+
this.pending += buffer.toString('utf8', 0, bytesRead);
|
|
380
|
+
this.flushLines();
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Split the buffer on newlines, surfacing each COMPLETE line; keep the rest. */
|
|
384
|
+
private flushLines(): void {
|
|
385
|
+
let newlineIndex = this.pending.indexOf('\n');
|
|
386
|
+
while (newlineIndex !== -1) {
|
|
387
|
+
const line = this.pending.slice(0, newlineIndex);
|
|
388
|
+
this.pending = this.pending.slice(newlineIndex + 1);
|
|
389
|
+
// A single record may surface MULTIPLE lines (assistant text + each of
|
|
390
|
+
// its tool starts), or none (skipped record types).
|
|
391
|
+
for (const formatted of formatWatchEvent(line, this.color)) {
|
|
392
|
+
this.sink(formatted);
|
|
393
|
+
}
|
|
394
|
+
newlineIndex = this.pending.indexOf('\n');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|