wingfoil 0.2.2 → 0.3.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/README.md +83 -43
- package/dist/agent/discovery.d.ts +72 -0
- package/dist/agent/discovery.d.ts.map +1 -0
- package/dist/agent/discovery.js +210 -0
- package/dist/agent/discovery.js.map +1 -0
- package/dist/agent/execute.d.ts +206 -0
- package/dist/agent/execute.d.ts.map +1 -0
- package/dist/agent/execute.js +581 -0
- package/dist/agent/execute.js.map +1 -0
- package/dist/agent/index.d.ts +23 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +85 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/launch.d.ts +133 -0
- package/dist/agent/launch.d.ts.map +1 -0
- package/dist/agent/launch.js +380 -0
- package/dist/agent/launch.js.map +1 -0
- package/dist/agent/manifest.d.ts +30 -0
- package/dist/agent/manifest.d.ts.map +1 -0
- package/dist/agent/manifest.js +93 -0
- package/dist/agent/manifest.js.map +1 -0
- package/dist/agent/placeholders.d.ts +49 -0
- package/dist/agent/placeholders.d.ts.map +1 -0
- package/dist/agent/placeholders.js +177 -0
- package/dist/agent/placeholders.js.map +1 -0
- package/dist/agent/run-log.d.ts +254 -0
- package/dist/agent/run-log.d.ts.map +1 -0
- package/dist/agent/run-log.js +612 -0
- package/dist/agent/run-log.js.map +1 -0
- package/dist/agent/schema.d.ts +100 -0
- package/dist/agent/schema.d.ts.map +1 -0
- package/dist/agent/schema.js +89 -0
- package/dist/agent/schema.js.map +1 -0
- package/dist/agent/step.d.ts +41 -0
- package/dist/agent/step.d.ts.map +1 -0
- package/dist/agent/step.js +95 -0
- package/dist/agent/step.js.map +1 -0
- package/dist/build-info.json +4 -0
- package/dist/cli/error.d.ts +11 -2
- package/dist/cli/error.d.ts.map +1 -1
- package/dist/cli/error.js +27 -6
- package/dist/cli/error.js.map +1 -1
- package/dist/cli/exit.d.ts +33 -3
- package/dist/cli/exit.d.ts.map +1 -1
- package/dist/cli/exit.js +54 -3
- package/dist/cli/exit.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init-command.d.ts.map +1 -1
- package/dist/cli/init-command.js +4 -2
- package/dist/cli/init-command.js.map +1 -1
- package/dist/cli/mcp-command.d.ts +13 -4
- package/dist/cli/mcp-command.d.ts.map +1 -1
- package/dist/cli/mcp-command.js +25 -8
- package/dist/cli/mcp-command.js.map +1 -1
- package/dist/cli/output.d.ts +12 -4
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +15 -4
- package/dist/cli/output.js.map +1 -1
- package/dist/cli/program.d.ts +1 -1
- package/dist/cli/program.d.ts.map +1 -1
- package/dist/cli/program.js +162 -40
- package/dist/cli/program.js.map +1 -1
- package/dist/cli/registrar.d.ts +1 -1
- package/dist/cli/registrar.d.ts.map +1 -1
- package/dist/cli/registrar.js +67 -6
- package/dist/cli/registrar.js.map +1 -1
- package/dist/cli/suggest.d.ts +20 -0
- package/dist/cli/suggest.d.ts.map +1 -0
- package/dist/cli/suggest.js +49 -0
- package/dist/cli/suggest.js.map +1 -0
- package/dist/cli/warning.d.ts +26 -0
- package/dist/cli/warning.d.ts.map +1 -0
- package/dist/cli/warning.js +66 -0
- package/dist/cli/warning.js.map +1 -0
- package/dist/cli.js +11 -2
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-execute.d.ts +44 -0
- package/dist/core/agent-execute.d.ts.map +1 -0
- package/dist/core/agent-execute.js +71 -0
- package/dist/core/agent-execute.js.map +1 -0
- package/dist/core/agent-list.d.ts +95 -0
- package/dist/core/agent-list.d.ts.map +1 -0
- package/dist/core/agent-list.js +207 -0
- package/dist/core/agent-list.js.map +1 -0
- package/dist/core/agent-show.d.ts +44 -0
- package/dist/core/agent-show.d.ts.map +1 -0
- package/dist/core/agent-show.js +188 -0
- package/dist/core/agent-show.js.map +1 -0
- package/dist/core/approval-authority.d.ts +17 -14
- package/dist/core/approval-authority.d.ts.map +1 -1
- package/dist/core/approval-authority.js +9 -9
- package/dist/core/approval-authority.js.map +1 -1
- package/dist/core/builtin-asset.d.ts +18 -15
- package/dist/core/builtin-asset.d.ts.map +1 -1
- package/dist/core/builtin-asset.js +17 -14
- package/dist/core/builtin-asset.js.map +1 -1
- package/dist/core/builtin-integrity.d.ts +12 -2
- package/dist/core/builtin-integrity.d.ts.map +1 -1
- package/dist/core/builtin-integrity.js +85 -9
- package/dist/core/builtin-integrity.js.map +1 -1
- package/dist/core/confinement.d.ts +3 -1
- package/dist/core/confinement.d.ts.map +1 -1
- package/dist/core/confinement.js +7 -4
- package/dist/core/confinement.js.map +1 -1
- package/dist/core/context.d.ts +150 -98
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +602 -66
- package/dist/core/context.js.map +1 -1
- package/dist/core/directive-assign.d.ts +33 -7
- package/dist/core/directive-assign.d.ts.map +1 -1
- package/dist/core/directive-assign.js +67 -27
- package/dist/core/directive-assign.js.map +1 -1
- package/dist/core/directive-scope.d.ts +36 -0
- package/dist/core/directive-scope.d.ts.map +1 -0
- package/dist/core/directive-scope.js +34 -0
- package/dist/core/directive-scope.js.map +1 -0
- package/dist/core/directives-list.d.ts +13 -3
- package/dist/core/directives-list.d.ts.map +1 -1
- package/dist/core/directives-list.js +18 -6
- package/dist/core/directives-list.js.map +1 -1
- package/dist/core/dry-run.d.ts +33 -0
- package/dist/core/dry-run.d.ts.map +1 -0
- package/dist/core/dry-run.js +45 -0
- package/dist/core/dry-run.js.map +1 -0
- package/dist/core/element-ref.d.ts +23 -0
- package/dist/core/element-ref.d.ts.map +1 -0
- package/dist/core/element-ref.js +24 -0
- package/dist/core/element-ref.js.map +1 -0
- package/dist/core/error-details.d.ts +20 -0
- package/dist/core/error-details.d.ts.map +1 -0
- package/dist/core/error-details.js +65 -0
- package/dist/core/error-details.js.map +1 -0
- package/dist/core/exit-code.d.ts.map +1 -1
- package/dist/core/exit-code.js +6 -7
- package/dist/core/exit-code.js.map +1 -1
- package/dist/core/git-identity.d.ts +46 -11
- package/dist/core/git-identity.d.ts.map +1 -1
- package/dist/core/git-identity.js +140 -27
- package/dist/core/git-identity.js.map +1 -1
- package/dist/core/index.d.ts +184 -10
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1045 -185
- package/dist/core/index.js.map +1 -1
- package/dist/core/init.d.ts +45 -5
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +134 -24
- package/dist/core/init.js.map +1 -1
- package/dist/core/loaders.d.ts +170 -14
- package/dist/core/loaders.d.ts.map +1 -1
- package/dist/core/loaders.js +570 -89
- package/dist/core/loaders.js.map +1 -1
- package/dist/core/memory-add-link.d.ts +28 -0
- package/dist/core/memory-add-link.d.ts.map +1 -0
- package/dist/core/memory-add-link.js +67 -0
- package/dist/core/memory-add-link.js.map +1 -0
- package/dist/core/memory-add-type.d.ts +25 -1
- package/dist/core/memory-add-type.d.ts.map +1 -1
- package/dist/core/memory-add-type.js +59 -1
- package/dist/core/memory-add-type.js.map +1 -1
- package/dist/core/memory-amend.d.ts +80 -0
- package/dist/core/memory-amend.d.ts.map +1 -0
- package/dist/core/memory-amend.js +160 -0
- package/dist/core/memory-amend.js.map +1 -0
- package/dist/core/memory-supersede.d.ts +28 -0
- package/dist/core/memory-supersede.d.ts.map +1 -0
- package/dist/core/memory-supersede.js +79 -0
- package/dist/core/memory-supersede.js.map +1 -0
- package/dist/core/memory-transition.d.ts +190 -30
- package/dist/core/memory-transition.d.ts.map +1 -1
- package/dist/core/memory-transition.js +480 -59
- package/dist/core/memory-transition.js.map +1 -1
- package/dist/core/registry.d.ts +82 -10
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +50 -0
- package/dist/core/registry.js.map +1 -1
- package/dist/core/relevance.d.ts +74 -12
- package/dist/core/relevance.d.ts.map +1 -1
- package/dist/core/relevance.js +45 -27
- package/dist/core/relevance.js.map +1 -1
- package/dist/core/require-reason.d.ts.map +1 -1
- package/dist/core/require-reason.js +6 -5
- package/dist/core/require-reason.js.map +1 -1
- package/dist/core/required-fields.d.ts +28 -0
- package/dist/core/required-fields.d.ts.map +1 -0
- package/dist/core/required-fields.js +60 -0
- package/dist/core/required-fields.js.map +1 -0
- package/dist/core/revision.d.ts +82 -0
- package/dist/core/revision.d.ts.map +1 -0
- package/dist/core/revision.js +144 -0
- package/dist/core/revision.js.map +1 -0
- package/dist/core/types.d.ts +28 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +12 -3
- package/dist/core/types.js.map +1 -1
- package/dist/core/workflow-core-checks.d.ts +34 -0
- package/dist/core/workflow-core-checks.d.ts.map +1 -0
- package/dist/core/workflow-core-checks.js +403 -0
- package/dist/core/workflow-core-checks.js.map +1 -0
- package/dist/core/workflow-create-remove.d.ts +65 -0
- package/dist/core/workflow-create-remove.d.ts.map +1 -0
- package/dist/core/workflow-create-remove.js +277 -0
- package/dist/core/workflow-create-remove.js.map +1 -0
- package/dist/core/workflow-deduction.d.ts +48 -0
- package/dist/core/workflow-deduction.d.ts.map +1 -0
- package/dist/core/workflow-deduction.js +523 -0
- package/dist/core/workflow-deduction.js.map +1 -0
- package/dist/core/workflow-diagnostics.d.ts +83 -0
- package/dist/core/workflow-diagnostics.d.ts.map +1 -0
- package/dist/core/workflow-diagnostics.js +375 -0
- package/dist/core/workflow-diagnostics.js.map +1 -0
- package/dist/core/workflow-exit-state.d.ts +125 -0
- package/dist/core/workflow-exit-state.d.ts.map +1 -0
- package/dist/core/workflow-exit-state.js +243 -0
- package/dist/core/workflow-exit-state.js.map +1 -0
- package/dist/core/workflow-finalize.d.ts +37 -0
- package/dist/core/workflow-finalize.d.ts.map +1 -0
- package/dist/core/workflow-finalize.js +153 -0
- package/dist/core/workflow-finalize.js.map +1 -0
- package/dist/core/workflow-list-show.d.ts +176 -0
- package/dist/core/workflow-list-show.d.ts.map +1 -0
- package/dist/core/workflow-list-show.js +268 -0
- package/dist/core/workflow-list-show.js.map +1 -0
- package/dist/core/workflow-next.d.ts +268 -0
- package/dist/core/workflow-next.d.ts.map +1 -0
- package/dist/core/workflow-next.js +503 -0
- package/dist/core/workflow-next.js.map +1 -0
- package/dist/core/workflow-registry.d.ts +52 -0
- package/dist/core/workflow-registry.d.ts.map +1 -0
- package/dist/core/workflow-registry.js +158 -0
- package/dist/core/workflow-registry.js.map +1 -0
- package/dist/core/workflow-start-end.d.ts +71 -0
- package/dist/core/workflow-start-end.d.ts.map +1 -0
- package/dist/core/workflow-start-end.js +298 -0
- package/dist/core/workflow-start-end.js.map +1 -0
- package/dist/core/workflow-status.d.ts +83 -0
- package/dist/core/workflow-status.d.ts.map +1 -0
- package/dist/core/workflow-status.js +125 -0
- package/dist/core/workflow-status.js.map +1 -0
- package/dist/core/write-guard.d.ts +51 -8
- package/dist/core/write-guard.d.ts.map +1 -1
- package/dist/core/write-guard.js +139 -9
- package/dist/core/write-guard.js.map +1 -1
- package/dist/directives/index.d.ts +3 -2
- package/dist/directives/index.d.ts.map +1 -1
- package/dist/directives/index.js +5 -3
- package/dist/directives/index.js.map +1 -1
- package/dist/directives/schema.d.ts +34 -29
- package/dist/directives/schema.d.ts.map +1 -1
- package/dist/directives/schema.js +35 -29
- package/dist/directives/schema.js.map +1 -1
- package/dist/directives/version.d.ts +28 -0
- package/dist/directives/version.d.ts.map +1 -0
- package/dist/directives/version.js +51 -0
- package/dist/directives/version.js.map +1 -0
- package/dist/dna/edit.d.ts +7 -6
- package/dist/dna/edit.d.ts.map +1 -1
- package/dist/dna/edit.js +96 -41
- package/dist/dna/edit.js.map +1 -1
- package/dist/dna/mutate.js +18 -3
- package/dist/dna/mutate.js.map +1 -1
- package/dist/dna/path.d.ts +2 -2
- package/dist/dna/roles.d.ts +1 -1
- package/dist/dna/schema.d.ts +61 -3
- package/dist/dna/schema.d.ts.map +1 -1
- package/dist/dna/schema.js +121 -6
- package/dist/dna/schema.js.map +1 -1
- package/dist/mcp/dna-resource.d.ts.map +1 -1
- package/dist/mcp/dna-resource.js +4 -4
- package/dist/mcp/dna-resource.js.map +1 -1
- package/dist/mcp/index.d.ts +8 -4
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +13 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/memory-resource.d.ts.map +1 -1
- package/dist/mcp/memory-resource.js +30 -19
- package/dist/mcp/memory-resource.js.map +1 -1
- package/dist/mcp/prompt.d.ts +61 -24
- package/dist/mcp/prompt.d.ts.map +1 -1
- package/dist/mcp/prompt.js +176 -47
- package/dist/mcp/prompt.js.map +1 -1
- package/dist/mcp/read-only.d.ts +39 -0
- package/dist/mcp/read-only.d.ts.map +1 -1
- package/dist/mcp/read-only.js +61 -0
- package/dist/mcp/read-only.js.map +1 -1
- package/dist/mcp/registrar.d.ts +10 -0
- package/dist/mcp/registrar.d.ts.map +1 -1
- package/dist/mcp/registrar.js +48 -12
- package/dist/mcp/registrar.js.map +1 -1
- package/dist/mcp/server.d.ts +21 -14
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +41 -15
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session-resource.d.ts +13 -0
- package/dist/mcp/session-resource.d.ts.map +1 -0
- package/dist/mcp/session-resource.js +65 -0
- package/dist/mcp/session-resource.js.map +1 -0
- package/dist/mcp/workflow-resource.d.ts +18 -0
- package/dist/mcp/workflow-resource.d.ts.map +1 -1
- package/dist/mcp/workflow-resource.js +37 -8
- package/dist/mcp/workflow-resource.js.map +1 -1
- package/dist/memory/add.d.ts +110 -24
- package/dist/memory/add.d.ts.map +1 -1
- package/dist/memory/add.js +251 -64
- package/dist/memory/add.js.map +1 -1
- package/dist/memory/audit.d.ts +152 -39
- package/dist/memory/audit.d.ts.map +1 -1
- package/dist/memory/audit.js +257 -103
- package/dist/memory/audit.js.map +1 -1
- package/dist/memory/commit-message.d.ts +60 -38
- package/dist/memory/commit-message.d.ts.map +1 -1
- package/dist/memory/commit-message.js +167 -21
- package/dist/memory/commit-message.js.map +1 -1
- package/dist/memory/entry.d.ts.map +1 -1
- package/dist/memory/entry.js +1 -2
- package/dist/memory/entry.js.map +1 -1
- package/dist/memory/frontmatter-edit.d.ts +16 -4
- package/dist/memory/frontmatter-edit.d.ts.map +1 -1
- package/dist/memory/frontmatter-edit.js +13 -1
- package/dist/memory/frontmatter-edit.js.map +1 -1
- package/dist/memory/git-log.d.ts +18 -6
- package/dist/memory/git-log.d.ts.map +1 -1
- package/dist/memory/git-log.js +40 -13
- package/dist/memory/git-log.js.map +1 -1
- package/dist/memory/history.d.ts +73 -12
- package/dist/memory/history.d.ts.map +1 -1
- package/dist/memory/history.js +128 -44
- package/dist/memory/history.js.map +1 -1
- package/dist/memory/index.d.ts +13 -11
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +34 -3
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/query.d.ts +161 -19
- package/dist/memory/query.d.ts.map +1 -1
- package/dist/memory/query.js +435 -55
- package/dist/memory/query.js.map +1 -1
- package/dist/memory/schema.d.ts +52 -3
- package/dist/memory/schema.d.ts.map +1 -1
- package/dist/memory/schema.js +132 -8
- package/dist/memory/schema.js.map +1 -1
- package/dist/memory/state-machine.d.ts +65 -10
- package/dist/memory/state-machine.d.ts.map +1 -1
- package/dist/memory/state-machine.js +134 -46
- package/dist/memory/state-machine.js.map +1 -1
- package/dist/memory/submit.d.ts +52 -3
- package/dist/memory/submit.d.ts.map +1 -1
- package/dist/memory/submit.js +118 -11
- package/dist/memory/submit.js.map +1 -1
- package/dist/storage/build-stamp.d.ts +18 -0
- package/dist/storage/build-stamp.d.ts.map +1 -0
- package/dist/storage/build-stamp.js +68 -0
- package/dist/storage/build-stamp.js.map +1 -0
- package/dist/storage/builtin-adapters.d.ts +32 -0
- package/dist/storage/builtin-adapters.d.ts.map +1 -0
- package/dist/storage/builtin-adapters.js +100 -0
- package/dist/storage/builtin-adapters.js.map +1 -0
- package/dist/storage/builtin-directives.d.ts +0 -29
- package/dist/storage/builtin-directives.d.ts.map +1 -1
- package/dist/storage/builtin-directives.js +9 -4
- package/dist/storage/builtin-directives.js.map +1 -1
- package/dist/storage/commit.d.ts +310 -3
- package/dist/storage/commit.d.ts.map +1 -1
- package/dist/storage/commit.js +807 -33
- package/dist/storage/commit.js.map +1 -1
- package/dist/storage/document.d.ts +6 -0
- package/dist/storage/document.d.ts.map +1 -1
- package/dist/storage/document.js +10 -1
- package/dist/storage/document.js.map +1 -1
- package/dist/storage/dry-run.d.ts +65 -0
- package/dist/storage/dry-run.d.ts.map +1 -0
- package/dist/storage/dry-run.js +150 -0
- package/dist/storage/dry-run.js.map +1 -0
- package/dist/storage/errors.d.ts +26 -0
- package/dist/storage/errors.d.ts.map +1 -1
- package/dist/storage/errors.js +27 -1
- package/dist/storage/errors.js.map +1 -1
- package/dist/storage/frontmatter.d.ts +1 -1
- package/dist/storage/frontmatter.js +1 -1
- package/dist/storage/git-read.d.ts +64 -0
- package/dist/storage/git-read.d.ts.map +1 -0
- package/dist/storage/git-read.js +107 -0
- package/dist/storage/git-read.js.map +1 -0
- package/dist/storage/index.d.ts +13 -5
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +39 -2
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/layout.d.ts +2 -2
- package/dist/storage/layout.d.ts.map +1 -1
- package/dist/storage/layout.js +2 -7
- package/dist/storage/layout.js.map +1 -1
- package/dist/storage/memory-path.d.ts +18 -5
- package/dist/storage/memory-path.d.ts.map +1 -1
- package/dist/storage/memory-path.js +41 -5
- package/dist/storage/memory-path.js.map +1 -1
- package/dist/storage/snapshot.js +1 -1
- package/dist/storage/templates.d.ts +33 -6
- package/dist/storage/templates.d.ts.map +1 -1
- package/dist/storage/templates.js +170 -16
- package/dist/storage/templates.js.map +1 -1
- package/dist/validation/config-version.d.ts +19 -0
- package/dist/validation/config-version.d.ts.map +1 -0
- package/dist/validation/config-version.js +53 -0
- package/dist/validation/config-version.js.map +1 -0
- package/dist/validation/diagnostic.d.ts +40 -0
- package/dist/validation/diagnostic.d.ts.map +1 -0
- package/dist/validation/diagnostic.js +41 -0
- package/dist/validation/diagnostic.js.map +1 -0
- package/dist/validation/format.d.ts +74 -0
- package/dist/validation/format.d.ts.map +1 -0
- package/dist/validation/format.js +104 -0
- package/dist/validation/format.js.map +1 -0
- package/dist/validation/id.d.ts +14 -0
- package/dist/validation/id.d.ts.map +1 -1
- package/dist/validation/id.js +35 -11
- package/dist/validation/id.js.map +1 -1
- package/dist/validation/identity.d.ts +40 -0
- package/dist/validation/identity.d.ts.map +1 -0
- package/dist/validation/identity.js +78 -0
- package/dist/validation/identity.js.map +1 -0
- package/dist/validation/index.d.ts +9 -3
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/index.js +30 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/secret-scan.d.ts +3 -2
- package/dist/validation/secret-scan.d.ts.map +1 -1
- package/dist/validation/secret-scan.js +32 -11
- package/dist/validation/secret-scan.js.map +1 -1
- package/dist/validation/warning.d.ts +30 -16
- package/dist/validation/warning.d.ts.map +1 -1
- package/dist/validation/warning.js +74 -7
- package/dist/validation/warning.js.map +1 -1
- package/dist/workflow/bindings.d.ts +152 -0
- package/dist/workflow/bindings.d.ts.map +1 -0
- package/dist/workflow/bindings.js +249 -0
- package/dist/workflow/bindings.js.map +1 -0
- package/dist/workflow/create.d.ts +13 -0
- package/dist/workflow/create.d.ts.map +1 -0
- package/dist/workflow/create.js +41 -0
- package/dist/workflow/create.js.map +1 -0
- package/dist/workflow/deduce.d.ts +400 -0
- package/dist/workflow/deduce.d.ts.map +1 -0
- package/dist/workflow/deduce.js +1109 -0
- package/dist/workflow/deduce.js.map +1 -0
- package/dist/workflow/manifest-edit.d.ts +19 -0
- package/dist/workflow/manifest-edit.d.ts.map +1 -0
- package/dist/workflow/manifest-edit.js +124 -0
- package/dist/workflow/manifest-edit.js.map +1 -0
- package/dist/workflow/schema.d.ts +158 -19
- package/dist/workflow/schema.d.ts.map +1 -1
- package/dist/workflow/schema.js +163 -15
- package/dist/workflow/schema.js.map +1 -1
- package/package.json +18 -8
package/dist/core/loaders.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ROLES_YAML_PATH = exports.DIRECTIVES_DIR_PATH = exports.DNA_YAML_PATH = exports.MEMORY_YAML_PATH = void 0;
|
|
3
|
+
exports.ROLES_YAML_PATH = exports.DIRECTIVES_DIR_PATH = exports.DNA_YAML_PATH = exports.MEMORY_YAML_PATH = exports.ConfigFileMissingError = void 0;
|
|
4
4
|
exports.loadMemoryYaml = loadMemoryYaml;
|
|
5
5
|
exports.loadMemoryYamlAtHead = loadMemoryYamlAtHead;
|
|
6
|
+
exports.loadMemoryYamlAtRev = loadMemoryYamlAtRev;
|
|
7
|
+
exports.loadMemoryYamlAtCommit = loadMemoryYamlAtCommit;
|
|
6
8
|
exports.loadDnaYaml = loadDnaYaml;
|
|
7
9
|
exports.loadDnaYamlAtHead = loadDnaYamlAtHead;
|
|
10
|
+
exports.loadDnaYamlAtRev = loadDnaYamlAtRev;
|
|
11
|
+
exports.loadDnaYamlAtCommit = loadDnaYamlAtCommit;
|
|
8
12
|
exports.loadWorkflowsYaml = loadWorkflowsYaml;
|
|
13
|
+
exports.loadWorkflowsYamlAtRev = loadWorkflowsYamlAtRev;
|
|
14
|
+
exports.loadWorkflowsYamlAtCommit = loadWorkflowsYamlAtCommit;
|
|
15
|
+
exports.loadDirectiveInventory = loadDirectiveInventory;
|
|
9
16
|
exports.loadDirectives = loadDirectives;
|
|
10
17
|
exports.loadDirectivesAtHead = loadDirectivesAtHead;
|
|
18
|
+
exports.loadDirectivesAtRev = loadDirectivesAtRev;
|
|
19
|
+
exports.loadDirectivesAtCommit = loadDirectivesAtCommit;
|
|
11
20
|
exports.loadRolesYaml = loadRolesYaml;
|
|
12
21
|
exports.loadRolesYamlAtHead = loadRolesYamlAtHead;
|
|
22
|
+
exports.loadRolesYamlAtRev = loadRolesYamlAtRev;
|
|
23
|
+
exports.loadRolesYamlAtCommit = loadRolesYamlAtCommit;
|
|
13
24
|
/**
|
|
14
25
|
* Per-pillar loaders (task-004-decoupled-pillars, REQ-SYS-02). `core` exposes exactly one loader
|
|
15
26
|
* per pillar — `loadMemoryYaml`, `loadDnaYaml`, `loadWorkflowsYaml`, `loadDirectives` — each reading
|
|
@@ -24,40 +35,163 @@ exports.loadRolesYamlAtHead = loadRolesYamlAtHead;
|
|
|
24
35
|
const fs_1 = require("fs");
|
|
25
36
|
const path_1 = require("path");
|
|
26
37
|
const schema_1 = require("../directives/schema");
|
|
38
|
+
const version_1 = require("../directives/version");
|
|
27
39
|
const schema_2 = require("../dna/schema");
|
|
28
40
|
const schema_3 = require("../memory/schema");
|
|
29
41
|
const storage_1 = require("../storage");
|
|
30
42
|
const validation_1 = require("../validation");
|
|
43
|
+
const format_1 = require("../validation/format");
|
|
31
44
|
const schema_4 = require("../workflow/schema");
|
|
45
|
+
const revision_1 = require("./revision");
|
|
46
|
+
const bindings_1 = require("../workflow/bindings");
|
|
47
|
+
const workflow_diagnostics_1 = require("./workflow-diagnostics");
|
|
32
48
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
49
|
+
* The resolved `Stats` of `full`, or why it cannot be resolved (task-143, `bug-125`). `statSync`
|
|
50
|
+
* follows a symbolic link, so a dangling one throws `ENOENT` — the raw error that used to escape every
|
|
51
|
+
* directive read. `lstatSync` sees the entry itself first, so a broken link is told apart from any
|
|
52
|
+
* other failure and named as one. The reasons are phrased like `requireInspectableTarget`'s
|
|
53
|
+
* (`./write-guard.ts`), the write-side guard over the same kind of entry.
|
|
54
|
+
*/
|
|
55
|
+
function resolveEntry(full) {
|
|
56
|
+
try {
|
|
57
|
+
return (0, fs_1.statSync)(full);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
const code = error.code;
|
|
61
|
+
let isLink = false;
|
|
62
|
+
try {
|
|
63
|
+
isLink = (0, fs_1.lstatSync)(full).isSymbolicLink();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// `lstat` fails too — a directory listed without search permission, or an entry that vanished
|
|
67
|
+
// after `readdirSync`: report what `stat` said.
|
|
68
|
+
}
|
|
69
|
+
if (isLink && (code === 'ENOENT' || code === 'ENOTDIR')) {
|
|
70
|
+
return { reason: 'it is a symbolic link whose target does not exist' };
|
|
71
|
+
}
|
|
72
|
+
return { reason: `it cannot be read (${String(code)})` };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** The `code` of a filesystem error, as a skip reason's suffix. */
|
|
76
|
+
function cannotBeRead(error) {
|
|
77
|
+
return `it cannot be read (${String(error.code)})`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Recursively list every `.md` file under `dir`, as paths relative to `dir`, in lexicographic
|
|
81
|
+
* traversal order (REQ-SYS-07: no unordered iteration in a context-building path — directory-entry
|
|
35
82
|
* order from `readdirSync` is not guaranteed stable, so traversal always sorts explicitly, mirroring
|
|
36
|
-
* `src/storage/snapshot.ts`'s `listFilesSorted`). Returns
|
|
83
|
+
* `src/storage/snapshot.ts`'s `listFilesSorted`). Returns no entries if `dir` doesn't exist.
|
|
84
|
+
*
|
|
85
|
+
* An entry that cannot be resolved — a dangling symbolic link above all, a subdirectory that cannot be
|
|
86
|
+
* listed, or a directory link back to one of its own ancestors — is **skipped**, and comes back as a
|
|
87
|
+
* `skipped` entry with its reason, in the same traversal order (task-143, `bug-125`). Whatever the
|
|
88
|
+
* link's name, it is reported: a dangling link has no type, so one named without `.md` may have been
|
|
89
|
+
* a directory of directives. `dir` itself, when it is a dangling link, is one skipped entry with
|
|
90
|
+
* `relative: ''`.
|
|
91
|
+
*
|
|
92
|
+
* Symlinked directories are followed, but never into a directory already on the walk's stack (by
|
|
93
|
+
* `realpath`): without that, a link to an ancestor is walked until the OS refuses (ELOOP after some
|
|
94
|
+
* forty levels), and every directive above it is listed that many times (task-143 review).
|
|
95
|
+
*
|
|
96
|
+
* `dir` itself is the pillar: when it cannot be listed, `rootError` carries the reason and the caller
|
|
97
|
+
* refuses the read.
|
|
37
98
|
*/
|
|
38
99
|
function listMarkdownFilesSorted(dir) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
100
|
+
const entries = [];
|
|
101
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
102
|
+
// `existsSync` follows a link: a dangling `dir` is reported, a genuinely absent one is not.
|
|
103
|
+
// `lstat` itself fails on a path through a non-directory (`.wingfoil` a file): absent, as before.
|
|
104
|
+
let isLink;
|
|
105
|
+
try {
|
|
106
|
+
isLink = (0, fs_1.lstatSync)(dir).isSymbolicLink();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
isLink = false;
|
|
110
|
+
}
|
|
111
|
+
if (isLink)
|
|
112
|
+
entries.push({ relative: '', kind: 'skipped', reason: 'it is a symbolic link whose target does not exist' });
|
|
113
|
+
return { entries };
|
|
114
|
+
}
|
|
115
|
+
if (!(0, fs_1.statSync)(dir).isDirectory())
|
|
116
|
+
return { entries };
|
|
117
|
+
let rootNames;
|
|
118
|
+
try {
|
|
119
|
+
rootNames = (0, fs_1.readdirSync)(dir);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return { entries, rootError: cannotBeRead(error) };
|
|
123
|
+
}
|
|
124
|
+
const walk = (current, prefix, names, ancestors) => {
|
|
125
|
+
for (const entry of [...names].sort()) {
|
|
44
126
|
const full = (0, path_1.join)(current, entry);
|
|
45
127
|
const relative = prefix ? `${prefix}/${entry}` : entry;
|
|
46
|
-
|
|
47
|
-
|
|
128
|
+
const resolved = resolveEntry(full);
|
|
129
|
+
if (!('isDirectory' in resolved)) {
|
|
130
|
+
entries.push({ relative, kind: 'skipped', reason: resolved.reason });
|
|
131
|
+
}
|
|
132
|
+
else if (resolved.isDirectory()) {
|
|
133
|
+
let real;
|
|
134
|
+
let children;
|
|
135
|
+
try {
|
|
136
|
+
real = (0, fs_1.realpathSync)(full);
|
|
137
|
+
children = (0, fs_1.readdirSync)(full);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
entries.push({ relative, kind: 'skipped', reason: cannotBeRead(error) });
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (ancestors.has(real)) {
|
|
144
|
+
entries.push({ relative, kind: 'skipped', reason: 'it is a symbolic link to an ancestor directory' });
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
walk(full, relative, children, new Set([...ancestors, real]));
|
|
48
148
|
}
|
|
49
149
|
else if (entry.endsWith('.md')) {
|
|
50
|
-
|
|
150
|
+
entries.push({ relative, kind: 'file' });
|
|
51
151
|
}
|
|
52
152
|
}
|
|
53
153
|
};
|
|
54
|
-
walk(dir, '');
|
|
55
|
-
return
|
|
154
|
+
walk(dir, '', rootNames, new Set([(0, fs_1.realpathSync)(dir)]));
|
|
155
|
+
return { entries };
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* A configuration file the read needs is not in the working tree (task-179, `bug-245`): a `.wingfoil/`
|
|
159
|
+
* exists — the absent case is `requireInitializedProject`'s — but this pillar's file does not. The
|
|
160
|
+
* message names the file repository-relative and what to do, never the host's absolute path, so the
|
|
161
|
+
* output is the same on every machine and safe to paste into an issue. It keeps Node's `code: 'ENOENT'`,
|
|
162
|
+
* so every caller that maps a missing file (`coreErrorOf` → `NOT_FOUND`, exit `1`) still does.
|
|
163
|
+
*/
|
|
164
|
+
class ConfigFileMissingError extends Error {
|
|
165
|
+
relativePath;
|
|
166
|
+
/** Node's code for a missing file, kept so existing `ENOENT` mappings still apply. */
|
|
167
|
+
code = 'ENOENT';
|
|
168
|
+
/** @param relativePath - the missing file, relative to the repository root (`.wingfoil/dna.yaml`). */
|
|
169
|
+
constructor(relativePath) {
|
|
170
|
+
super(`${relativePath} is missing: restore it from git, or re-create it ('wingfoil init' scaffolds a complete .wingfoil/ in a project that has none)`);
|
|
171
|
+
this.relativePath = relativePath;
|
|
172
|
+
this.name = 'ConfigFileMissingError';
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.ConfigFileMissingError = ConfigFileMissingError;
|
|
176
|
+
/**
|
|
177
|
+
* Read a working-tree configuration file named repository-relative (`.wingfoil/dna.yaml`): a missing one
|
|
178
|
+
* is a {@link ConfigFileMissingError}, any other failure propagates unchanged. The relative path is also
|
|
179
|
+
* the label every parse and validation issue carries (task-179, `bug-245`), as the `HEAD:`-labelled
|
|
180
|
+
* readers already do.
|
|
181
|
+
*/
|
|
182
|
+
function readConfigFile(root, relativePath) {
|
|
183
|
+
try {
|
|
184
|
+
return (0, storage_1.readDocument)((0, path_1.join)(root, relativePath));
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (error?.code === 'ENOENT')
|
|
188
|
+
throw new ConfigFileMissingError(relativePath);
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
56
191
|
}
|
|
57
192
|
/** Load and validate `.wingfoil/memory.yaml` in isolation (spec-001-memory-yaml-schema). */
|
|
58
193
|
function loadMemoryYaml(root) {
|
|
59
|
-
|
|
60
|
-
return parseMemoryYaml((0, storage_1.readDocument)(filePath), filePath);
|
|
194
|
+
return parseMemoryYaml(readConfigFile(root, exports.MEMORY_YAML_PATH), exports.MEMORY_YAML_PATH);
|
|
61
195
|
}
|
|
62
196
|
/**
|
|
63
197
|
* Root-relative POSIX path of `memory.yaml` — the form git wants for a revision read
|
|
@@ -69,10 +203,13 @@ exports.MEMORY_YAML_PATH = '.wingfoil/memory.yaml';
|
|
|
69
203
|
* The Memory pillar's two-pass parse, over bytes that may come from anywhere — the working-tree file
|
|
70
204
|
* ({@link loadMemoryYaml}) or a git revision ({@link loadMemoryYamlAtHead}). `filePath` is a label
|
|
71
205
|
* only: it rides every issue this raises, so an error names the baseline it came from
|
|
72
|
-
* (`HEAD:.wingfoil/memory.yaml`, not just a path on disk).
|
|
206
|
+
* (`HEAD:.wingfoil/memory.yaml`, not just a path on disk). A `format` newer than this build reads is
|
|
207
|
+
* refused before the structural pass (`dl-149`, task-251).
|
|
73
208
|
*/
|
|
74
209
|
function parseMemoryYaml(raw, filePath) {
|
|
75
|
-
|
|
210
|
+
const data = (0, validation_1.parseYaml)(raw, filePath);
|
|
211
|
+
(0, format_1.refuseNewerFormat)(data, format_1.MEMORY_YAML_FORMAT, filePath);
|
|
212
|
+
return (0, validation_1.runValidation)(schema_3.MemoryYaml, data, filePath);
|
|
76
213
|
}
|
|
77
214
|
/**
|
|
78
215
|
* Load and validate `.wingfoil/memory.yaml` **as the repository has committed it** — the version at
|
|
@@ -94,10 +231,34 @@ function parseMemoryYaml(raw, filePath) {
|
|
|
94
231
|
* byte-identical, and `HEAD` is the one available before that commit exists.
|
|
95
232
|
*/
|
|
96
233
|
function loadMemoryYamlAtHead(root) {
|
|
97
|
-
|
|
234
|
+
return (0, revision_1.atHeadOr)(root, () => loadMemoryYamlAtRev(root, 'HEAD'), null);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Load and validate `.wingfoil/memory.yaml` **as commit `rev` holds it** (task-137, `spec-012` §2
|
|
238
|
+
* `stateRef`) — the general form of {@link loadMemoryYamlAtHead}, which is this at `'HEAD'`. `null`
|
|
239
|
+
* when that commit does not hold the file; a later commit and the working tree are never read.
|
|
240
|
+
*
|
|
241
|
+
* @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
|
|
242
|
+
* @throws `RevisionError` when `rev` is malformed or names no commit — never an empty answer.
|
|
243
|
+
*/
|
|
244
|
+
function loadMemoryYamlAtRev(root, rev) {
|
|
245
|
+
return memoryYamlAt(root, (0, revision_1.resolveRevision)(root, rev), rev);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* {@link loadMemoryYamlAtRev} for a `sha` already resolved to a commit (`resolveRevision`, `./revision.ts`),
|
|
249
|
+
* read without resolving it again: a reader that reads several pillars at one commit resolves it once
|
|
250
|
+
* (task-283, `bug-309`: the workflow deduction's snapshot).
|
|
251
|
+
*
|
|
252
|
+
* @param sha - The full sha of a commit, as `resolveRevision` returned it; parse errors name it.
|
|
253
|
+
*/
|
|
254
|
+
function loadMemoryYamlAtCommit(root, sha) {
|
|
255
|
+
return memoryYamlAt(root, sha, sha);
|
|
256
|
+
}
|
|
257
|
+
function memoryYamlAt(root, sha, label) {
|
|
258
|
+
const raw = (0, storage_1.readPathAtRev)(root, sha, exports.MEMORY_YAML_PATH);
|
|
98
259
|
if (raw === null)
|
|
99
260
|
return null;
|
|
100
|
-
return parseMemoryYaml(raw,
|
|
261
|
+
return parseMemoryYaml(raw, `${label}:${exports.MEMORY_YAML_PATH}`);
|
|
101
262
|
}
|
|
102
263
|
/**
|
|
103
264
|
* `js-yaml`'s `YAMLException#message` always embeds the failure position as `(<line>:<column>)`
|
|
@@ -121,7 +282,7 @@ function extractYamlErrorLine(message) {
|
|
|
121
282
|
* `parseYaml`/`ValidationError`'s shared behavior for `memory.yaml`/`workflows.yaml`/directives.
|
|
122
283
|
*/
|
|
123
284
|
function loadDnaYaml(root) {
|
|
124
|
-
return parseDnaYaml((
|
|
285
|
+
return parseDnaYaml(readConfigFile(root, exports.DNA_YAML_PATH), exports.DNA_YAML_PATH);
|
|
125
286
|
}
|
|
126
287
|
/**
|
|
127
288
|
* Root-relative POSIX path of `dna.yaml` — the form git wants for a revision read
|
|
@@ -147,6 +308,8 @@ function parseDnaYaml(raw, filePath) {
|
|
|
147
308
|
}
|
|
148
309
|
throw err;
|
|
149
310
|
}
|
|
311
|
+
// A file from a newer WingFoil is refused for its format alone (`dl-149`, task-251).
|
|
312
|
+
(0, format_1.refuseNewerFormat)(data, format_1.DNA_YAML_FORMAT, filePath);
|
|
150
313
|
return (0, validation_1.runValidation)(schema_2.DnaYaml, data, filePath);
|
|
151
314
|
}
|
|
152
315
|
/**
|
|
@@ -177,55 +340,263 @@ function parseDnaYaml(raw, filePath) {
|
|
|
177
340
|
* a write refuses while its target carries modifications it does not own.)
|
|
178
341
|
*/
|
|
179
342
|
function loadDnaYamlAtHead(root) {
|
|
180
|
-
|
|
343
|
+
return (0, revision_1.atHeadOr)(root, () => loadDnaYamlAtRev(root, 'HEAD'), null);
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Load and validate `.wingfoil/dna.yaml` **as commit `rev` holds it** (task-137, `spec-012` §2
|
|
347
|
+
* `stateRef`) — the general form of {@link loadDnaYamlAtHead}, which is this at `'HEAD'`. `null` when
|
|
348
|
+
* that commit does not hold the file; a later commit and the working tree are never read.
|
|
349
|
+
*
|
|
350
|
+
* @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
|
|
351
|
+
* @throws `RevisionError` when `rev` is malformed or names no commit — never an empty answer.
|
|
352
|
+
*/
|
|
353
|
+
function loadDnaYamlAtRev(root, rev) {
|
|
354
|
+
return dnaYamlAt(root, (0, revision_1.resolveRevision)(root, rev), rev);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* {@link loadDnaYamlAtRev} for a `sha` already resolved to a commit, read without resolving it again
|
|
358
|
+
* (task-283, `bug-309`).
|
|
359
|
+
*
|
|
360
|
+
* @param sha - The full sha of a commit, as `resolveRevision` returned it; parse errors name it.
|
|
361
|
+
*/
|
|
362
|
+
function loadDnaYamlAtCommit(root, sha) {
|
|
363
|
+
return dnaYamlAt(root, sha, sha);
|
|
364
|
+
}
|
|
365
|
+
function dnaYamlAt(root, sha, label) {
|
|
366
|
+
const raw = (0, storage_1.readPathAtRev)(root, sha, exports.DNA_YAML_PATH);
|
|
181
367
|
if (raw === null)
|
|
182
368
|
return null;
|
|
183
|
-
return parseDnaYaml(raw,
|
|
369
|
+
return parseDnaYaml(raw, `${label}:${exports.DNA_YAML_PATH}`);
|
|
370
|
+
}
|
|
371
|
+
/** The manifest's path relative to `.wingfoil/` — the `file` of its diagnostics (spec-003). */
|
|
372
|
+
const WORKFLOWS_MANIFEST_FILE = 'workflows.yaml';
|
|
373
|
+
/** A Zod issue path as spec-003 spells a diagnostic `path`: `phases[3].include`. */
|
|
374
|
+
function diagnosticPath(path) {
|
|
375
|
+
let out = '';
|
|
376
|
+
for (const segment of path) {
|
|
377
|
+
if (typeof segment === 'number')
|
|
378
|
+
out += `[${segment}]`;
|
|
379
|
+
else
|
|
380
|
+
out += out === '' ? String(segment) : `.${String(segment)}`;
|
|
381
|
+
}
|
|
382
|
+
return out;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* One included file's structural (Zod) pass, as diagnostics (spec-003 § "Diagnostics": structural
|
|
386
|
+
* failures keep spec-009's structural codes, except the named `kind` refusal). An empty list means the
|
|
387
|
+
* file is valid and `workflow` is set.
|
|
388
|
+
*/
|
|
389
|
+
function parseWorkflowFile(data, file, filePath) {
|
|
390
|
+
const result = schema_4.Workflow.safeParse(data);
|
|
391
|
+
if (result.success) {
|
|
392
|
+
// The same once-per-file unknown-field warning `runValidation` fires (spec-009 §2).
|
|
393
|
+
(0, validation_1.emitUnknownFieldWarning)(data, schema_4.Workflow, filePath);
|
|
394
|
+
return { workflow: result.data, diagnostics: [] };
|
|
395
|
+
}
|
|
396
|
+
const diagnostics = zodDiagnostics(result.error.issues, file).map((diagnostic) => diagnostic.path === 'kind' ? { ...diagnostic, code: 'E_WORKFLOW_INVALID_KIND' } : diagnostic);
|
|
397
|
+
return { workflow: null, diagnostics };
|
|
398
|
+
}
|
|
399
|
+
/** Zod issues as `E_VALIDATION` diagnostics of `file` (spec-009's structural code). */
|
|
400
|
+
function zodDiagnostics(issues, file) {
|
|
401
|
+
return issues.map((issue) => ({ code: validation_1.E_VALIDATION, severity: 'error', file, path: diagnosticPath(issue.path), message: issue.message }));
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Parse `raw` as YAML; a parse failure becomes one `E_YAML_PARSE_ERROR` diagnostic of `file` (the
|
|
405
|
+
* spec-009 code, with the parser's message) instead of a throw, so it takes its place in the array.
|
|
406
|
+
*/
|
|
407
|
+
function parseYamlOrDiagnostic(raw, filePath, file) {
|
|
408
|
+
try {
|
|
409
|
+
return { data: (0, validation_1.parseYaml)(raw, filePath), diagnostic: null };
|
|
410
|
+
}
|
|
411
|
+
catch (err) {
|
|
412
|
+
// `parseYaml` throws nothing but `ValidationError.yamlParse`: one issue, the parser's message.
|
|
413
|
+
const { message } = err.issues[0];
|
|
414
|
+
return { data: null, diagnostic: { code: validation_1.E_YAML_PARSE_ERROR, severity: 'error', file, path: '', message } };
|
|
415
|
+
}
|
|
184
416
|
}
|
|
185
417
|
/**
|
|
186
418
|
* Load and validate the Workflow pillar in isolation (spec-003-workflows-yaml-schema): Layer 1
|
|
187
419
|
* (`.wingfoil/workflows.yaml`), then every file its `include` list names (Layer 2), resolved
|
|
188
|
-
* relative to `.wingfoil/` per spec-003
|
|
189
|
-
* themselves (spec-009 §1 "Cross-file" category) run here
|
|
420
|
+
* relative to `.wingfoil/` per spec-003, then the **loader** rows of spec-003 § "Diagnostics" — the
|
|
421
|
+
* checks that need the loaded files themselves (spec-009 §1 "Cross-file" category), run here rather
|
|
422
|
+
* than in the schema module (`./workflow-diagnostics.ts`, task-136).
|
|
190
423
|
*
|
|
191
|
-
* -
|
|
192
|
-
*
|
|
193
|
-
*
|
|
424
|
+
* - An **absent** manifest is an empty registry, with no diagnostic (spec-003 Layer 1).
|
|
425
|
+
* - A manifest that is not YAML, or fails its structural pass, is the whole array: one
|
|
426
|
+
* `E_YAML_PARSE_ERROR`, or its Zod issues as `E_VALIDATION`, on `workflows.yaml`.
|
|
427
|
+
* - Everything else is collected into one ordered `diagnostics` array (REQ-SYS-07): the manifest's
|
|
428
|
+
* (`E_WORKFLOW_FILE_NOT_FOUND`, `E_NO_MAIN_WORKFLOW`), then each file in `include` order —
|
|
429
|
+
* a YAML parse failure (`E_YAML_PARSE_ERROR`) or the structural (Zod) issues, then
|
|
430
|
+
* workflow-level, then phase-level. If it holds an error, the load throws a
|
|
431
|
+
* {@link DiagnosticsError}: `VALIDATION`, exit `1`, the first error as the reason and the whole array
|
|
432
|
+
* attached.
|
|
194
433
|
*/
|
|
195
434
|
function loadWorkflowsYaml(root) {
|
|
196
435
|
const configDir = (0, path_1.join)(root, '.wingfoil');
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
436
|
+
return loadWorkflowsFrom({
|
|
437
|
+
read: (file) => {
|
|
438
|
+
const path = (0, path_1.join)(configDir, file);
|
|
439
|
+
return (0, storage_1.documentExists)(path) ? (0, storage_1.readDocument)(path) : null;
|
|
440
|
+
},
|
|
441
|
+
// Repository-relative, never the host path (task-179, `bug-245`).
|
|
442
|
+
label: (file) => `.wingfoil/${file}`,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Load and validate the Workflow pillar **as commit `rev` holds it** (task-137, `spec-017` §1.1: the
|
|
447
|
+
* registry is read at `HEAD`): the manifest and every file it includes, read at that commit. It runs
|
|
448
|
+
* the same body as {@link loadWorkflowsYaml} over the committed bytes, so the result — or the
|
|
449
|
+
* {@link DiagnosticsError} and its ordered `diagnostics` array (task-136) — is the one the working-tree
|
|
450
|
+
* loader gives for the same bytes. Only the parse-error labels differ (`<rev>:.wingfoil/<file>`); every
|
|
451
|
+
* diagnostic's `file` stays relative to `.wingfoil/`. A path the commit holds as a directory counts as
|
|
452
|
+
* absent (`E_WORKFLOW_FILE_NOT_FOUND`).
|
|
453
|
+
*
|
|
454
|
+
* @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
|
|
455
|
+
* @param overlay - Files that replace the commit's own, keyed by repository-relative path (`.wingfoil/…`);
|
|
456
|
+
* `null` reads as absent. `workflow create` / `remove` (task-211) load the registry as their commit would
|
|
457
|
+
* leave it, before writing it. Empty by default: the commit alone.
|
|
458
|
+
* @throws `RevisionError` when `rev` is malformed or names no commit — never an empty registry.
|
|
459
|
+
*/
|
|
460
|
+
function loadWorkflowsYamlAtRev(root, rev, overlay = new Map()) {
|
|
461
|
+
return workflowsYamlAt(root, (0, revision_1.resolveRevision)(root, rev), rev, overlay);
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* {@link loadWorkflowsYamlAtRev} for a `sha` already resolved to a commit, read without resolving it
|
|
465
|
+
* again (task-283, `bug-309`).
|
|
466
|
+
*
|
|
467
|
+
* @param sha - The full sha of a commit, as `resolveRevision` returned it; labels name it.
|
|
468
|
+
* @param overlay - As {@link loadWorkflowsYamlAtRev}'s.
|
|
469
|
+
*/
|
|
470
|
+
function loadWorkflowsYamlAtCommit(root, sha, overlay = new Map()) {
|
|
471
|
+
return workflowsYamlAt(root, sha, sha, overlay);
|
|
472
|
+
}
|
|
473
|
+
function workflowsYamlAt(root, sha, rev, overlay) {
|
|
474
|
+
const committedPath = (file) => path_1.posix.normalize(`.wingfoil/${file}`);
|
|
475
|
+
// The included files and `bindings.yaml`, read in one `cat-file --batch` once the manifest names them
|
|
476
|
+
// (task-216, REQ-PERF-03: one spawn instead of one per workflow file).
|
|
477
|
+
const fetched = new Map();
|
|
478
|
+
const fetch = (files) => {
|
|
479
|
+
const answers = (0, storage_1.readPathsAtRev)(root, sha, files.map(committedPath));
|
|
480
|
+
return files.map((file, index) => {
|
|
481
|
+
const path = committedPath(file);
|
|
482
|
+
return overlay.has(path) ? overlay.get(path) : (answers[index] ?? null);
|
|
483
|
+
});
|
|
484
|
+
};
|
|
485
|
+
return loadWorkflowsFrom({
|
|
486
|
+
read: (file) => {
|
|
487
|
+
if (fetched.has(file))
|
|
488
|
+
return fetched.get(file);
|
|
489
|
+
const [raw = null] = fetch([file]);
|
|
490
|
+
return raw;
|
|
491
|
+
},
|
|
492
|
+
prefetch: (files) => {
|
|
493
|
+
const answers = fetch(files);
|
|
494
|
+
files.forEach((file, index) => fetched.set(file, answers[index] ?? null));
|
|
495
|
+
},
|
|
496
|
+
label: (file) => `${rev}:${committedPath(file)}`,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* The Workflow pillar's load (spec-003 Layers 1–2 and the loader rows of § "Diagnostics"), over a
|
|
501
|
+
* {@link WorkflowSource} — the one body {@link loadWorkflowsYaml} and {@link loadWorkflowsYamlAtRev}
|
|
502
|
+
* share, so the two baselines cannot drift on what a registry or a diagnostic is.
|
|
503
|
+
*/
|
|
504
|
+
function loadWorkflowsFrom(source) {
|
|
505
|
+
const manifestRaw = source.read(WORKFLOWS_MANIFEST_FILE);
|
|
506
|
+
if (manifestRaw === null)
|
|
507
|
+
return { manifest: null, workflows: [], bindings: null, diagnostics: [] };
|
|
508
|
+
const manifestPath = source.label(WORKFLOWS_MANIFEST_FILE);
|
|
509
|
+
// A manifest that cannot be read as YAML, or fails its structural pass, names no file to load: its
|
|
510
|
+
// diagnostics are the whole array.
|
|
511
|
+
const manifestData = parseYamlOrDiagnostic(manifestRaw, manifestPath, WORKFLOWS_MANIFEST_FILE);
|
|
512
|
+
if (manifestData.diagnostic)
|
|
513
|
+
throw new validation_1.DiagnosticsError([manifestData.diagnostic]);
|
|
514
|
+
// A manifest from a newer WingFoil is refused for its format alone (`dl-149`, task-251).
|
|
515
|
+
const manifestFormat = (0, format_1.newerFormatIssue)(manifestData.data, format_1.WORKFLOWS_YAML_FORMAT, WORKFLOWS_MANIFEST_FILE);
|
|
516
|
+
if (manifestFormat)
|
|
517
|
+
throw new validation_1.DiagnosticsError([{ ...manifestFormat, severity: 'error' }]);
|
|
518
|
+
const manifestResult = schema_4.WorkflowsYaml.safeParse(manifestData.data);
|
|
519
|
+
if (!manifestResult.success)
|
|
520
|
+
throw new validation_1.DiagnosticsError(zodDiagnostics(manifestResult.error.issues, WORKFLOWS_MANIFEST_FILE));
|
|
521
|
+
(0, validation_1.emitUnknownFieldWarning)(manifestData.data, schema_4.WorkflowsYaml, manifestPath);
|
|
522
|
+
const manifest = manifestResult.data;
|
|
523
|
+
source.prefetch?.([...manifest.include, bindings_1.BINDINGS_FILE]);
|
|
524
|
+
const manifestDiagnostics = [];
|
|
525
|
+
const files = [];
|
|
526
|
+
const structural = [];
|
|
527
|
+
manifest.include.forEach((includePath, position) => {
|
|
528
|
+
const raw = source.read(includePath);
|
|
529
|
+
if (raw === null) {
|
|
530
|
+
manifestDiagnostics.push({
|
|
531
|
+
code: 'E_WORKFLOW_FILE_NOT_FOUND',
|
|
532
|
+
severity: 'error',
|
|
533
|
+
file: WORKFLOWS_MANIFEST_FILE,
|
|
534
|
+
path: `include[${position}]`,
|
|
535
|
+
message: `included workflow file not found: ${includePath}`,
|
|
536
|
+
});
|
|
537
|
+
return;
|
|
213
538
|
}
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
539
|
+
const workflowPath = source.label(includePath);
|
|
540
|
+
const yaml = parseYamlOrDiagnostic(raw, workflowPath, includePath);
|
|
541
|
+
if (yaml.diagnostic) {
|
|
542
|
+
files.push({ file: includePath, workflow: null, rawName: null });
|
|
543
|
+
structural.push([yaml.diagnostic]);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const data = yaml.data;
|
|
547
|
+
const rawName = data?.name;
|
|
548
|
+
// A workflow file from a newer WingFoil: its format is its one structural diagnostic (task-251).
|
|
549
|
+
const newerFormat = (0, format_1.newerFormatIssue)(data, format_1.WORKFLOW_FORMAT, includePath);
|
|
550
|
+
if (newerFormat) {
|
|
551
|
+
files.push({ file: includePath, workflow: null, rawName: typeof rawName === 'string' ? rawName : null });
|
|
552
|
+
structural.push([{ ...newerFormat, severity: 'error' }]);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const parsed = parseWorkflowFile(data, includePath, workflowPath);
|
|
556
|
+
files.push({ file: includePath, workflow: parsed.workflow, rawName: typeof rawName === 'string' ? rawName : null });
|
|
557
|
+
structural.push(parsed.diagnostics);
|
|
558
|
+
});
|
|
559
|
+
const index = (0, workflow_diagnostics_1.indexWorkflowFiles)(files, manifest.include.length - files.length);
|
|
560
|
+
const noStartable = (0, workflow_diagnostics_1.noStartableDiagnostic)(files, index);
|
|
561
|
+
if (noStartable)
|
|
562
|
+
manifestDiagnostics.push(noStartable);
|
|
563
|
+
// `workflows/bindings.yaml` (Layer 3, task-175) is read before the workflow files are checked, since
|
|
564
|
+
// their tokens resolve through it, but its own diagnostics come last (spec-003 § "Diagnostics" order).
|
|
565
|
+
const layer3 = loadBindingsFrom(source);
|
|
566
|
+
const diagnostics = [...manifestDiagnostics];
|
|
567
|
+
files.forEach((_, i) => {
|
|
568
|
+
diagnostics.push(...structural[i], ...(0, workflow_diagnostics_1.workflowFileDiagnostics)(files, index, i, layer3.context));
|
|
569
|
+
});
|
|
570
|
+
diagnostics.push(...layer3.diagnostics);
|
|
571
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === 'error'))
|
|
572
|
+
throw new validation_1.DiagnosticsError(diagnostics);
|
|
573
|
+
// Every file is structurally valid once no error was reported.
|
|
574
|
+
return { manifest, workflows: files.map((loaded) => loaded.workflow), bindings: layer3.context.bindings, diagnostics };
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Layer 3 (spec-003, task-175): `workflows/bindings.yaml` through the same source as the workflow
|
|
578
|
+
* files. Absent → no bindings and no diagnostic. Not YAML → one `E_YAML_PARSE_ERROR`; a newer `format`
|
|
579
|
+
* (`dl-153` (A)) → one `E_INVALID_FORMAT` in place of the structural pass; structurally invalid → its Zod
|
|
580
|
+
* issues as `E_VALIDATION`; in all three cases the tokens' bindings are left undecided.
|
|
581
|
+
* Otherwise the file's loader rows ({@link bindingsFileDiagnostics}).
|
|
582
|
+
*/
|
|
583
|
+
function loadBindingsFrom(source) {
|
|
584
|
+
const raw = source.read(bindings_1.BINDINGS_FILE);
|
|
585
|
+
if (raw === null)
|
|
586
|
+
return { context: workflow_diagnostics_1.NO_BINDINGS_FILE, diagnostics: [] };
|
|
587
|
+
const undecided = { bindings: null, decided: false };
|
|
588
|
+
const yaml = parseYamlOrDiagnostic(raw, source.label(bindings_1.BINDINGS_FILE), bindings_1.BINDINGS_FILE);
|
|
589
|
+
if (yaml.diagnostic)
|
|
590
|
+
return { context: undecided, diagnostics: [yaml.diagnostic] };
|
|
591
|
+
// A bindings file from a newer WingFoil is refused for its format alone (`dl-153` (A), task-199).
|
|
592
|
+
const newerFormat = (0, format_1.newerFormatIssue)(yaml.data, format_1.BINDINGS_YAML_FORMAT, bindings_1.BINDINGS_FILE);
|
|
593
|
+
if (newerFormat)
|
|
594
|
+
return { context: undecided, diagnostics: [{ ...newerFormat, severity: 'error' }] };
|
|
595
|
+
const result = bindings_1.BindingsYaml.safeParse(yaml.data ?? {});
|
|
596
|
+
if (!result.success)
|
|
597
|
+
return { context: undecided, diagnostics: zodDiagnostics(result.error.issues, bindings_1.BINDINGS_FILE) };
|
|
598
|
+
(0, validation_1.emitUnknownFieldWarning)((yaml.data ?? {}), bindings_1.BindingsYaml, source.label(bindings_1.BINDINGS_FILE));
|
|
599
|
+
return { context: { bindings: result.data, decided: true }, diagnostics: (0, workflow_diagnostics_1.bindingsFileDiagnostics)(result.data) };
|
|
229
600
|
}
|
|
230
601
|
/**
|
|
231
602
|
* Root-relative POSIX path of the Directives tree — the form git wants for a revision listing, as
|
|
@@ -241,8 +612,11 @@ exports.DIRECTIVES_DIR_PATH = '.wingfoil/directives';
|
|
|
241
612
|
* cannot drift on what a directive file *is*.
|
|
242
613
|
*
|
|
243
614
|
* @param relativePath - The file's path relative to `.wingfoil/directives/`, POSIX-spelled.
|
|
615
|
+
* @param warn - Receives a `version` warning (task-144, R1). `src/core` prints nothing (task-143): the
|
|
616
|
+
* working-tree inventory forwards it to its `warnings`; a reader at a revision has no channel and
|
|
617
|
+
* drops it.
|
|
244
618
|
*/
|
|
245
|
-
function parseDirectiveFile(raw, filePath, relativePath) {
|
|
619
|
+
function parseDirectiveFile(raw, filePath, relativePath, warn = () => { }) {
|
|
246
620
|
const frontmatterText = (0, storage_1.extractFrontmatter)(raw);
|
|
247
621
|
if (frontmatterText === null) {
|
|
248
622
|
throw validation_1.ValidationError.semantic([
|
|
@@ -255,25 +629,79 @@ function parseDirectiveFile(raw, filePath, relativePath) {
|
|
|
255
629
|
]);
|
|
256
630
|
}
|
|
257
631
|
const data = (0, validation_1.parseYaml)(frontmatterText, filePath);
|
|
258
|
-
|
|
632
|
+
// A directive from a newer WingFoil is refused for its format alone (`dl-149`, task-251).
|
|
633
|
+
(0, format_1.refuseNewerFormat)(data, format_1.DIRECTIVE_FORMAT, filePath);
|
|
634
|
+
// `version` never fails the pillar (task-144 review, R1): a lossy number or a wrong type is a warning.
|
|
635
|
+
const versionCheck = (0, version_1.checkDirectiveVersion)(data, frontmatterText);
|
|
636
|
+
if (versionCheck.warning !== null)
|
|
637
|
+
warn(versionCheck.warning);
|
|
638
|
+
const frontmatter = (0, validation_1.runValidation)(schema_1.DirectiveFrontmatter, versionCheck.data, filePath);
|
|
259
639
|
// `join`, so `DirectiveFile.path` carries the platform separator on both loaders — `requireCustomAsset`
|
|
260
640
|
// and `selectDirectivesById` see one spelling whichever baseline produced the file.
|
|
261
641
|
return { path: (0, path_1.join)('directives', ...relativePath.split('/')), frontmatter };
|
|
262
642
|
}
|
|
263
643
|
/**
|
|
264
|
-
*
|
|
265
|
-
* `
|
|
644
|
+
* The working-tree directive inventory, warnings included: the form a caller with a channel of its
|
|
645
|
+
* own reads. `directives list` puts the warnings in its payload (`dl-042`), so they reach `--format
|
|
646
|
+
* json` readers and MCP clients; `directive remove` puts them in its refusal's `details`. `src/core`
|
|
647
|
+
* never prints them itself (task-143 review: a stray stderr line breaks spec-005 §3.2's one-object
|
|
648
|
+
* error under `--format json`).
|
|
649
|
+
*
|
|
650
|
+
* A `.md` file that cannot be **read** is skipped like any other unreadable entry: its bytes never
|
|
651
|
+
* reach the parser. A file that is read and then fails to parse or validate stays fatal — that is a
|
|
652
|
+
* broken directive, not a broken entry, and silently dropping a rule an agent must obey is worse than
|
|
653
|
+
* refusing the read. So is a `.wingfoil/directives` that cannot be listed: it is the pillar, and is
|
|
654
|
+
* refused as `E_DIRECTIVES_UNREADABLE`, naming it root-relative.
|
|
655
|
+
*/
|
|
656
|
+
function loadDirectiveInventory(root) {
|
|
657
|
+
const directivesDir = (0, path_1.join)(root, '.wingfoil', 'directives');
|
|
658
|
+
const { entries, rootError } = listMarkdownFilesSorted(directivesDir);
|
|
659
|
+
if (rootError !== undefined) {
|
|
660
|
+
throw new validation_1.ValidationError([
|
|
661
|
+
{ code: 'E_DIRECTIVES_UNREADABLE', path: '', file: '.wingfoil/directives', message: rootError },
|
|
662
|
+
]);
|
|
663
|
+
}
|
|
664
|
+
const files = [];
|
|
665
|
+
const warnings = [];
|
|
666
|
+
const skip = (relative, reason) => {
|
|
667
|
+
const shown = relative === '' ? '.wingfoil/directives' : `.wingfoil/directives/${relative}`;
|
|
668
|
+
warnings.push(`directive entry '${shown}' skipped: ${reason}`);
|
|
669
|
+
};
|
|
670
|
+
for (const entry of entries) {
|
|
671
|
+
if (entry.kind === 'skipped') {
|
|
672
|
+
skip(entry.relative, entry.reason);
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
const absolute = (0, path_1.join)(directivesDir, entry.relative);
|
|
676
|
+
let raw;
|
|
677
|
+
try {
|
|
678
|
+
raw = (0, storage_1.readDocument)(absolute);
|
|
679
|
+
}
|
|
680
|
+
catch (error) {
|
|
681
|
+
skip(entry.relative, cannotBeRead(error));
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
files.push(parseDirectiveFile(raw, `.wingfoil/directives/${entry.relative}`, entry.relative, (warning) => warnings.push(`directive '.wingfoil/directives/${entry.relative}': ${warning}`)));
|
|
685
|
+
}
|
|
686
|
+
return { files, warnings };
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Load and validate every Directives file in isolation (task-004-decoupled-pillars), against
|
|
690
|
+
* `spec-013-directive-frontmatter-schema`'s `DirectiveFrontmatter` (`src/directives/schema.ts`). Reads
|
|
266
691
|
* every `.md` file under `.wingfoil/directives/**` (built-in + custom) **in the working tree**,
|
|
267
692
|
* sorted deterministically (REQ-SYS-07: no unordered iteration in a context-building path), extracts
|
|
268
693
|
* its frontmatter (`storage.extractFrontmatter`), and validates it against `DirectiveFrontmatter`.
|
|
269
694
|
*
|
|
270
|
-
* This is the *report* baseline — what the user has now — and it is what
|
|
271
|
-
*
|
|
272
|
-
*
|
|
695
|
+
* This is the *report* baseline — what the user has now — and it is what context assembly and the
|
|
696
|
+
* MCP role Prompts read. A read that **gates** a mutation reads {@link loadDirectivesAtHead} instead
|
|
697
|
+
* (`dl-080` (B)).
|
|
698
|
+
*
|
|
699
|
+
* The files only: an entry that cannot be resolved or read is skipped (task-143, `bug-125`) and its
|
|
700
|
+
* warning dropped, since `src/core` does not print. A caller that can report the warnings reads
|
|
701
|
+
* {@link loadDirectiveInventory} instead.
|
|
273
702
|
*/
|
|
274
703
|
function loadDirectives(root) {
|
|
275
|
-
|
|
276
|
-
return listMarkdownFilesSorted(directivesDir).map((relativePath) => parseDirectiveFile((0, storage_1.readDocument)((0, path_1.join)(directivesDir, relativePath)), (0, path_1.join)(directivesDir, relativePath), relativePath));
|
|
704
|
+
return loadDirectiveInventory(root).files;
|
|
277
705
|
}
|
|
278
706
|
/**
|
|
279
707
|
* Load and validate every Directives file **as the repository has committed it** — the tree at
|
|
@@ -301,19 +729,46 @@ function loadDirectives(root) {
|
|
|
301
729
|
* unknown). `listPathsAtRev` keeps the distinction for callers that do need it.
|
|
302
730
|
*/
|
|
303
731
|
function loadDirectivesAtHead(root) {
|
|
304
|
-
|
|
732
|
+
return (0, revision_1.atHeadOr)(root, () => loadDirectivesAtRev(root, 'HEAD'), []);
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Load and validate every Directives file **as commit `rev` holds it** (task-137, `spec-012` §2
|
|
736
|
+
* `stateRef`) — the general form of {@link loadDirectivesAtHead}, which is this at `'HEAD'`: the
|
|
737
|
+
* `.md` files `storage.listPathsAtRev` lists under `.wingfoil/directives/` at that commit, in its
|
|
738
|
+
* sorted order, all read in one batch with `storage.readPathsAtRev` at the resolved sha (task-283). An empty array when the commit holds no
|
|
739
|
+
* directive file; a later commit and the working tree are never read.
|
|
740
|
+
*
|
|
741
|
+
* @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
|
|
742
|
+
* @throws `RevisionError` when `rev` is malformed or names no commit — never an empty array.
|
|
743
|
+
*/
|
|
744
|
+
function loadDirectivesAtRev(root, rev) {
|
|
745
|
+
return directivesAt(root, (0, revision_1.resolveRevision)(root, rev), rev);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* {@link loadDirectivesAtRev} for a `sha` already resolved to a commit, read without resolving it again
|
|
749
|
+
* (task-283, `bug-309`: `workflow next`, `status` and `show` read the directives at their snapshot's commit).
|
|
750
|
+
*
|
|
751
|
+
* @param sha - The full sha of a commit, as `resolveRevision` returned it; parse errors name it.
|
|
752
|
+
*/
|
|
753
|
+
function loadDirectivesAtCommit(root, sha) {
|
|
754
|
+
return directivesAt(root, sha, sha);
|
|
755
|
+
}
|
|
756
|
+
function directivesAt(root, sha, label) {
|
|
757
|
+
const paths = (0, revision_1.listPathsAtCommit)(root, sha, exports.DIRECTIVES_DIR_PATH).filter((path) => path.endsWith('.md'));
|
|
758
|
+
// One batched read (`readPathsAtRev`, one `git cat-file --batch`) for every directive, not one `git show`
|
|
759
|
+
// each (task-283, `bug-309`: 13 spawns per `workflow next` on this repository). `task-096`'s suite pins
|
|
760
|
+
// this read by name, through the `../storage` barrel.
|
|
761
|
+
const raws = (0, storage_1.readPathsAtRev)(root, sha, paths);
|
|
305
762
|
const files = [];
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
//
|
|
311
|
-
// between the two calls, and skipping is the fail-closed answer there: a directive nobody can read
|
|
312
|
-
// is a directive that does not exist, so a binding to it is refused rather than committed.
|
|
763
|
+
paths.forEach((path, index) => {
|
|
764
|
+
const raw = raws[index];
|
|
765
|
+
// Unreachable by construction — git has just listed this blob at the same sha. Skipping is still
|
|
766
|
+
// the fail-closed answer: a directive nobody can read is a directive that does not exist, so a
|
|
767
|
+
// binding to it is refused rather than committed.
|
|
313
768
|
if (raw === null)
|
|
314
|
-
|
|
315
|
-
files.push(parseDirectiveFile(raw,
|
|
316
|
-
}
|
|
769
|
+
return;
|
|
770
|
+
files.push(parseDirectiveFile(raw, `${label}:${path}`, path.slice(exports.DIRECTIVES_DIR_PATH.length + 1)));
|
|
771
|
+
});
|
|
317
772
|
return files;
|
|
318
773
|
}
|
|
319
774
|
/**
|
|
@@ -328,15 +783,16 @@ exports.ROLES_YAML_PATH = '.wingfoil/roles.yaml';
|
|
|
328
783
|
* Load and validate `.wingfoil/roles.yaml` in isolation (task-037-role-task-scoped-context,
|
|
329
784
|
* REQ-STATE-05's `directive-loader`, P3.2/P3.7 role → directive bindings) — the same shared two-pass
|
|
330
785
|
* pipeline (`readDocument` + `parseYaml` + `runValidation`) every other pillar loader uses, so this
|
|
331
|
-
* pillar's own validation never depends on another pillar's schema (REQ-SYS-02).
|
|
332
|
-
* `
|
|
333
|
-
*
|
|
786
|
+
* pillar's own validation never depends on another pillar's schema (REQ-SYS-02). The working-tree
|
|
787
|
+
* reader: `directives list` and the MCP role Prompts resolve a role's directives from it, while the
|
|
788
|
+
* execution context (`assembleExecutionContext`, `./context.ts`) reads {@link loadRolesYamlAtRev} at its
|
|
789
|
+
* `stateRef`.
|
|
334
790
|
*/
|
|
335
791
|
function loadRolesYaml(root) {
|
|
336
|
-
const
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
return (0, validation_1.runValidation)(schema_1.RolesYaml, data,
|
|
792
|
+
const raw = readConfigFile(root, exports.ROLES_YAML_PATH);
|
|
793
|
+
const data = (0, validation_1.parseYaml)(raw, exports.ROLES_YAML_PATH);
|
|
794
|
+
(0, format_1.refuseNewerFormat)(data, format_1.ROLES_YAML_FORMAT, exports.ROLES_YAML_PATH);
|
|
795
|
+
return (0, validation_1.runValidation)(schema_1.RolesYaml, data, exports.ROLES_YAML_PATH);
|
|
340
796
|
}
|
|
341
797
|
/**
|
|
342
798
|
* Load and validate `.wingfoil/roles.yaml` **as the repository has committed it** — the version at
|
|
@@ -355,10 +811,35 @@ function loadRolesYaml(root) {
|
|
|
355
811
|
* committed `roles.yaml` that does not **validate** is the different case, and that one does refuse.
|
|
356
812
|
*/
|
|
357
813
|
function loadRolesYamlAtHead(root) {
|
|
358
|
-
|
|
814
|
+
return (0, revision_1.atHeadOr)(root, () => loadRolesYamlAtRev(root, 'HEAD'), null);
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Load and validate `.wingfoil/roles.yaml` **as commit `rev` holds it** (task-137, `spec-012` §2
|
|
818
|
+
* `stateRef`) — the general form of {@link loadRolesYamlAtHead}, which is this at `'HEAD'`. `null` when
|
|
819
|
+
* that commit does not hold the file; a later commit and the working tree are never read.
|
|
820
|
+
*
|
|
821
|
+
* @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
|
|
822
|
+
* @throws `RevisionError` when `rev` is malformed or names no commit — never an empty answer.
|
|
823
|
+
*/
|
|
824
|
+
function loadRolesYamlAtRev(root, rev) {
|
|
825
|
+
return rolesYamlAt(root, (0, revision_1.resolveRevision)(root, rev), rev);
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* {@link loadRolesYamlAtRev} for a `sha` already resolved to a commit, read without resolving it again
|
|
829
|
+
* (task-283, `bug-309`).
|
|
830
|
+
*
|
|
831
|
+
* @param sha - The full sha of a commit, as `resolveRevision` returned it; parse errors name it.
|
|
832
|
+
*/
|
|
833
|
+
function loadRolesYamlAtCommit(root, sha) {
|
|
834
|
+
return rolesYamlAt(root, sha, sha);
|
|
835
|
+
}
|
|
836
|
+
function rolesYamlAt(root, sha, rev) {
|
|
837
|
+
const raw = (0, storage_1.readPathAtRev)(root, sha, exports.ROLES_YAML_PATH);
|
|
359
838
|
if (raw === null)
|
|
360
839
|
return null;
|
|
361
|
-
const label =
|
|
362
|
-
|
|
840
|
+
const label = `${rev}:${exports.ROLES_YAML_PATH}`;
|
|
841
|
+
const data = (0, validation_1.parseYaml)(raw, label);
|
|
842
|
+
(0, format_1.refuseNewerFormat)(data, format_1.ROLES_YAML_FORMAT, label);
|
|
843
|
+
return (0, validation_1.runValidation)(schema_1.RolesYaml, data, label);
|
|
363
844
|
}
|
|
364
845
|
//# sourceMappingURL=loaders.js.map
|