@devtrack-solution/codesdd 1.2.2 → 1.2.3
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/.sdd/skills/curated/api-clean-flask-langgraph/SKILL.md +17 -17
- package/.sdd/skills/curated/devtrack-api/SKILL.md +160 -28
- package/.sdd/skills/curated/devtrack-api/agents/openai.yaml +1 -1
- package/.sdd/skills/curated/devtrack-api/references/architecture-governance.md +8 -7
- package/.sdd/skills/curated/devtrack-api/references/consumer-sync-policy.md +93 -0
- package/.sdd/skills/curated/devtrack-api/references/contract-pack.yaml +317 -0
- package/.sdd/skills/curated/devtrack-api/references/field-validation-protocol.md +95 -0
- package/.sdd/skills/curated/devtrack-api/references/foundation-layout.md +295 -0
- package/.sdd/skills/curated/devtrack-api/references/implementation-checklist.md +4 -4
- package/.sdd/skills/curated/devtrack-api/references/imports-lint.md +4 -0
- package/.sdd/skills/curated/devtrack-api/references/testing-validation.md +2 -2
- package/LICENSE +1 -1
- package/README.md +243 -51
- package/bin/codesdd.js +3 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +11 -558
- package/dist/cli/program.d.ts +14 -0
- package/dist/cli/program.js +645 -0
- package/dist/commands/change.js +5 -5
- package/dist/commands/completion.d.ts +1 -1
- package/dist/commands/completion.js +9 -2
- package/dist/commands/config.js +159 -20
- package/dist/commands/feedback.js +1 -1
- package/dist/commands/schema.d.ts +63 -0
- package/dist/commands/schema.js +12 -12
- package/dist/commands/sdd/backlog.d.ts +3 -0
- package/dist/commands/sdd/backlog.js +54 -0
- package/dist/commands/sdd/execution.js +147 -16
- package/dist/commands/sdd/plugin.d.ts +3 -0
- package/dist/commands/sdd/plugin.js +153 -0
- package/dist/commands/sdd/shared.js +2 -23
- package/dist/commands/sdd/skills.js +7 -0
- package/dist/commands/sdd.js +69 -12
- package/dist/commands/spec.js +9 -9
- package/dist/commands/validate.js +6 -6
- package/dist/commands/workflow/instructions.js +6 -6
- package/dist/commands/workflow/new-change.js +3 -3
- package/dist/commands/workflow/shared.d.ts +1 -1
- package/dist/commands/workflow/shared.js +4 -4
- package/dist/core/archive.js +15 -5
- package/dist/core/artifact-graph/instruction-loader.d.ts +1 -1
- package/dist/core/artifact-graph/instruction-loader.js +3 -3
- package/dist/core/artifact-graph/resolver.d.ts +4 -4
- package/dist/core/artifact-graph/resolver.js +6 -6
- package/dist/core/branding.js +3 -3
- package/dist/core/cli/command-matrix.js +10 -1
- package/dist/core/cli-command-quality.d.ts +27 -0
- package/dist/core/cli-command-quality.js +171 -0
- package/dist/core/command-generation/adapters/costrict.d.ts +1 -1
- package/dist/core/command-generation/adapters/costrict.js +2 -2
- package/dist/core/command-generation/types.d.ts +1 -1
- package/dist/core/completions/command-registry.d.ts +1 -1
- package/dist/core/completions/command-registry.js +155 -12
- package/dist/core/completions/completion-provider.d.ts +14 -1
- package/dist/core/completions/completion-provider.js +29 -1
- package/dist/core/completions/generators/bash-generator.d.ts +1 -1
- package/dist/core/completions/generators/bash-generator.js +20 -12
- package/dist/core/completions/generators/fish-generator.d.ts +9 -1
- package/dist/core/completions/generators/fish-generator.js +39 -25
- package/dist/core/completions/generators/powershell-generator.d.ts +1 -1
- package/dist/core/completions/generators/powershell-generator.js +21 -11
- package/dist/core/completions/generators/zsh-generator.d.ts +3 -6
- package/dist/core/completions/generators/zsh-generator.js +21 -42
- package/dist/core/completions/installers/bash-installer.js +6 -6
- package/dist/core/completions/installers/fish-installer.js +1 -1
- package/dist/core/completions/installers/powershell-installer.js +14 -14
- package/dist/core/completions/installers/zsh-installer.d.ts +7 -1
- package/dist/core/completions/installers/zsh-installer.js +36 -8
- package/dist/core/completions/templates/bash-templates.d.ts +1 -1
- package/dist/core/completions/templates/bash-templates.js +12 -6
- package/dist/core/completions/templates/fish-templates.d.ts +2 -2
- package/dist/core/completions/templates/fish-templates.js +20 -9
- package/dist/core/completions/templates/powershell-templates.d.ts +1 -1
- package/dist/core/completions/templates/powershell-templates.js +13 -4
- package/dist/core/completions/templates/zsh-templates.d.ts +1 -1
- package/dist/core/completions/templates/zsh-templates.js +18 -9
- package/dist/core/config-schema.d.ts +3 -1
- package/dist/core/config-schema.js +26 -1
- package/dist/core/config.d.ts +3 -3
- package/dist/core/config.js +4 -4
- package/dist/core/global-config.d.ts +41 -12
- package/dist/core/global-config.js +344 -27
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +2 -2
- package/dist/core/init.d.ts +6 -1
- package/dist/core/init.js +99 -77
- package/dist/core/legacy-cleanup.d.ts +17 -17
- package/dist/core/legacy-cleanup.js +96 -79
- package/dist/core/list.js +18 -4
- package/dist/core/migration.d.ts +3 -1
- package/dist/core/migration.js +7 -8
- package/dist/core/parsers/change-parser.js +1 -1
- package/dist/core/parsers/markdown-parser.js +2 -2
- package/dist/core/profile-sync-drift.d.ts +1 -1
- package/dist/core/profile-sync-drift.js +13 -13
- package/dist/core/project-config.d.ts +4 -4
- package/dist/core/project-config.js +11 -11
- package/dist/core/schemas/change.schema.d.ts +1 -1
- package/dist/core/schemas/change.schema.js +1 -1
- package/dist/core/schemas/spec.schema.d.ts +1 -1
- package/dist/core/schemas/spec.schema.js +1 -1
- package/dist/core/sdd/adr.js +23 -1
- package/dist/core/sdd/agent-binding.d.ts +346 -0
- package/dist/core/sdd/agent-binding.js +343 -0
- package/dist/core/sdd/backlog-cli.d.ts +16 -0
- package/dist/core/sdd/backlog-cli.js +146 -0
- package/dist/core/sdd/backlog-conflict-policy.d.ts +58 -0
- package/dist/core/sdd/backlog-conflict-policy.js +230 -0
- package/dist/core/sdd/backlog-projection.d.ts +8 -0
- package/dist/core/sdd/backlog-projection.js +89 -0
- package/dist/core/sdd/backlog-provider-contract.d.ts +252 -0
- package/dist/core/sdd/backlog-provider-contract.js +158 -0
- package/dist/core/sdd/bootstrap.js +2 -2
- package/dist/core/sdd/check.d.ts +42 -0
- package/dist/core/sdd/check.js +22 -22
- package/dist/core/sdd/contract.d.ts +13 -0
- package/dist/core/sdd/contract.js +36 -0
- package/dist/core/sdd/coordination/coordination-adapters.d.ts +38 -0
- package/dist/core/sdd/coordination/coordination-adapters.js +139 -1
- package/dist/core/sdd/deepagent-contracts.d.ts +276 -0
- package/dist/core/sdd/deepagent-contracts.js +173 -0
- package/dist/core/sdd/deepagents/adr-governor.d.ts +2 -0
- package/dist/core/sdd/deepagents/adr-governor.js +30 -0
- package/dist/core/sdd/deepagents/backend.d.ts +63 -0
- package/dist/core/sdd/deepagents/backend.js +174 -0
- package/dist/core/sdd/deepagents/codesdd-tools.d.ts +39 -0
- package/dist/core/sdd/deepagents/codesdd-tools.js +83 -0
- package/dist/core/sdd/deepagents/evidence-mapper.d.ts +86 -0
- package/dist/core/sdd/deepagents/evidence-mapper.js +178 -0
- package/dist/core/sdd/deepagents/model-provider.d.ts +53 -0
- package/dist/core/sdd/deepagents/model-provider.js +379 -0
- package/dist/core/sdd/deepagents/policy-enforcement.d.ts +30 -0
- package/dist/core/sdd/deepagents/policy-enforcement.js +90 -0
- package/dist/core/sdd/deepagents/policy.d.ts +75 -0
- package/dist/core/sdd/deepagents/policy.js +358 -0
- package/dist/core/sdd/deepagents/quality-witness.d.ts +3 -0
- package/dist/core/sdd/deepagents/quality-witness.js +77 -0
- package/dist/core/sdd/deepagents/reversa-subagents.d.ts +75 -0
- package/dist/core/sdd/deepagents/reversa-subagents.js +182 -0
- package/dist/core/sdd/deepagents/runtime-factory.d.ts +90 -0
- package/dist/core/sdd/deepagents/runtime-factory.js +231 -0
- package/dist/core/sdd/deepagents/runtime-loader.d.ts +16 -0
- package/dist/core/sdd/deepagents/runtime-loader.js +65 -0
- package/dist/core/sdd/default-bootstrap-files.d.ts +2 -2
- package/dist/core/sdd/default-bootstrap-files.js +36 -2
- package/dist/core/sdd/default-skills.d.ts +30 -0
- package/dist/core/sdd/default-skills.js +181 -5
- package/dist/core/sdd/devtrack-api-appliance.d.ts +84 -0
- package/dist/core/sdd/devtrack-api-appliance.js +257 -0
- package/dist/core/sdd/devtrack-api-architecture.d.ts +31 -0
- package/dist/core/sdd/devtrack-api-architecture.js +608 -0
- package/dist/core/sdd/devtrack-api-import-boundary.d.ts +19 -0
- package/dist/core/sdd/devtrack-api-import-boundary.js +32 -0
- package/dist/core/sdd/diagnose.d.ts +59 -0
- package/dist/core/sdd/diagnose.js +37 -37
- package/dist/core/sdd/docs-sync.js +33 -5
- package/dist/core/sdd/domain/post-active-validation.d.ts +7 -0
- package/dist/core/sdd/domain/post-active-validation.js +61 -0
- package/dist/core/sdd/domain/transition-engine.js +1 -0
- package/dist/core/sdd/entity-reference.d.ts +5 -0
- package/dist/core/sdd/entity-reference.js +22 -0
- package/dist/core/sdd/governance-backfill.d.ts +31 -0
- package/dist/core/sdd/governance-backfill.js +359 -0
- package/dist/core/sdd/governance-parser.d.ts +21 -0
- package/dist/core/sdd/governance-parser.js +91 -0
- package/dist/core/sdd/governance-schemas.d.ts +245 -0
- package/dist/core/sdd/governance-schemas.js +143 -0
- package/dist/core/sdd/{import-openspec.d.ts → import-legacy-spec.d.ts} +7 -7
- package/dist/core/sdd/{import-openspec.js → import-legacy-spec.js} +21 -29
- package/dist/core/sdd/init.d.ts +3 -0
- package/dist/core/sdd/init.js +6 -3
- package/dist/core/sdd/json-schema.js +100 -6
- package/dist/core/sdd/knowledge-graph.d.ts +45 -0
- package/dist/core/sdd/knowledge-graph.js +288 -0
- package/dist/core/sdd/legacy-operations.js +431 -43
- package/dist/core/sdd/lenses.d.ts +1 -0
- package/dist/core/sdd/lenses.js +29 -1
- package/dist/core/sdd/migrate-workspace.js +56 -2
- package/dist/core/sdd/migrate.d.ts +1 -1
- package/dist/core/sdd/migrate.js +36 -2
- package/dist/core/sdd/package-structure-gate.d.ts +83 -0
- package/dist/core/sdd/package-structure-gate.js +362 -0
- package/dist/core/sdd/parallel-feat-automation.d.ts +152 -0
- package/dist/core/sdd/parallel-feat-automation.js +212 -0
- package/dist/core/sdd/plugin-broker.d.ts +558 -0
- package/dist/core/sdd/plugin-broker.js +482 -0
- package/dist/core/sdd/plugin-certification.d.ts +79 -0
- package/dist/core/sdd/plugin-certification.js +453 -0
- package/dist/core/sdd/plugin-cli.d.ts +109 -0
- package/dist/core/sdd/plugin-cli.js +198 -0
- package/dist/core/sdd/plugin-evidence.d.ts +275 -0
- package/dist/core/sdd/plugin-evidence.js +307 -0
- package/dist/core/sdd/plugin-manifest.d.ts +164 -0
- package/dist/core/sdd/plugin-manifest.js +215 -0
- package/dist/core/sdd/plugin-policy-pack.d.ts +88 -0
- package/dist/core/sdd/plugin-policy-pack.js +236 -0
- package/dist/core/sdd/plugin-policy.d.ts +68 -0
- package/dist/core/sdd/plugin-policy.js +212 -0
- package/dist/core/sdd/plugin-registry.d.ts +311 -0
- package/dist/core/sdd/plugin-registry.js +138 -0
- package/dist/core/sdd/plugin-skill-binding.d.ts +151 -0
- package/dist/core/sdd/plugin-skill-binding.js +339 -0
- package/dist/core/sdd/quality-artifact-manifest-validator.d.ts +28 -0
- package/dist/core/sdd/quality-artifact-manifest-validator.js +167 -0
- package/dist/core/sdd/quality-evidence-renderer.d.ts +65 -0
- package/dist/core/sdd/quality-evidence-renderer.js +218 -0
- package/dist/core/sdd/quality-scenario-runner.d.ts +42 -0
- package/dist/core/sdd/quality-scenario-runner.js +613 -0
- package/dist/core/sdd/quality-validation.d.ts +547 -0
- package/dist/core/sdd/quality-validation.js +239 -0
- package/dist/core/sdd/resolve-project-root.d.ts +2 -2
- package/dist/core/sdd/resolve-project-root.js +11 -5
- package/dist/core/sdd/sanitize.d.ts +30 -1
- package/dist/core/sdd/sanitize.js +23 -23
- package/dist/core/sdd/services/agent-run.service.d.ts +65 -0
- package/dist/core/sdd/services/agent-run.service.js +189 -0
- package/dist/core/sdd/services/breakdown.service.js +2 -1
- package/dist/core/sdd/services/context.service.js +18 -16
- package/dist/core/sdd/services/debate.service.js +15 -2
- package/dist/core/sdd/services/feature-lint.service.d.ts +22 -0
- package/dist/core/sdd/services/feature-lint.service.js +105 -5
- package/dist/core/sdd/services/finalize.service.d.ts +80 -0
- package/dist/core/sdd/services/finalize.service.js +323 -24
- package/dist/core/sdd/services/frontend-gap.service.js +22 -7
- package/dist/core/sdd/services/governance-control-plane-runtime-adapters.d.ts +17 -0
- package/dist/core/sdd/services/governance-control-plane-runtime-adapters.js +38 -0
- package/dist/core/sdd/services/governance-control-plane.service.d.ts +66 -0
- package/dist/core/sdd/services/governance-control-plane.service.js +134 -0
- package/dist/core/sdd/services/ingest-deposito.service.js +1 -1
- package/dist/core/sdd/services/legacy-capability.service.d.ts +10 -7
- package/dist/core/sdd/services/legacy-capability.service.js +38 -21
- package/dist/core/sdd/services/mcp-runtime.service.d.ts +123 -8
- package/dist/core/sdd/services/mcp-runtime.service.js +1085 -33
- package/dist/core/sdd/services/onboard.service.js +2 -1
- package/dist/core/sdd/services/rebuild.service.js +6 -1
- package/dist/core/sdd/services/skills-sync.service.d.ts +17 -5
- package/dist/core/sdd/services/skills-sync.service.js +55 -2
- package/dist/core/sdd/services/start.service.js +6 -4
- package/dist/core/sdd/skill-bundles-curation-schema.d.ts +66 -0
- package/dist/core/sdd/skill-bundles-curation-schema.js +52 -0
- package/dist/core/sdd/skill-evidence.d.ts +19 -0
- package/dist/core/sdd/skill-evidence.js +38 -0
- package/dist/core/sdd/skill-policy-pool.d.ts +46 -0
- package/dist/core/sdd/skill-policy-pool.js +185 -0
- package/dist/core/sdd/state.d.ts +22 -0
- package/dist/core/sdd/state.js +66 -41
- package/dist/core/sdd/structural-health.d.ts +42 -42
- package/dist/core/sdd/types.d.ts +33 -7
- package/dist/core/sdd/types.js +17 -0
- package/dist/core/sdd/upgrade-to-codesdd.d.ts +45 -0
- package/dist/core/sdd/upgrade-to-codesdd.js +179 -0
- package/dist/core/sdd/workspace-schemas.d.ts +285 -14
- package/dist/core/sdd/workspace-schemas.js +148 -0
- package/dist/core/sdd/write-manifest.js +22 -4
- package/dist/core/shared/skill-generation.d.ts +1 -1
- package/dist/core/shared/skill-generation.js +15 -15
- package/dist/core/shared/tool-detection.d.ts +3 -3
- package/dist/core/shared/tool-detection.js +14 -14
- package/dist/core/specs-apply.js +6 -6
- package/dist/core/templates/index.d.ts +1 -1
- package/dist/core/templates/index.js +1 -1
- package/dist/core/templates/workflows/apply-change.js +14 -14
- package/dist/core/templates/workflows/archive-change.js +32 -32
- package/dist/core/templates/workflows/bulk-archive-change.js +25 -25
- package/dist/core/templates/workflows/continue-change.js +12 -12
- package/dist/core/templates/workflows/explore.js +29 -29
- package/dist/core/templates/workflows/feedback.js +6 -6
- package/dist/core/templates/workflows/ff-change.js +24 -24
- package/dist/core/templates/workflows/new-change.js +20 -20
- package/dist/core/templates/workflows/onboard.js +33 -33
- package/dist/core/templates/workflows/propose.js +23 -23
- package/dist/core/templates/workflows/sdd.js +8 -8
- package/dist/core/templates/workflows/sync-specs.js +19 -19
- package/dist/core/templates/workflows/verify-change.js +17 -17
- package/dist/core/update.d.ts +2 -2
- package/dist/core/update.js +16 -15
- package/dist/core/validation/constants.d.ts +1 -1
- package/dist/core/validation/constants.js +1 -1
- package/dist/core/view.js +11 -11
- package/dist/telemetry/config.d.ts +2 -1
- package/dist/telemetry/config.js +17 -8
- package/dist/telemetry/index.d.ts +10 -2
- package/dist/telemetry/index.js +40 -7
- package/dist/ui/ascii-patterns.d.ts +2 -2
- package/dist/ui/ascii-patterns.js +2 -2
- package/dist/ui/welcome-screen.js +2 -2
- package/dist/utils/change-metadata.d.ts +4 -4
- package/dist/utils/change-metadata.js +6 -6
- package/dist/utils/change-utils.d.ts +3 -3
- package/dist/utils/change-utils.js +5 -5
- package/dist/utils/file-system.js +1 -1
- package/dist/utils/interactive.js +1 -1
- package/dist/utils/item-discovery.js +4 -4
- package/dist/utils/legacy-spec-compat.d.ts +2 -0
- package/dist/utils/legacy-spec-compat.js +2 -0
- package/dist/utils/shell-detection.d.ts +1 -0
- package/dist/utils/shell-detection.js +16 -0
- package/package.json +27 -17
- package/schemas/sdd/1-spec.schema.json +1 -1
- package/schemas/sdd/2-plan.schema.json +73 -1
- package/schemas/sdd/3-tasks.schema.json +73 -1
- package/schemas/sdd/4-changelog.schema.json +1 -1
- package/schemas/sdd/5-quality.schema.json +442 -2
- package/schemas/sdd/adr.schema.json +148 -0
- package/schemas/sdd/agent-binding-adapter.schema.json +210 -0
- package/schemas/sdd/agent-binding-resolution.schema.json +338 -0
- package/schemas/sdd/backlog-projection-plan.schema.json +180 -0
- package/schemas/sdd/backlog-provider-contract.schema.json +260 -0
- package/schemas/sdd/codesdd-plugin.schema.json +474 -0
- package/schemas/sdd/debate.schema.json +244 -0
- package/schemas/sdd/deepagent-decision-evidence.schema.json +58 -0
- package/schemas/sdd/deepagent-env-contract.schema.json +143 -0
- package/schemas/sdd/deepagent-quality-evidence.schema.json +108 -0
- package/schemas/sdd/deepagent-run-evidence.schema.json +192 -0
- package/schemas/sdd/deepagent-run-plan.schema.json +197 -0
- package/schemas/sdd/deepagent-run-request.schema.json +321 -0
- package/schemas/sdd/deepagent-subagent-evidence.schema.json +110 -0
- package/schemas/sdd/deepagent-tool-call-evidence.schema.json +78 -0
- package/schemas/sdd/discarded.schema.json +127 -0
- package/schemas/sdd/epic.schema.json +147 -0
- package/schemas/sdd/insight.schema.json +136 -0
- package/schemas/sdd/parallel-feat-automation-plan.schema.json +215 -0
- package/schemas/sdd/parallel-feat-automation-request.schema.json +109 -0
- package/schemas/sdd/plugin-artifact-manifest.schema.json +150 -0
- package/schemas/sdd/plugin-compliance-index.schema.json +136 -0
- package/schemas/sdd/plugin-dry-run-plan.schema.json +260 -0
- package/schemas/sdd/plugin-evidence-manifest.schema.json +569 -0
- package/schemas/sdd/plugin-policy-evaluation.schema.json +92 -0
- package/schemas/sdd/plugin-policy-pack-evaluation.schema.json +94 -0
- package/schemas/sdd/plugin-policy-pack.schema.json +196 -0
- package/schemas/sdd/plugin-registry.schema.json +558 -0
- package/schemas/sdd/plugin-rollback-manifest.schema.json +87 -0
- package/schemas/sdd/plugin-runtime-invocation-plan.schema.json +845 -0
- package/schemas/sdd/plugin-skill-binding-resolution.schema.json +305 -0
- package/schemas/sdd/plugin-skill-binding.schema.json +88 -0
- package/schemas/sdd/plugin-validation-manifest.schema.json +123 -0
- package/schemas/sdd/quality-architecture-schema.schema.json +216 -0
- package/schemas/sdd/quality-evidence-bundle.schema.json +1228 -0
- package/schemas/sdd/quality-run.schema.json +197 -0
- package/schemas/sdd/quality-scenario.schema.json +252 -0
- package/schemas/sdd/workspace-catalog.schema.json +9841 -22
- package/schemas/spec-driven/schema.yaml +4 -4
- package/schemas/spec-driven/templates/proposal.md +1 -1
- package/dist/utils/openspec-compat.d.ts +0 -2
- package/dist/utils/openspec-compat.js +0 -2
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import * as yaml from 'yaml';
|
|
4
|
+
import { adrFrontmatterSchema, debateFrontmatterSchema, discardedFrontmatterSchema, epicFrontmatterSchema, insightFrontmatterSchema, } from './governance-schemas.js';
|
|
5
|
+
const GOVERNANCE_STATUSES = new Set([
|
|
6
|
+
'NEW',
|
|
7
|
+
'OPEN',
|
|
8
|
+
'DEBATED',
|
|
9
|
+
'PROMOTED',
|
|
10
|
+
'DROPPED',
|
|
11
|
+
'APPROVED',
|
|
12
|
+
'DISCARDED',
|
|
13
|
+
'SUPERSEDED',
|
|
14
|
+
'READY',
|
|
15
|
+
'PLANNED',
|
|
16
|
+
'IN_PROGRESS',
|
|
17
|
+
'DONE',
|
|
18
|
+
'CANCELLED',
|
|
19
|
+
]);
|
|
20
|
+
const DISCOVERY_DIRS = [
|
|
21
|
+
{ type: 'INS', relDir: '.sdd/discovery/1-insights' },
|
|
22
|
+
{ type: 'DEB', relDir: '.sdd/discovery/2-debates' },
|
|
23
|
+
{ type: 'EPIC', relDir: '.sdd/discovery/3-epic' },
|
|
24
|
+
{ type: 'Discarded', relDir: '.sdd/discovery/4-discarded' },
|
|
25
|
+
];
|
|
26
|
+
function emptyBucket() {
|
|
27
|
+
return { inspected: 0, updated: 0, skipped: 0, exceptions: [] };
|
|
28
|
+
}
|
|
29
|
+
function relPath(projectRoot, filePath) {
|
|
30
|
+
return path.relative(projectRoot, filePath).replace(/\\/gu, '/');
|
|
31
|
+
}
|
|
32
|
+
function hasFrontmatter(content) {
|
|
33
|
+
return /^---\r?\n/u.test(content);
|
|
34
|
+
}
|
|
35
|
+
function frontmatterDocument(frontmatter, body) {
|
|
36
|
+
return `---\n${yaml.stringify(frontmatter).trim()}\n---\n${body}`;
|
|
37
|
+
}
|
|
38
|
+
function normalizeStatus(value, fallback) {
|
|
39
|
+
if (typeof value === 'string' && GOVERNANCE_STATUSES.has(value))
|
|
40
|
+
return value;
|
|
41
|
+
return fallback;
|
|
42
|
+
}
|
|
43
|
+
function normalizeTimestamp(value, fallback) {
|
|
44
|
+
if (typeof value === 'string' && Number.isFinite(Date.parse(value))) {
|
|
45
|
+
return new Date(value).toISOString();
|
|
46
|
+
}
|
|
47
|
+
if (value instanceof Date && Number.isFinite(value.getTime())) {
|
|
48
|
+
return value.toISOString();
|
|
49
|
+
}
|
|
50
|
+
return fallback;
|
|
51
|
+
}
|
|
52
|
+
function stripMarkdown(value) {
|
|
53
|
+
return value
|
|
54
|
+
.replace(/[`*_]+/gu, '')
|
|
55
|
+
.replace(/\[[^\]]+\]\(([^)]+)\)/gu, '$1')
|
|
56
|
+
.replace(/\s+/gu, ' ')
|
|
57
|
+
.trim();
|
|
58
|
+
}
|
|
59
|
+
function extractHeadingTitle(content, id) {
|
|
60
|
+
const heading = content.match(/^#\s+(.+)$/mu)?.[1];
|
|
61
|
+
if (!heading)
|
|
62
|
+
return id;
|
|
63
|
+
return stripMarkdown(heading
|
|
64
|
+
.replace(new RegExp(`\\b${id}\\b`, 'u'), '')
|
|
65
|
+
.replace(/^[:\-\s]+/u, '')
|
|
66
|
+
.replace(/^(Insight|Debate|Epic|Radar|ADR)\s*/iu, '')
|
|
67
|
+
.trim()) || id;
|
|
68
|
+
}
|
|
69
|
+
function extractSection(content, headingPattern) {
|
|
70
|
+
const lines = content.split(/\r?\n/u);
|
|
71
|
+
const start = lines.findIndex((line) => headingPattern.test(line.trim()));
|
|
72
|
+
if (start === -1)
|
|
73
|
+
return '';
|
|
74
|
+
const sectionLines = [];
|
|
75
|
+
for (let index = start + 1; index < lines.length; index++) {
|
|
76
|
+
if (/^##\s+/u.test(lines[index].trim()))
|
|
77
|
+
break;
|
|
78
|
+
sectionLines.push(lines[index]);
|
|
79
|
+
}
|
|
80
|
+
return sectionLines.join('\n').trim();
|
|
81
|
+
}
|
|
82
|
+
function extractBulletLines(section) {
|
|
83
|
+
return section
|
|
84
|
+
.split(/\r?\n/u)
|
|
85
|
+
.map((line) => line.trim().replace(/^[-*]\s*/u, '').trim())
|
|
86
|
+
.filter(Boolean);
|
|
87
|
+
}
|
|
88
|
+
function firstParagraph(section, fallback) {
|
|
89
|
+
const paragraph = section
|
|
90
|
+
.split(/\r?\n\r?\n/u)
|
|
91
|
+
.map((part) => stripMarkdown(part.replace(/^[-*]\s*/gmu, '')))
|
|
92
|
+
.find(Boolean);
|
|
93
|
+
return paragraph || fallback;
|
|
94
|
+
}
|
|
95
|
+
function normalizeOptionId(value, fallback = 'A') {
|
|
96
|
+
const match = String(value || '').match(/[A-Z]/u)?.[0];
|
|
97
|
+
return match || fallback;
|
|
98
|
+
}
|
|
99
|
+
function idFromFile(filePath) {
|
|
100
|
+
return path.basename(filePath).match(/\b(?:INS|DEB|EPIC|FEAT)-\d{3,4}\b/u)?.[0] ?? path.basename(filePath, '.md');
|
|
101
|
+
}
|
|
102
|
+
function discoverySchemaFor(frontmatter) {
|
|
103
|
+
if (frontmatter.artifact_type === 'INS')
|
|
104
|
+
return insightFrontmatterSchema;
|
|
105
|
+
if (frontmatter.artifact_type === 'DEB')
|
|
106
|
+
return debateFrontmatterSchema;
|
|
107
|
+
if (frontmatter.artifact_type === 'EPIC')
|
|
108
|
+
return epicFrontmatterSchema;
|
|
109
|
+
return discardedFrontmatterSchema;
|
|
110
|
+
}
|
|
111
|
+
function buildCommonDiscovery(type, id, content, record, fallbackNow) {
|
|
112
|
+
const relatedIds = Array.isArray(record?.related_ids) ? record.related_ids.filter(Boolean) : [];
|
|
113
|
+
const title = record?.title || extractHeadingTitle(content, id);
|
|
114
|
+
const createdAt = normalizeTimestamp(record?.created_at, fallbackNow);
|
|
115
|
+
const updatedAt = normalizeTimestamp(record?.updated_at, createdAt);
|
|
116
|
+
const origin = type === 'DEB'
|
|
117
|
+
? { type: 'insight', ref: relatedIds.find((value) => /^INS-\d{3,4}$/u.test(value)) }
|
|
118
|
+
: type === 'EPIC'
|
|
119
|
+
? { type: 'debate', ref: relatedIds.find((value) => /^DEB-\d{3,4}$/u.test(value)) }
|
|
120
|
+
: type === 'Discarded'
|
|
121
|
+
? { type: 'debate', ref: relatedIds.find((value) => /^DEB-\d{3,4}$/u.test(value)) || id }
|
|
122
|
+
: { type: 'user_prompt' };
|
|
123
|
+
return {
|
|
124
|
+
schema_version: 1,
|
|
125
|
+
id,
|
|
126
|
+
title,
|
|
127
|
+
...(record?.title_canonical ? { title_canonical: record.title_canonical } : {}),
|
|
128
|
+
status: normalizeStatus(record?.status, type === 'EPIC' ? 'READY' : type === 'Discarded' ? 'DISCARDED' : 'NEW'),
|
|
129
|
+
origin: origin.ref ? origin : { type: origin.type },
|
|
130
|
+
related_ids: relatedIds,
|
|
131
|
+
created_at: createdAt,
|
|
132
|
+
updated_at: updatedAt,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function buildInsightFrontmatter(id, content, record, fallbackNow) {
|
|
136
|
+
const common = buildCommonDiscovery('INS', id, content, record, fallbackNow);
|
|
137
|
+
return insightFrontmatterSchema.parse({
|
|
138
|
+
...common,
|
|
139
|
+
artifact_type: 'INS',
|
|
140
|
+
id,
|
|
141
|
+
promoted_to: common.related_ids.find((value) => !value.startsWith('INS-')),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function buildDebateFrontmatter(id, content, record, fallbackNow, exceptions, file) {
|
|
145
|
+
const common = buildCommonDiscovery('DEB', id, content, record, fallbackNow);
|
|
146
|
+
const questionSection = extractSection(content, /^##\s*1\)?\s*Decision Question/iu);
|
|
147
|
+
const criteriaSection = extractSection(content, /^##\s*2\)?\s*Decision Criteria/iu);
|
|
148
|
+
const options = Array.from(content.matchAll(/^###\s+(?:Option|Opcao|Opção)\s+([A-Z])/gmu)).map((match) => ({
|
|
149
|
+
id: normalizeOptionId(match[1]),
|
|
150
|
+
label: `Option ${normalizeOptionId(match[1])}`,
|
|
151
|
+
}));
|
|
152
|
+
const choiceLine = content.match(/(?:Choice|Escolha)(?:\s*\([^)]*\))?[:* ]+\s*([A-Z])/iu);
|
|
153
|
+
const choice = normalizeOptionId(choiceLine?.[1] || options[0]?.id || 'A');
|
|
154
|
+
const optionIds = new Set(options.map((option) => option.id));
|
|
155
|
+
optionIds.add(choice);
|
|
156
|
+
if (optionIds.size < 2) {
|
|
157
|
+
optionIds.add(choice === 'A' ? 'B' : 'A');
|
|
158
|
+
exceptions.push({ scope: 'discovery', file, id, reason: 'Debate options were defaulted from legacy body.', severity: 'warning' });
|
|
159
|
+
}
|
|
160
|
+
if (!choiceLine) {
|
|
161
|
+
exceptions.push({ scope: 'discovery', file, id, reason: 'Mediator choice was defaulted from legacy body.', severity: 'warning' });
|
|
162
|
+
}
|
|
163
|
+
const rationaleMatch = content.match(/(?:Rationale|Racional|Justificativa):\s*(.+)$/imu);
|
|
164
|
+
const rationale = stripMarkdown(rationaleMatch?.[1] || '') || 'Backfilled from legacy debate body; see Markdown body for full rationale.';
|
|
165
|
+
const output = common.status === 'DISCARDED' ? 'DISCARDED' : 'APPROVED';
|
|
166
|
+
return debateFrontmatterSchema.parse({
|
|
167
|
+
...common,
|
|
168
|
+
artifact_type: 'DEB',
|
|
169
|
+
id,
|
|
170
|
+
status: normalizeStatus(common.status, 'OPEN'),
|
|
171
|
+
decision: {
|
|
172
|
+
question: firstParagraph(questionSection, `Decide ${common.title}.`),
|
|
173
|
+
options: Array.from(optionIds).sort().map((optionId) => ({
|
|
174
|
+
id: optionId,
|
|
175
|
+
label: options.find((option) => option.id === optionId)?.label || `Option ${optionId}`,
|
|
176
|
+
})),
|
|
177
|
+
criteria: extractBulletLines(criteriaSection),
|
|
178
|
+
scoring: [],
|
|
179
|
+
mediator: {
|
|
180
|
+
choice,
|
|
181
|
+
rationale,
|
|
182
|
+
accepted_risks: [],
|
|
183
|
+
reversal_conditions: [],
|
|
184
|
+
},
|
|
185
|
+
output,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function buildEpicFrontmatter(id, content, record, fallbackNow) {
|
|
190
|
+
const common = buildCommonDiscovery('EPIC', id, content, record, fallbackNow);
|
|
191
|
+
const summarySection = extractSection(content, /^##\s*Approved Summary/iu) || extractSection(content, /^##\s*Resumo aprovado/iu);
|
|
192
|
+
return epicFrontmatterSchema.parse({
|
|
193
|
+
...common,
|
|
194
|
+
artifact_type: 'EPIC',
|
|
195
|
+
id,
|
|
196
|
+
status: normalizeStatus(common.status, 'READY'),
|
|
197
|
+
approved_summary: firstParagraph(summarySection, common.title),
|
|
198
|
+
quality_contract: {
|
|
199
|
+
scope: 'touched_scope',
|
|
200
|
+
unit_target_percent: 95,
|
|
201
|
+
integration_target_percent: 95,
|
|
202
|
+
evidence_mode: 'hybrid',
|
|
203
|
+
},
|
|
204
|
+
lock_domains: [],
|
|
205
|
+
produces: [],
|
|
206
|
+
consumes: [],
|
|
207
|
+
breakdown_policy: 'graph',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function buildDiscardedFrontmatter(id, content, record, fallbackNow) {
|
|
211
|
+
const common = buildCommonDiscovery('Discarded', id, content, record, fallbackNow);
|
|
212
|
+
const reasonSection = extractSection(content, /^##\s*Discard Reason/iu) || extractSection(content, /^##\s*Raz[aã]o/iu);
|
|
213
|
+
return discardedFrontmatterSchema.parse({
|
|
214
|
+
...common,
|
|
215
|
+
artifact_type: 'Discarded',
|
|
216
|
+
id,
|
|
217
|
+
status: 'DISCARDED',
|
|
218
|
+
discarded_from: id,
|
|
219
|
+
discard_reason: firstParagraph(reasonSection, 'Backfilled from legacy discarded artifact.'),
|
|
220
|
+
discarded_at: common.updated_at,
|
|
221
|
+
discarded_by: 'codesdd backfill',
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function buildAdrFrontmatter(filePath, content, feature, fallbackNow) {
|
|
225
|
+
const featureId = idFromFile(filePath);
|
|
226
|
+
const refs = [featureId, feature?.origin_ref].filter((value) => Boolean(value));
|
|
227
|
+
const headingTitle = extractHeadingTitle(content, featureId);
|
|
228
|
+
const createdFromBody = content.match(/(?:Created at|Finalized at|Finalizado em):\s*([^\n]+)/iu)?.[1]?.trim();
|
|
229
|
+
const createdAt = normalizeTimestamp(createdFromBody || feature?.done_at || feature?.archived_at, fallbackNow);
|
|
230
|
+
const updatedAt = normalizeTimestamp(feature?.done_at || feature?.archived_at || createdAt, createdAt);
|
|
231
|
+
const status = normalizeStatus(feature?.status, feature?.done_at || feature?.archived_at ? 'DONE' : 'IN_PROGRESS');
|
|
232
|
+
const decisionStatus = status === 'DONE' ? 'ACCEPTED' : status === 'CANCELLED' ? 'SUPERSEDED' : 'PROPOSED';
|
|
233
|
+
const referencesSection = extractSection(content, /^##\s*References/iu) || extractSection(content, /^##\s*Refs/iu) || refs.join(', ');
|
|
234
|
+
const references = extractBulletLines(referencesSection)
|
|
235
|
+
.flatMap((line) => line.split(','))
|
|
236
|
+
.map((line) => line.trim())
|
|
237
|
+
.filter(Boolean);
|
|
238
|
+
return adrFrontmatterSchema.parse({
|
|
239
|
+
schema_version: 1,
|
|
240
|
+
artifact_type: 'ADR',
|
|
241
|
+
id: `ADR-${featureId}`,
|
|
242
|
+
feature_id: featureId,
|
|
243
|
+
title: feature?.title ? `ADR ${featureId}: ${feature.title}` : `ADR ${featureId}: ${headingTitle}`,
|
|
244
|
+
status,
|
|
245
|
+
origin: { type: 'feature', ref: featureId },
|
|
246
|
+
related_ids: refs,
|
|
247
|
+
created_at: createdAt,
|
|
248
|
+
updated_at: updatedAt,
|
|
249
|
+
decision_status: decisionStatus,
|
|
250
|
+
supersedes: [],
|
|
251
|
+
superseded_by: [],
|
|
252
|
+
references: references.length > 0 ? references : refs,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
async function listMarkdownFiles(dirPath) {
|
|
256
|
+
try {
|
|
257
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
258
|
+
return entries
|
|
259
|
+
.filter((entry) => entry.isFile() && path.extname(entry.name).toLowerCase() === '.md')
|
|
260
|
+
.map((entry) => path.join(dirPath, entry.name))
|
|
261
|
+
.sort((left, right) => left.localeCompare(right));
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT')
|
|
265
|
+
return [];
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function loadYamlDocument(filePath, fallback) {
|
|
270
|
+
try {
|
|
271
|
+
return yaml.parse(await fs.readFile(filePath, 'utf-8'));
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT')
|
|
275
|
+
return fallback;
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function backfillDiscovery(projectRoot, write, now) {
|
|
280
|
+
const bucket = emptyBucket();
|
|
281
|
+
const discoveryIndex = await loadYamlDocument(path.join(projectRoot, '.sdd/state/discovery-index.yaml'), { records: [] });
|
|
282
|
+
const records = new Map((discoveryIndex.records || []).map((record) => [record.id, record]));
|
|
283
|
+
for (const group of DISCOVERY_DIRS) {
|
|
284
|
+
for (const filePath of await listMarkdownFiles(path.join(projectRoot, group.relDir))) {
|
|
285
|
+
bucket.inspected++;
|
|
286
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
287
|
+
const file = relPath(projectRoot, filePath);
|
|
288
|
+
if (hasFrontmatter(content)) {
|
|
289
|
+
bucket.skipped++;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const id = idFromFile(filePath);
|
|
293
|
+
const record = records.get(id);
|
|
294
|
+
if (!record) {
|
|
295
|
+
bucket.exceptions.push({ scope: 'discovery', file, id, reason: 'No discovery-index record found; using file body fallback fields.', severity: 'warning' });
|
|
296
|
+
}
|
|
297
|
+
const frontmatter = group.type === 'INS'
|
|
298
|
+
? buildInsightFrontmatter(id, content, record, now)
|
|
299
|
+
: group.type === 'DEB'
|
|
300
|
+
? buildDebateFrontmatter(id, content, record, now, bucket.exceptions, file)
|
|
301
|
+
: group.type === 'EPIC'
|
|
302
|
+
? buildEpicFrontmatter(id, content, record, now)
|
|
303
|
+
: buildDiscardedFrontmatter(id, content, record, now);
|
|
304
|
+
discoverySchemaFor(frontmatter).parse(frontmatter);
|
|
305
|
+
if (write)
|
|
306
|
+
await fs.writeFile(filePath, frontmatterDocument(frontmatter, content), 'utf-8');
|
|
307
|
+
bucket.updated++;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return bucket;
|
|
311
|
+
}
|
|
312
|
+
async function backfillAdrs(projectRoot, write, now) {
|
|
313
|
+
const bucket = emptyBucket();
|
|
314
|
+
const backlog = await loadYamlDocument(path.join(projectRoot, '.sdd/state/backlog.yaml'), {
|
|
315
|
+
items: [],
|
|
316
|
+
});
|
|
317
|
+
const features = new Map((backlog.items || []).map((item) => [item.id, item]));
|
|
318
|
+
for (const filePath of await listMarkdownFiles(path.join(projectRoot, '.sdd/core/adrs'))) {
|
|
319
|
+
bucket.inspected++;
|
|
320
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
321
|
+
const file = relPath(projectRoot, filePath);
|
|
322
|
+
if (hasFrontmatter(content)) {
|
|
323
|
+
bucket.skipped++;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const featureId = idFromFile(filePath);
|
|
327
|
+
const feature = features.get(featureId);
|
|
328
|
+
if (!feature) {
|
|
329
|
+
bucket.exceptions.push({ scope: 'adr', file, id: featureId, reason: 'No backlog record found; using ADR body fallback fields.', severity: 'warning' });
|
|
330
|
+
}
|
|
331
|
+
const frontmatter = buildAdrFrontmatter(filePath, content, feature, now);
|
|
332
|
+
if (write)
|
|
333
|
+
await fs.writeFile(filePath, frontmatterDocument(frontmatter, content), 'utf-8');
|
|
334
|
+
bucket.updated++;
|
|
335
|
+
}
|
|
336
|
+
return bucket;
|
|
337
|
+
}
|
|
338
|
+
export async function backfillGovernanceFrontmatter(options) {
|
|
339
|
+
const now = options.nowIso || new Date().toISOString();
|
|
340
|
+
const write = options.write === true;
|
|
341
|
+
const report = {
|
|
342
|
+
generated_at: now,
|
|
343
|
+
write,
|
|
344
|
+
discovery: emptyBucket(),
|
|
345
|
+
adr: emptyBucket(),
|
|
346
|
+
};
|
|
347
|
+
if (options.discovery !== false) {
|
|
348
|
+
report.discovery = await backfillDiscovery(options.projectRoot, write, now);
|
|
349
|
+
}
|
|
350
|
+
if (options.adr !== false) {
|
|
351
|
+
report.adr = await backfillAdrs(options.projectRoot, write, now);
|
|
352
|
+
}
|
|
353
|
+
if (options.reportPath) {
|
|
354
|
+
await fs.mkdir(path.dirname(options.reportPath), { recursive: true });
|
|
355
|
+
await fs.writeFile(options.reportPath, yaml.stringify(report), 'utf-8');
|
|
356
|
+
}
|
|
357
|
+
return report;
|
|
358
|
+
}
|
|
359
|
+
//# sourceMappingURL=governance-backfill.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export type GovernanceFormat = 'frontmatter-md' | 'legacy-md';
|
|
3
|
+
export interface ParsedGovernanceFile<T> {
|
|
4
|
+
frontmatter: T | null;
|
|
5
|
+
body: string;
|
|
6
|
+
hasFrontmatter: boolean;
|
|
7
|
+
format: GovernanceFormat;
|
|
8
|
+
rawPath: string;
|
|
9
|
+
}
|
|
10
|
+
type GovernanceParseErrorKind = 'invalid_yaml' | 'schema_violation' | 'invalid_delimiter';
|
|
11
|
+
export declare class GovernanceParseError extends Error {
|
|
12
|
+
readonly kind: GovernanceParseErrorKind;
|
|
13
|
+
readonly rawPath: string;
|
|
14
|
+
readonly detail: string;
|
|
15
|
+
readonly line?: number;
|
|
16
|
+
constructor(kind: GovernanceParseErrorKind, rawPath: string, detail: string, line?: number);
|
|
17
|
+
}
|
|
18
|
+
export declare function parseGovernanceFile<T>(rawPath: string, content: string, schema?: z.ZodType<T>): ParsedGovernanceFile<T>;
|
|
19
|
+
export declare function parseGovernanceDir<T>(dirPath: string, schema: z.ZodType<T>): Promise<Array<ParsedGovernanceFile<T>>>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=governance-parser.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import { parseDocument } from 'yaml';
|
|
4
|
+
const FRONTMATTER_OPENING_PATTERN = /^---\r?\n/;
|
|
5
|
+
const FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---\r?\n/;
|
|
6
|
+
export class GovernanceParseError extends Error {
|
|
7
|
+
kind;
|
|
8
|
+
rawPath;
|
|
9
|
+
detail;
|
|
10
|
+
line;
|
|
11
|
+
constructor(kind, rawPath, detail, line) {
|
|
12
|
+
super(`${kind} at ${rawPath}${line ? `:${line}` : ''} - ${detail}`);
|
|
13
|
+
this.name = 'GovernanceParseError';
|
|
14
|
+
this.kind = kind;
|
|
15
|
+
this.rawPath = rawPath;
|
|
16
|
+
this.detail = detail;
|
|
17
|
+
this.line = line;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function issuePath(issue) {
|
|
21
|
+
const pathText = issue.path?.length ? issue.path.join('.') : '(root)';
|
|
22
|
+
return `${pathText}: ${issue.message}`;
|
|
23
|
+
}
|
|
24
|
+
function yamlErrorLine(error, source) {
|
|
25
|
+
const linePos = error.linePos?.[0]?.line;
|
|
26
|
+
if (typeof linePos === 'number')
|
|
27
|
+
return linePos;
|
|
28
|
+
const offset = error.pos?.[0];
|
|
29
|
+
if (typeof offset !== 'number')
|
|
30
|
+
return undefined;
|
|
31
|
+
return source.slice(0, offset).split(/\r\n|\r|\n/u).length;
|
|
32
|
+
}
|
|
33
|
+
export function parseGovernanceFile(rawPath, content, schema) {
|
|
34
|
+
if (FRONTMATTER_OPENING_PATTERN.test(content) && !FRONTMATTER_PATTERN.test(content)) {
|
|
35
|
+
throw new GovernanceParseError('invalid_delimiter', rawPath, 'Frontmatter opening delimiter found without a closing delimiter on its own line.');
|
|
36
|
+
}
|
|
37
|
+
const match = FRONTMATTER_PATTERN.exec(content);
|
|
38
|
+
if (!match) {
|
|
39
|
+
return {
|
|
40
|
+
frontmatter: null,
|
|
41
|
+
body: content,
|
|
42
|
+
hasFrontmatter: false,
|
|
43
|
+
format: 'legacy-md',
|
|
44
|
+
rawPath,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const frontmatterRaw = match[1];
|
|
48
|
+
const body = content.slice(match[0].length);
|
|
49
|
+
const yamlDoc = parseDocument(frontmatterRaw, { prettyErrors: false });
|
|
50
|
+
if (yamlDoc.errors.length > 0) {
|
|
51
|
+
const firstError = yamlDoc.errors[0];
|
|
52
|
+
const line = yamlErrorLine(firstError, frontmatterRaw);
|
|
53
|
+
throw new GovernanceParseError('invalid_yaml', rawPath, firstError.message, line);
|
|
54
|
+
}
|
|
55
|
+
const parsedYaml = yamlDoc.toJSON();
|
|
56
|
+
if (schema) {
|
|
57
|
+
const result = schema.safeParse(parsedYaml);
|
|
58
|
+
if (!result.success) {
|
|
59
|
+
const firstIssue = result.error.issues[0];
|
|
60
|
+
throw new GovernanceParseError('schema_violation', rawPath, issuePath(firstIssue));
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
frontmatter: result.data,
|
|
64
|
+
body,
|
|
65
|
+
hasFrontmatter: true,
|
|
66
|
+
format: 'frontmatter-md',
|
|
67
|
+
rawPath,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
frontmatter: parsedYaml,
|
|
72
|
+
body,
|
|
73
|
+
hasFrontmatter: true,
|
|
74
|
+
format: 'frontmatter-md',
|
|
75
|
+
rawPath,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export async function parseGovernanceDir(dirPath, schema) {
|
|
79
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
80
|
+
const files = entries
|
|
81
|
+
.filter((entry) => entry.isFile() && path.extname(entry.name).toLowerCase() === '.md')
|
|
82
|
+
.map((entry) => path.join(dirPath, entry.name))
|
|
83
|
+
.sort((left, right) => left.localeCompare(right));
|
|
84
|
+
const parsed = [];
|
|
85
|
+
for (const filePath of files) {
|
|
86
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
87
|
+
parsed.push(parseGovernanceFile(filePath, content, schema));
|
|
88
|
+
}
|
|
89
|
+
return parsed;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=governance-parser.js.map
|