freeharness 0.4.1
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/LICENSE +21 -0
- package/NOTICE +108 -0
- package/README.md +134 -0
- package/canonical/HARVEST.yaml +858 -0
- package/canonical/PROVENANCE.yaml +740 -0
- package/canonical/agents/README.md +95 -0
- package/canonical/agents/agentic-architect.md +18 -0
- package/canonical/agents/database-reviewer.md +41 -0
- package/canonical/agents/explorer-ai-engineer-analyst.md +65 -0
- package/canonical/agents/pr-reviewer.md +96 -0
- package/canonical/agents/python-reviewer.md +55 -0
- package/canonical/agents/report-formatter.md +19 -0
- package/canonical/agents/security-reviewer.md +53 -0
- package/canonical/agents/test-writer.md +34 -0
- package/canonical/commands/fh-context.md +37 -0
- package/canonical/evals/promptfoo.redteam.yaml +108 -0
- package/canonical/hooks/cost-prices.json +14 -0
- package/canonical/hooks/freeharness-auto-memory.mjs +158 -0
- package/canonical/hooks/freeharness-config-guard.mjs +162 -0
- package/canonical/hooks/freeharness-context-monitor.mjs +198 -0
- package/canonical/hooks/freeharness-converge.mjs +227 -0
- package/canonical/hooks/freeharness-cost-tracker.mjs +1027 -0
- package/canonical/hooks/freeharness-memory-guard.mjs +57 -0
- package/canonical/hooks/freeharness-output-pruner.mjs +333 -0
- package/canonical/hooks/freeharness-precompact.mjs +323 -0
- package/canonical/hooks/freeharness-repeat-tool.mjs +208 -0
- package/canonical/hooks/freeharness-security-guidance.mjs +164 -0
- package/canonical/hooks/freeharness-session-start +583 -0
- package/canonical/hooks/hooks.auto-memory.claude-code.json +16 -0
- package/canonical/hooks/hooks.claude-code.json +17 -0
- package/canonical/hooks/hooks.config-guard.claude-code.json +16 -0
- package/canonical/hooks/hooks.context-monitor.claude-code.json +15 -0
- package/canonical/hooks/hooks.converge.claude-code.json +15 -0
- package/canonical/hooks/hooks.cost-tracker.claude-code.json +37 -0
- package/canonical/hooks/hooks.memory-guard.claude-code.json +16 -0
- package/canonical/hooks/hooks.output-pruner.claude-code.json +16 -0
- package/canonical/hooks/hooks.precompact.claude-code.json +15 -0
- package/canonical/hooks/hooks.repeat-tool.claude-code.json +25 -0
- package/canonical/hooks/hooks.security-guidance.claude-code.json +16 -0
- package/canonical/mcp/servers.json +20 -0
- package/canonical/openspec/config.base.yaml +89 -0
- package/canonical/openspec/schema.spec-driven.yaml +277 -0
- package/canonical/openspec/templates/data-contract.md +36 -0
- package/canonical/openspec/templates/design.md +84 -0
- package/canonical/openspec/templates/eval-plan.md +33 -0
- package/canonical/openspec/templates/follow-ups.md +2 -0
- package/canonical/openspec/templates/proposal.md +45 -0
- package/canonical/openspec/templates/spec.md +42 -0
- package/canonical/openspec/templates/tasks.md +38 -0
- package/canonical/profiles/local-llamacpp.LOCAL-MODELS.md +47 -0
- package/canonical/profiles/local-llamacpp.json +27 -0
- package/canonical/profiles/local-ollama.json +20 -0
- package/canonical/profiles/mcp.json +91 -0
- package/canonical/rules/CLAUDE.md +78 -0
- package/canonical/rules/project-CLAUDE.template.md +62 -0
- package/canonical/rules/routing.md +20 -0
- package/canonical/rules/safety.md +41 -0
- package/canonical/settings/settings.cache-hygiene.claude-code.json +5 -0
- package/canonical/skills/agent-evaluation/SKILL.md +102 -0
- package/canonical/skills/agent-evaluation/assets/evaluation_report_template.md +206 -0
- package/canonical/skills/agent-evaluation/evals/cases.yaml +106 -0
- package/canonical/skills/agent-evaluation/references/dataset-preparation.md +351 -0
- package/canonical/skills/agent-evaluation/references/scorers-constraints.md +165 -0
- package/canonical/skills/agent-evaluation/references/scorers.md +431 -0
- package/canonical/skills/agent-evaluation/references/setup-guide.md +252 -0
- package/canonical/skills/agent-evaluation/references/tracing-integration.md +164 -0
- package/canonical/skills/agent-evaluation/references/troubleshooting.md +514 -0
- package/canonical/skills/agent-evaluation/scripts/analyze_results.py +462 -0
- package/canonical/skills/agent-evaluation/scripts/create_dataset_template.py +382 -0
- package/canonical/skills/agent-evaluation/scripts/list_datasets.py +273 -0
- package/canonical/skills/agent-evaluation/scripts/run_evaluation_template.py +309 -0
- package/canonical/skills/agent-evaluation/scripts/setup_mlflow.py +353 -0
- package/canonical/skills/agent-evaluation/scripts/utils/__init__.py +27 -0
- package/canonical/skills/agent-evaluation/scripts/utils/env_validation.py +123 -0
- package/canonical/skills/agent-evaluation/scripts/utils/tracing_utils.py +108 -0
- package/canonical/skills/agent-evaluation/scripts/validate_agent_tracing.py +148 -0
- package/canonical/skills/agent-evaluation/scripts/validate_auth.py +240 -0
- package/canonical/skills/agent-evaluation/scripts/validate_environment.py +185 -0
- package/canonical/skills/agent-evaluation/scripts/validate_tracing_runtime.py +318 -0
- package/canonical/skills/agentic-architecture/SKILL.md +41 -0
- package/canonical/skills/agentic-architecture/evals/cases.yaml +94 -0
- package/canonical/skills/agentic-architecture/reference/context-engineering.md +75 -0
- package/canonical/skills/agentic-architecture/reference/patterns.md +76 -0
- package/canonical/skills/agentic-architecture/reference/reliability-observability.md +94 -0
- package/canonical/skills/agentic-architecture/reference/review-framework.md +59 -0
- package/canonical/skills/code-review/SKILL.md +52 -0
- package/canonical/skills/code-review/evals/cases.yaml +197 -0
- package/canonical/skills/git-workflow/SKILL.md +41 -0
- package/canonical/skills/git-workflow/evals/cases.yaml +92 -0
- package/canonical/skills/local-code/SKILL.md +24 -0
- package/canonical/skills/local-code/evals/cases.yaml +85 -0
- package/canonical/skills/local-code/scripts/query_llama.py +68 -0
- package/canonical/skills/report-formatting/SKILL.md +62 -0
- package/canonical/skills/report-formatting/evals/cases.yaml +83 -0
- package/canonical/skills/report-formatting/reference/ARCHITECTURE.md +135 -0
- package/canonical/skills/report-formatting/reference/HELPERS.md +120 -0
- package/canonical/skills/report-formatting/reference/PPTX.md +136 -0
- package/canonical/skills/report-formatting/reference/RELEASE.md +99 -0
- package/canonical/skills/report-formatting/reference/TEMPLATES.md +48 -0
- package/canonical/skills/report-formatting/reference/authoring.md +66 -0
- package/canonical/skills/report-formatting/scripts/architecture-template.md +271 -0
- package/canonical/skills/report-formatting/scripts/assets/claro-logo.png +0 -0
- package/canonical/skills/report-formatting/scripts/assets/claro-wordmark.svg +1 -0
- package/canonical/skills/report-formatting/scripts/build-architecture.js +57 -0
- package/canonical/skills/report-formatting/scripts/build-md-claro.js +224 -0
- package/canonical/skills/report-formatting/scripts/build-release.js +51 -0
- package/canonical/skills/report-formatting/scripts/check-env.js +58 -0
- package/canonical/skills/report-formatting/scripts/docmodel.js +283 -0
- package/canonical/skills/report-formatting/scripts/example-build.js +119 -0
- package/canonical/skills/report-formatting/scripts/example_build_ArchitectureReview_docx.js +195 -0
- package/canonical/skills/report-formatting/scripts/example_build_ExecutiveReport_docx.js +114 -0
- package/canonical/skills/report-formatting/scripts/package.json +17 -0
- package/canonical/skills/report-formatting/scripts/pptx/example_build_StatusReport_pptx.py +223 -0
- package/canonical/skills/report-formatting/scripts/pptx/example_build_WeeklyStatus_pptx.py +245 -0
- package/canonical/skills/report-formatting/scripts/pptx/pptx_office.py +144 -0
- package/canonical/skills/report-formatting/scripts/pptx/requirements.txt +7 -0
- package/canonical/skills/report-formatting/scripts/pptx/status_report_blocks.py +193 -0
- package/canonical/skills/report-formatting/scripts/pptx/theme_claro_pptx.py +473 -0
- package/canonical/skills/report-formatting/scripts/release-template.md +80 -0
- package/canonical/skills/report-formatting/scripts/render-mermaid.js +105 -0
- package/canonical/skills/report-formatting/scripts/theme-claro.js +403 -0
- package/canonical/skills/subagent-driven-development/SKILL.md +60 -0
- package/canonical/skills/subagent-driven-development/evals/cases.yaml +208 -0
- package/canonical/skills/subagent-driven-development/reference/brief.md +91 -0
- package/canonical/skills/subagent-driven-development/reference/handoff.md +118 -0
- package/canonical/skills/subagent-driven-development/scripts/validate-handoff.mjs +850 -0
- package/canonical/skills/systematic-debugging/SKILL.md +44 -0
- package/canonical/skills/systematic-debugging/evals/cases.yaml +90 -0
- package/canonical/skills/test-driven-development/SKILL.md +42 -0
- package/canonical/skills/test-driven-development/evals/cases.yaml +86 -0
- package/canonical/skills/test-driven-development/reference/writing-good-tests.md +52 -0
- package/canonical/skills/verification-before-completion/SKILL.md +53 -0
- package/canonical/skills/verification-before-completion/evals/cases.yaml +107 -0
- package/dist/adapters/content/language-lint.d.ts +46 -0
- package/dist/adapters/content/language-lint.js +191 -0
- package/dist/adapters/harness/claude-code.d.ts +57 -0
- package/dist/adapters/harness/claude-code.js +385 -0
- package/dist/adapters/harness/index.d.ts +3 -0
- package/dist/adapters/harness/index.js +17 -0
- package/dist/adapters/harness/opencode.d.ts +7 -0
- package/dist/adapters/harness/opencode.js +166 -0
- package/dist/adapters/harness/plugin.d.ts +22 -0
- package/dist/adapters/harness/plugin.js +257 -0
- package/dist/adapters/memory/doctor.d.ts +480 -0
- package/dist/adapters/memory/doctor.js +1217 -0
- package/dist/adapters/memory/vendored.d.ts +150 -0
- package/dist/adapters/memory/vendored.js +487 -0
- package/dist/adapters/package/node.d.ts +9 -0
- package/dist/adapters/package/node.js +20 -0
- package/dist/adapters/prompt/readline.d.ts +19 -0
- package/dist/adapters/prompt/readline.js +53 -0
- package/dist/adapters/sync/diff.d.ts +43 -0
- package/dist/adapters/sync/diff.js +380 -0
- package/dist/cli/cleanup.d.ts +86 -0
- package/dist/cli/cleanup.js +382 -0
- package/dist/cli/cost.d.ts +304 -0
- package/dist/cli/cost.js +501 -0
- package/dist/cli/doctor.d.ts +205 -0
- package/dist/cli/doctor.js +654 -0
- package/dist/cli/executor.d.ts +5 -0
- package/dist/cli/executor.js +48 -0
- package/dist/cli/plugin-validate.d.ts +37 -0
- package/dist/cli/plugin-validate.js +256 -0
- package/dist/cli/prefix.d.ts +68 -0
- package/dist/cli/prefix.js +218 -0
- package/dist/cli/profile.d.ts +170 -0
- package/dist/cli/profile.js +285 -0
- package/dist/cli/runner.d.ts +10 -0
- package/dist/cli/runner.js +27 -0
- package/dist/cli/uninstall.d.ts +65 -0
- package/dist/cli/uninstall.js +147 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +989 -0
- package/dist/infra/dogfood.d.ts +31 -0
- package/dist/infra/dogfood.js +51 -0
- package/dist/infra/fsutil.d.ts +40 -0
- package/dist/infra/fsutil.js +143 -0
- package/dist/infra/manifest.d.ts +76 -0
- package/dist/infra/manifest.js +92 -0
- package/dist/infra/paths.d.ts +19 -0
- package/dist/infra/paths.js +74 -0
- package/dist/infra/routing.d.ts +4 -0
- package/dist/infra/routing.js +7 -0
- package/node_modules/@freeharness/core/dist/context.d.ts +30 -0
- package/node_modules/@freeharness/core/dist/context.js +1 -0
- package/node_modules/@freeharness/core/dist/index.d.ts +5 -0
- package/node_modules/@freeharness/core/dist/index.js +6 -0
- package/node_modules/@freeharness/core/dist/planner.d.ts +13 -0
- package/node_modules/@freeharness/core/dist/planner.js +18 -0
- package/node_modules/@freeharness/core/dist/ports.d.ts +90 -0
- package/node_modules/@freeharness/core/dist/ports.js +1 -0
- package/node_modules/@freeharness/core/dist/registry.d.ts +41 -0
- package/node_modules/@freeharness/core/dist/registry.js +124 -0
- package/node_modules/@freeharness/core/dist/tiers.d.ts +30 -0
- package/node_modules/@freeharness/core/dist/tiers.js +99 -0
- package/node_modules/@freeharness/core/package.json +34 -0
- package/node_modules/@freeharness/sdd/PROVENANCE.yaml +175 -0
- package/node_modules/@freeharness/sdd/dist/src/bin/openspec.d.ts +2 -0
- package/node_modules/@freeharness/sdd/dist/src/bin/openspec.js +166 -0
- package/node_modules/@freeharness/sdd/dist/src/index.d.ts +14 -0
- package/node_modules/@freeharness/sdd/dist/src/index.js +11 -0
- package/node_modules/@freeharness/sdd/dist/src/init.d.ts +39 -0
- package/node_modules/@freeharness/sdd/dist/src/init.js +190 -0
- package/node_modules/@freeharness/sdd/dist/src/root-resolver.d.ts +8 -0
- package/node_modules/@freeharness/sdd/dist/src/root-resolver.js +32 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.d.ts +81 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.js +483 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/context.d.ts +4 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/context.js +48 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.d.ts +57 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.js +279 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.d.ts +67 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.js +460 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/list.d.ts +5 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/list.js +95 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/new-change.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/new-change.js +150 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/schemas.d.ts +4 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/schemas.js +23 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.d.ts +56 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.js +140 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/status.d.ts +8 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/status.js +85 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.js +229 -0
- package/node_modules/@freeharness/sdd/dist/src/validate-change.d.ts +40 -0
- package/node_modules/@freeharness/sdd/dist/src/validate-change.js +114 -0
- package/node_modules/@freeharness/sdd/dist/src/validate.d.ts +24 -0
- package/node_modules/@freeharness/sdd/dist/src/validate.js +72 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/specs-apply.d.ts +130 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/specs-apply.js +981 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-metadata.d.ts +46 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-metadata.js +128 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.d.ts +26 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.js +32 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/file-system.d.ts +50 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/file-system.js +394 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/global-config.d.ts +15 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/global-config.js +49 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/graph.d.ts +71 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/graph.js +164 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.d.ts +11 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.js +21 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/instruction-loader.d.ts +102 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/instruction-loader.js +230 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.d.ts +18 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.js +138 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/resolver.d.ts +82 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/resolver.js +333 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/schema.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/schema.js +110 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/state.d.ts +11 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/state.js +32 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/task-progress.d.ts +33 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/task-progress.js +149 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/types.d.ts +39 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/types.js +49 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/claude.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/claude.js +31 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/index.d.ts +10 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/index.js +10 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/opencode.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/opencode.js +26 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/generator.d.ts +28 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/generator.js +41 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/index.d.ts +23 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/index.js +25 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/invocation.d.ts +72 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/invocation.js +76 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/registry.d.ts +39 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/registry.js +53 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/types.d.ts +62 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/types.js +7 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/yaml.d.ts +35 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/yaml.js +47 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-references.d.ts +33 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-references.js +55 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/shared/allowed-tools.d.ts +11 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/shared/allowed-tools.js +11 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/shared/skill-generation.d.ts +48 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/shared/skill-generation.js +99 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/skill-templates.d.ts +19 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/skill-templates.js +18 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/types.d.ts +18 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/types.js +4 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/apply-change.d.ts +19 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/apply-change.js +207 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/archive-change.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/archive-change.js +409 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/bulk-archive-change.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/bulk-archive-change.js +673 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/continue-change.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/continue-change.js +230 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/explore.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/explore.js +550 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/feedback.d.ts +8 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/feedback.js +107 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/ff-change.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/ff-change.js +228 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/new-change.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/new-change.js +147 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/onboard.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/onboard.js +571 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/propose.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/propose.js +322 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/store-selection.d.ts +7 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/store-selection.js +7 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/sync-specs.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/sync-specs.js +518 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/update-change.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/update-change.js +176 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/verify-change.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/verify-change.js +344 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.d.ts +39 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.js +54 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/delta-resolution.d.ts +35 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/delta-resolution.js +171 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.d.ts +27 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.js +210 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/code-fence.d.ts +14 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/code-fence.js +48 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/markdown-parser.d.ts +22 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/markdown-parser.js +186 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.d.ts +65 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.js +409 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.d.ts +55 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.js +98 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.d.ts +8 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.js +77 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/purpose-placeholder.d.ts +44 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/purpose-placeholder.js +116 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/base.schema.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/base.schema.js +17 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/change.schema.d.ts +72 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/change.schema.js +30 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/index.d.ts +3 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/index.js +3 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/spec.schema.d.ts +17 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/spec.schema.js +14 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.d.ts +39 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.js +114 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.d.ts +45 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.js +105 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-numbering.d.ts +15 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-numbering.js +56 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/types.d.ts +17 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/types.js +1 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.d.ts +94 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.js +756 -0
- package/node_modules/@freeharness/sdd/package.json +57 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/LICENSE.upstream +22 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/schema.upstream.yaml +232 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/README.md +19 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-apply-change/SKILL.md +187 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-archive-change/SKILL.md +181 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-bulk-archive-change/SKILL.md +338 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-continue-change/SKILL.md +117 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-explore/SKILL.md +342 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-ff-change/SKILL.md +116 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-new-change/SKILL.md +76 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-onboard/SKILL.md +560 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-propose/SKILL.md +162 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-sync-specs/SKILL.md +261 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-update-change/SKILL.md +90 -0
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-verify-change/SKILL.md +174 -0
- package/package.json +70 -0
- package/provenance/canonical.PROVENANCE.yaml +740 -0
- package/provenance/claude-mem.LICENSE.upstream +202 -0
- package/provenance/memory.PROVENANCE.yaml +399 -0
- package/provenance/sdd.PROVENANCE.yaml +175 -0
- package/vendor-licenses/caveman.LICENSE +27 -0
- package/vendor-licenses/deepseek-harness.LICENSE +21 -0
- package/vendor-licenses/ecc.LICENSE +21 -0
- package/vendor-licenses/pi.LICENSE +21 -0
- package/vendor-licenses/superpowers.LICENSE +21 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface RequirementBlock {
|
|
2
|
+
headerLine: string;
|
|
3
|
+
name: string;
|
|
4
|
+
raw: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RequirementsSectionParts {
|
|
7
|
+
before: string;
|
|
8
|
+
headerLine: string;
|
|
9
|
+
preamble: string;
|
|
10
|
+
bodyBlocks: RequirementBlock[];
|
|
11
|
+
after: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function normalizeRequirementName(name: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Case- and whitespace-insensitive fold of a requirement name. Requirement
|
|
16
|
+
* matching itself is case-sensitive (normalizeRequirementName); this fold
|
|
17
|
+
* exists only for typo detection - near-miss REMOVED headers and the
|
|
18
|
+
* RENAMED+REMOVED cross-section conflict - where two spellings that differ
|
|
19
|
+
* only in case or interior whitespace mean a mistake, never two requirements.
|
|
20
|
+
*/
|
|
21
|
+
export declare function foldRequirementName(name: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Extracts the Requirements section from a spec file and parses requirement blocks.
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractRequirementsSection(content: string): RequirementsSectionParts;
|
|
26
|
+
/**
|
|
27
|
+
* A level-3 header inside `## ADDED`/`## MODIFIED Requirements` that is not a
|
|
28
|
+
* canonical `### Requirement:` header, recorded at the moment the delta reader
|
|
29
|
+
* skips over it. Surfaced as an INFO note by `validate <change>` (#498).
|
|
30
|
+
*/
|
|
31
|
+
export interface SkippedHeader {
|
|
32
|
+
header: string;
|
|
33
|
+
section: string;
|
|
34
|
+
line: number;
|
|
35
|
+
}
|
|
36
|
+
export interface DeltaPlan {
|
|
37
|
+
added: RequirementBlock[];
|
|
38
|
+
modified: RequirementBlock[];
|
|
39
|
+
removed: string[];
|
|
40
|
+
renamed: Array<{
|
|
41
|
+
from: string;
|
|
42
|
+
to: string;
|
|
43
|
+
}>;
|
|
44
|
+
skippedHeaders: SkippedHeader[];
|
|
45
|
+
sectionPresence: {
|
|
46
|
+
added: boolean;
|
|
47
|
+
modified: boolean;
|
|
48
|
+
removed: boolean;
|
|
49
|
+
renamed: boolean;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse a delta-formatted spec change file content into a DeltaPlan with raw blocks.
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseDeltaSpec(content: string): DeltaPlan;
|
|
56
|
+
/**
|
|
57
|
+
* Scenario names the current requirement block has and the incoming
|
|
58
|
+
* (MODIFIED) block does not. A MODIFIED requirement replaces the whole block,
|
|
59
|
+
* so every name reported here would be dropped from the main spec.
|
|
60
|
+
*
|
|
61
|
+
* Shared by archive (which refuses to apply the block) and validate (which
|
|
62
|
+
* reports the same loss at authoring time, #1477), so the two cannot disagree
|
|
63
|
+
* about what counts as a dropped scenario.
|
|
64
|
+
*/
|
|
65
|
+
export declare function findMissingCurrentScenarios(current: RequirementBlock, incoming: RequirementBlock): string[];
|
package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.js
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { buildCodeFenceMask, SCENARIO_HEADER } from './requirement-text.js';
|
|
2
|
+
export function normalizeRequirementName(name) {
|
|
3
|
+
return name.trim();
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Case- and whitespace-insensitive fold of a requirement name. Requirement
|
|
7
|
+
* matching itself is case-sensitive (normalizeRequirementName); this fold
|
|
8
|
+
* exists only for typo detection - near-miss REMOVED headers and the
|
|
9
|
+
* RENAMED+REMOVED cross-section conflict - where two spellings that differ
|
|
10
|
+
* only in case or interior whitespace mean a mistake, never two requirements.
|
|
11
|
+
*/
|
|
12
|
+
export function foldRequirementName(name) {
|
|
13
|
+
return normalizeRequirementName(name).toLowerCase().replace(/\s+/g, ' ');
|
|
14
|
+
}
|
|
15
|
+
/** The canonical requirement header the delta reader recognizes. */
|
|
16
|
+
const REQUIREMENT_HEADER_REGEX = /^###\s*Requirement:\s*(.+)\s*$/i;
|
|
17
|
+
/**
|
|
18
|
+
* Extracts the Requirements section from a spec file and parses requirement blocks.
|
|
19
|
+
*/
|
|
20
|
+
export function extractRequirementsSection(content) {
|
|
21
|
+
const normalized = normalizeLineEndings(content);
|
|
22
|
+
const lines = normalized.split('\n');
|
|
23
|
+
const fenceMask = buildCodeFenceMask(lines);
|
|
24
|
+
const reqHeaderIndex = lines.findIndex((l, i) => !fenceMask[i] && /^##\s+Requirements\s*$/i.test(l));
|
|
25
|
+
if (reqHeaderIndex === -1) {
|
|
26
|
+
// No requirements section; create an empty one at the end
|
|
27
|
+
const before = content.trimEnd();
|
|
28
|
+
const headerLine = '## Requirements';
|
|
29
|
+
return {
|
|
30
|
+
before: before ? before + '\n\n' : '',
|
|
31
|
+
headerLine,
|
|
32
|
+
preamble: '',
|
|
33
|
+
bodyBlocks: [],
|
|
34
|
+
after: '\n',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Find end of this section: next line that starts with '## ' at same or higher level
|
|
38
|
+
let endIndex = lines.length;
|
|
39
|
+
for (let i = reqHeaderIndex + 1; i < lines.length; i++) {
|
|
40
|
+
if (!fenceMask[i] && /^##\s+/.test(lines[i])) {
|
|
41
|
+
endIndex = i;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const before = lines.slice(0, reqHeaderIndex).join('\n');
|
|
46
|
+
const headerLine = lines[reqHeaderIndex];
|
|
47
|
+
const sectionBodyLines = lines.slice(reqHeaderIndex + 1, endIndex);
|
|
48
|
+
const sectionBodyMask = fenceMask.slice(reqHeaderIndex + 1, endIndex);
|
|
49
|
+
const isRequirementHeader = (cursor) => !sectionBodyMask[cursor] && REQUIREMENT_HEADER_REGEX.test(sectionBodyLines[cursor]);
|
|
50
|
+
const isTopLevelHeader = (cursor) => !sectionBodyMask[cursor] && /^##\s+/.test(sectionBodyLines[cursor]);
|
|
51
|
+
// Parse requirement blocks within section body
|
|
52
|
+
const blocks = [];
|
|
53
|
+
let cursor = 0;
|
|
54
|
+
let preambleLines = [];
|
|
55
|
+
// Collect preamble lines until first requirement header
|
|
56
|
+
while (cursor < sectionBodyLines.length && !isRequirementHeader(cursor)) {
|
|
57
|
+
preambleLines.push(sectionBodyLines[cursor]);
|
|
58
|
+
cursor++;
|
|
59
|
+
}
|
|
60
|
+
while (cursor < sectionBodyLines.length) {
|
|
61
|
+
const headerLineCandidate = sectionBodyLines[cursor];
|
|
62
|
+
if (!isRequirementHeader(cursor)) {
|
|
63
|
+
// Not a requirement header; skip line defensively
|
|
64
|
+
cursor++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const headerMatch = headerLineCandidate.match(REQUIREMENT_HEADER_REGEX);
|
|
68
|
+
const name = normalizeRequirementName(headerMatch[1]);
|
|
69
|
+
cursor++;
|
|
70
|
+
// Gather lines until next requirement header or end of section
|
|
71
|
+
const bodyLines = [headerLineCandidate];
|
|
72
|
+
while (cursor < sectionBodyLines.length && !isRequirementHeader(cursor) && !isTopLevelHeader(cursor)) {
|
|
73
|
+
bodyLines.push(sectionBodyLines[cursor]);
|
|
74
|
+
cursor++;
|
|
75
|
+
}
|
|
76
|
+
const raw = bodyLines.join('\n').trimEnd();
|
|
77
|
+
blocks.push({ headerLine: headerLineCandidate, name, raw });
|
|
78
|
+
}
|
|
79
|
+
const after = lines.slice(endIndex).join('\n');
|
|
80
|
+
const preamble = preambleLines.join('\n').trimEnd();
|
|
81
|
+
return {
|
|
82
|
+
before: before.trimEnd() ? before + '\n' : before,
|
|
83
|
+
headerLine,
|
|
84
|
+
preamble,
|
|
85
|
+
bodyBlocks: blocks,
|
|
86
|
+
after: after.startsWith('\n') ? after : '\n' + after,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function normalizeLineEndings(content) {
|
|
90
|
+
// Strip a UTF-8 BOM: Windows editors and PowerShell redirects prepend one,
|
|
91
|
+
// and it would keep the first line's `## ADDED Requirements` from matching.
|
|
92
|
+
return content.replace(/^/, '').replace(/\r\n?/g, '\n');
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parse a delta-formatted spec change file content into a DeltaPlan with raw blocks.
|
|
96
|
+
*/
|
|
97
|
+
export function parseDeltaSpec(content) {
|
|
98
|
+
const normalized = normalizeLineEndings(content);
|
|
99
|
+
const lines = normalized.split('\n');
|
|
100
|
+
const fenceMask = buildCodeFenceMask(lines);
|
|
101
|
+
const sections = splitTopLevelSections(lines, fenceMask);
|
|
102
|
+
const addedLookup = getSectionsCaseInsensitive(sections, 'ADDED Requirements');
|
|
103
|
+
const modifiedLookup = getSectionsCaseInsensitive(sections, 'MODIFIED Requirements');
|
|
104
|
+
const removedLookup = getSectionsCaseInsensitive(sections, 'REMOVED Requirements');
|
|
105
|
+
const renamedLookup = getSectionsCaseInsensitive(sections, 'RENAMED Requirements');
|
|
106
|
+
const skippedHeaders = [];
|
|
107
|
+
const added = addedLookup.bodies.flatMap((body) => parseRequirementBlocksFromSection(body, {
|
|
108
|
+
section: addedLookup.title,
|
|
109
|
+
bodyStartLine: body.bodyStartLine,
|
|
110
|
+
sink: skippedHeaders,
|
|
111
|
+
}));
|
|
112
|
+
const modified = modifiedLookup.bodies.flatMap((body) => parseRequirementBlocksFromSection(body, {
|
|
113
|
+
section: modifiedLookup.title,
|
|
114
|
+
bodyStartLine: body.bodyStartLine,
|
|
115
|
+
sink: skippedHeaders,
|
|
116
|
+
}));
|
|
117
|
+
const removedNames = removedLookup.bodies.flatMap((body) => parseRemovedNames(body));
|
|
118
|
+
// Pairs are read per section, so a FROM in one copy of the header can never
|
|
119
|
+
// pair with a TO written under a different copy.
|
|
120
|
+
const renamedPairs = renamedLookup.bodies.flatMap((body) => parseRenamedPairs(body, { section: renamedLookup.title, sink: skippedHeaders }));
|
|
121
|
+
skippedHeaders.sort((a, b) => a.line - b.line);
|
|
122
|
+
return {
|
|
123
|
+
added,
|
|
124
|
+
modified,
|
|
125
|
+
removed: removedNames,
|
|
126
|
+
renamed: renamedPairs,
|
|
127
|
+
skippedHeaders,
|
|
128
|
+
sectionPresence: {
|
|
129
|
+
added: addedLookup.found,
|
|
130
|
+
modified: modifiedLookup.found,
|
|
131
|
+
removed: removedLookup.found,
|
|
132
|
+
renamed: renamedLookup.found,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Every `## ` section of a delta file, as a list rather than a title-keyed
|
|
138
|
+
* record.
|
|
139
|
+
*
|
|
140
|
+
* A title-keyed record keeps only the last body under a repeated title, so a
|
|
141
|
+
* delta that writes `## ADDED Requirements` twice would silently drop every
|
|
142
|
+
* requirement under the first copy before any validation or merge rule sees
|
|
143
|
+
* it. A list keeps each occurrence in document order; `getSectionsCaseInsensitive`
|
|
144
|
+
* below merges the ones that share a title.
|
|
145
|
+
*/
|
|
146
|
+
function splitTopLevelSections(lines, fenceMask) {
|
|
147
|
+
const sections = [];
|
|
148
|
+
const indices = [];
|
|
149
|
+
for (let i = 0; i < lines.length; i++) {
|
|
150
|
+
if (fenceMask[i])
|
|
151
|
+
continue;
|
|
152
|
+
const m = lines[i].match(/^(##)\s+(.+)$/);
|
|
153
|
+
if (m) {
|
|
154
|
+
indices.push({ title: m[2].trim(), index: i });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (let i = 0; i < indices.length; i++) {
|
|
158
|
+
const current = indices[i];
|
|
159
|
+
const next = indices[i + 1];
|
|
160
|
+
const end = next ? next.index : lines.length;
|
|
161
|
+
sections.push({
|
|
162
|
+
title: current.title,
|
|
163
|
+
body: {
|
|
164
|
+
lines: lines.slice(current.index + 1, end),
|
|
165
|
+
fenceMask: fenceMask.slice(current.index + 1, end),
|
|
166
|
+
bodyStartLine: current.index + 2,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return sections;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Every section body whose title folds to `desired`, in document order.
|
|
174
|
+
*
|
|
175
|
+
* Returning all matches - not just the first - is what lets a repeated header
|
|
176
|
+
* (`## ADDED Requirements` twice) or a case variant (`## ADDED Requirements`
|
|
177
|
+
* next to `## Added Requirements`) apply in full: each body keeps its own
|
|
178
|
+
* `bodyStartLine`, so a diagnostic points at the copy the header actually came
|
|
179
|
+
* from. `title` is the first spelling the author used, which diagnostics quote.
|
|
180
|
+
*/
|
|
181
|
+
function getSectionsCaseInsensitive(sections, desired) {
|
|
182
|
+
const target = desired.toLowerCase();
|
|
183
|
+
const matches = sections.filter((section) => section.title.toLowerCase() === target);
|
|
184
|
+
if (matches.length === 0) {
|
|
185
|
+
return { title: desired, bodies: [], found: false };
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
title: matches[0].title,
|
|
189
|
+
bodies: matches.map((section) => section.body),
|
|
190
|
+
found: true,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function parseRequirementBlocksFromSection(sectionBody, skipped) {
|
|
194
|
+
const { lines, fenceMask } = sectionBody;
|
|
195
|
+
if (lines.length === 0)
|
|
196
|
+
return [];
|
|
197
|
+
const isRequirementHeader = (i) => !fenceMask[i] && REQUIREMENT_HEADER_REGEX.test(lines[i]);
|
|
198
|
+
const isTopLevelHeader = (i) => !fenceMask[i] && /^##\s+/.test(lines[i]);
|
|
199
|
+
const recordIfSkippedHeader = (index) => {
|
|
200
|
+
if (!skipped || fenceMask[index])
|
|
201
|
+
return;
|
|
202
|
+
const h3 = lines[index].match(/^###\s+(.+?)\s*$/);
|
|
203
|
+
if (h3 && !REQUIREMENT_HEADER_REGEX.test(lines[index])) {
|
|
204
|
+
skipped.sink.push({
|
|
205
|
+
header: h3[1].trim(),
|
|
206
|
+
section: skipped.section,
|
|
207
|
+
line: skipped.bodyStartLine + index,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const blocks = [];
|
|
212
|
+
let i = 0;
|
|
213
|
+
while (i < lines.length) {
|
|
214
|
+
// Seek next requirement header
|
|
215
|
+
while (i < lines.length && !isRequirementHeader(i)) {
|
|
216
|
+
recordIfSkippedHeader(i);
|
|
217
|
+
i++;
|
|
218
|
+
}
|
|
219
|
+
if (i >= lines.length)
|
|
220
|
+
break;
|
|
221
|
+
const headerLine = lines[i];
|
|
222
|
+
const m = headerLine.match(REQUIREMENT_HEADER_REGEX);
|
|
223
|
+
if (!m) {
|
|
224
|
+
i++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const name = normalizeRequirementName(m[1]);
|
|
228
|
+
const buf = [headerLine];
|
|
229
|
+
i++;
|
|
230
|
+
while (i < lines.length && !isRequirementHeader(i) && !isTopLevelHeader(i)) {
|
|
231
|
+
recordIfSkippedHeader(i);
|
|
232
|
+
buf.push(lines[i]);
|
|
233
|
+
i++;
|
|
234
|
+
}
|
|
235
|
+
blocks.push({ headerLine, name, raw: buf.join('\n').trimEnd() });
|
|
236
|
+
}
|
|
237
|
+
return blocks;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Requirement names listed in `## REMOVED Requirements`, in document order.
|
|
241
|
+
*
|
|
242
|
+
* Two spellings are accepted: a plain `### Requirement:` header, and a bullet
|
|
243
|
+
* carrying one. Every CommonMark bullet marker (`-`, `*`, `+`) counts for the
|
|
244
|
+
* bullet form: accepting only `-` turns a removal written with `*` or `+`
|
|
245
|
+
* into a silent no-op - archive reports success while the requirement stays
|
|
246
|
+
* in the spec.
|
|
247
|
+
*/
|
|
248
|
+
function parseRemovedNames(sectionBody) {
|
|
249
|
+
const { lines, fenceMask } = sectionBody;
|
|
250
|
+
if (lines.length === 0)
|
|
251
|
+
return [];
|
|
252
|
+
const names = [];
|
|
253
|
+
for (let i = 0; i < lines.length; i++) {
|
|
254
|
+
if (fenceMask[i])
|
|
255
|
+
continue;
|
|
256
|
+
const line = lines[i];
|
|
257
|
+
const m = line.match(REQUIREMENT_HEADER_REGEX);
|
|
258
|
+
if (m) {
|
|
259
|
+
names.push(normalizeRequirementName(m[1]));
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
// Also support bullet list of headers
|
|
263
|
+
const bullet = line.match(/^\s*[-*+]\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
|
|
264
|
+
if (bullet) {
|
|
265
|
+
names.push(normalizeRequirementName(bullet[1]));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return names;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* `FROM:`/`TO:` rename pairs from `## RENAMED Requirements`, in document
|
|
272
|
+
* order.
|
|
273
|
+
*
|
|
274
|
+
* The leading bullet is optional, and every CommonMark bullet marker (`-`,
|
|
275
|
+
* `*`, `+`) is accepted: accepting only `-` turns a rename written with `*`
|
|
276
|
+
* or `+` into a silent no-op - archive reports success while the rename never
|
|
277
|
+
* happens.
|
|
278
|
+
*/
|
|
279
|
+
function parseRenamedPairs(sectionBody, skipped) {
|
|
280
|
+
const { lines, fenceMask, bodyStartLine } = sectionBody;
|
|
281
|
+
if (lines.length === 0)
|
|
282
|
+
return [];
|
|
283
|
+
const pairs = [];
|
|
284
|
+
let current = {};
|
|
285
|
+
const flushUnpairedFrom = () => {
|
|
286
|
+
if (current.from === undefined || !skipped)
|
|
287
|
+
return;
|
|
288
|
+
skipped.sink.push({
|
|
289
|
+
header: `${current.from} (FROM without a matching TO)`,
|
|
290
|
+
section: skipped.section,
|
|
291
|
+
line: bodyStartLine + current.fromLine,
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
for (let i = 0; i < lines.length; i++) {
|
|
295
|
+
if (fenceMask[i])
|
|
296
|
+
continue;
|
|
297
|
+
const line = lines[i];
|
|
298
|
+
const fromMatch = line.match(/^\s*[-*+]?\s*FROM:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
|
|
299
|
+
const toMatch = line.match(/^\s*[-*+]?\s*TO:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
|
|
300
|
+
if (fromMatch) {
|
|
301
|
+
// A prior FROM that never got its TO is about to be overwritten and
|
|
302
|
+
// discarded - the only chance to name it before it is gone.
|
|
303
|
+
flushUnpairedFrom();
|
|
304
|
+
current.from = normalizeRequirementName(fromMatch[1]);
|
|
305
|
+
current.fromLine = i;
|
|
306
|
+
}
|
|
307
|
+
else if (toMatch) {
|
|
308
|
+
current.to = normalizeRequirementName(toMatch[1]);
|
|
309
|
+
if (current.from && current.to) {
|
|
310
|
+
pairs.push({ from: current.from, to: current.to });
|
|
311
|
+
current = {};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
flushUnpairedFrom();
|
|
316
|
+
return pairs;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Scenario names the current requirement block has and the incoming
|
|
320
|
+
* (MODIFIED) block does not. A MODIFIED requirement replaces the whole block,
|
|
321
|
+
* so every name reported here would be dropped from the main spec.
|
|
322
|
+
*
|
|
323
|
+
* Shared by archive (which refuses to apply the block) and validate (which
|
|
324
|
+
* reports the same loss at authoring time, #1477), so the two cannot disagree
|
|
325
|
+
* about what counts as a dropped scenario.
|
|
326
|
+
*/
|
|
327
|
+
export function findMissingCurrentScenarios(current, incoming) {
|
|
328
|
+
// Multiplicity-aware: a name present N times in current and M times in
|
|
329
|
+
// incoming means max(0, N - M) instances are missing. Set membership would
|
|
330
|
+
// treat N>M as fully covered and let archive silently drop duplicates
|
|
331
|
+
// (residual #1246 / duplicate-scenario-name blind spot).
|
|
332
|
+
const remainingIncoming = new Map();
|
|
333
|
+
for (const scenario of parseScenarioBlocks(incoming.raw)) {
|
|
334
|
+
const name = scenario.name;
|
|
335
|
+
remainingIncoming.set(name, (remainingIncoming.get(name) ?? 0) + 1);
|
|
336
|
+
}
|
|
337
|
+
const missing = [];
|
|
338
|
+
for (const scenario of parseScenarioBlocks(current.raw)) {
|
|
339
|
+
const name = scenario.name;
|
|
340
|
+
const remaining = remainingIncoming.get(name) ?? 0;
|
|
341
|
+
if (remaining > 0) {
|
|
342
|
+
remainingIncoming.set(name, remaining - 1);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
missing.push(name);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return missing;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Any non-fenced level-4 header on the given (masked) line. Reuses the spec
|
|
352
|
+
* path's SCENARIO_HEADER so the two counters cannot drift apart.
|
|
353
|
+
*/
|
|
354
|
+
function scenarioHeaderAt(lines, mask, index) {
|
|
355
|
+
return !mask[index] && SCENARIO_HEADER.test(lines[index]);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* The scenario name for a `#### ` header, matching the label the author reads:
|
|
359
|
+
* the header text with the leading `####`, an optional CommonMark closing `#`
|
|
360
|
+
* run (`#### Foo ####` renders as `Foo`), and an optional `Scenario:` prefix
|
|
361
|
+
* stripped. Both the current and incoming blocks run through here, so the
|
|
362
|
+
* comparison in findMissingCurrentScenarios stays internally consistent
|
|
363
|
+
* regardless of label — and two headers that render to the same title (one
|
|
364
|
+
* ATX-closed, one not) are not mistaken for a dropped scenario.
|
|
365
|
+
*/
|
|
366
|
+
function scenarioNameAt(line) {
|
|
367
|
+
return line
|
|
368
|
+
.replace(SCENARIO_HEADER, '')
|
|
369
|
+
// Optional ATX closing sequence. CommonMark only treats a trailing `#` run
|
|
370
|
+
// as a close when it is preceded by a space or tab — not any Unicode space —
|
|
371
|
+
// so this uses `[ \t]`, not `\s`. A looser `\s` could strip a `#` run after
|
|
372
|
+
// an exotic space (e.g. NBSP) that CommonMark keeps, folding two distinct
|
|
373
|
+
// scenario names into one and masking a real loss. `[ \t]` keeps the fold
|
|
374
|
+
// faithful to how the header actually renders.
|
|
375
|
+
.replace(/[ \t]+#+[ \t]*$/, '')
|
|
376
|
+
.replace(/^Scenario:\s*/i, '')
|
|
377
|
+
.trim();
|
|
378
|
+
}
|
|
379
|
+
function parseScenarioBlocks(requirementRaw) {
|
|
380
|
+
const lines = requirementRaw.replace(/\r\n?/g, '\n').split('\n');
|
|
381
|
+
// A scenario is ANY non-fenced `#### ` header, matching the spec path's
|
|
382
|
+
// SCENARIO_HEADER / countScenarios (requirement-text.ts) exactly — not only
|
|
383
|
+
// `#### Scenario:`. The two MUST agree: a level-4 child whose header is not
|
|
384
|
+
// literally `Scenario:` (e.g. `#### Edge case`) is still a scenario the spec
|
|
385
|
+
// path counts, so a MODIFIED block that drops it would otherwise slip past
|
|
386
|
+
// this loss check and be deleted by archive with no error (the parity the
|
|
387
|
+
// SCENARIO_HEADER comment warns not to break). A `####` inside a fenced
|
|
388
|
+
// example is masked out, matching countScenarios.
|
|
389
|
+
const mask = buildCodeFenceMask(lines);
|
|
390
|
+
const scenarios = [];
|
|
391
|
+
let index = 0;
|
|
392
|
+
while (index < lines.length) {
|
|
393
|
+
if (!scenarioHeaderAt(lines, mask, index)) {
|
|
394
|
+
index++;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
const start = index;
|
|
398
|
+
const name = scenarioNameAt(lines[index]);
|
|
399
|
+
index++;
|
|
400
|
+
while (index < lines.length && !scenarioHeaderAt(lines, mask, index)) {
|
|
401
|
+
index++;
|
|
402
|
+
}
|
|
403
|
+
scenarios.push({
|
|
404
|
+
name,
|
|
405
|
+
raw: lines.slice(start, index).join('\n').trimEnd(),
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
return scenarios;
|
|
409
|
+
}
|
package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, fence-aware requirement-reading helpers.
|
|
3
|
+
*
|
|
4
|
+
* The requirement reader used to be implemented twice — once for main specs
|
|
5
|
+
* (`MarkdownParser.parseRequirements`) and once for change deltas
|
|
6
|
+
* (`Validator.extractRequirementText` / `countScenarios`) — and the two drifted
|
|
7
|
+
* apart. These helpers are the single source of truth for requirement-body
|
|
8
|
+
* extraction, scenario counting, and `SHALL`/`MUST` detection in
|
|
9
|
+
* `validate <change>`, `validate <spec>`, and `archive`.
|
|
10
|
+
*/
|
|
11
|
+
export { buildCodeFenceMask } from './code-fence.js';
|
|
12
|
+
/**
|
|
13
|
+
* A level-4 header. Deliberately matches ANY `####` header, not only
|
|
14
|
+
* `#### Scenario:` — the spec path treats every level-4 child of a requirement
|
|
15
|
+
* as a scenario, so the delta counter must too (parity). The delta/loss path
|
|
16
|
+
* reuses this exact constant via `scenarioHeaderAt` in requirement-blocks.ts;
|
|
17
|
+
* keep both paths on it rather than reintroducing a separate `Scenario:` regex.
|
|
18
|
+
*/
|
|
19
|
+
export declare const SCENARIO_HEADER: RegExp;
|
|
20
|
+
/**
|
|
21
|
+
* The one predicate for normative-keyword detection. Matches `SHALL` or `MUST`
|
|
22
|
+
* as whole words so the change-delta reader and the schema-based reader accept
|
|
23
|
+
* and reject identical text.
|
|
24
|
+
*/
|
|
25
|
+
export declare function containsShallOrMust(text: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Extract the full requirement body from the lines that follow a
|
|
28
|
+
* `### Requirement:` header (the lines may include scenarios and fenced code).
|
|
29
|
+
*
|
|
30
|
+
* Captures every body line from the start up to the first header found on a
|
|
31
|
+
* non-fenced line — usually the first `#### Scenario:`, but also a stray `###`
|
|
32
|
+
* divider the delta reader absorbed into the block — skipping blank lines and
|
|
33
|
+
* any line inside a fenced code block. `**metadata**:` lines are skipped only
|
|
34
|
+
* when other body text remains: a requirement written entirely as
|
|
35
|
+
* `**Constraint**: The system MUST ...` keeps that line as its body. Captured
|
|
36
|
+
* lines are trimmed and joined with newlines so a requirement whose text wraps
|
|
37
|
+
* across lines — or whose `SHALL`/`MUST` lands on a later line — is read in
|
|
38
|
+
* full.
|
|
39
|
+
*/
|
|
40
|
+
export declare function extractRequirementBody(bodyLines: string[]): string;
|
|
41
|
+
/**
|
|
42
|
+
* Parser/display fallback for a requirement block with no body text. This is
|
|
43
|
+
* what lets a bare `### The system SHALL ...` header remain readable on the
|
|
44
|
+
* spec path (the title is the requirement). Validator body-keyword checks for
|
|
45
|
+
* canonical `### Requirement:` blocks use `extractRequirementBody` directly so
|
|
46
|
+
* a keyword that appears only in the header still receives the #1156/#1280
|
|
47
|
+
* body-keyword hint.
|
|
48
|
+
*/
|
|
49
|
+
export declare function extractRequirementText(headerTitle: string, bodyLines: string[]): string;
|
|
50
|
+
/**
|
|
51
|
+
* Count the real scenarios in a requirement block: `#### ` headers on non-fenced
|
|
52
|
+
* lines. A `#### Scenario:` that lives inside a fenced example is not a real
|
|
53
|
+
* scenario and is not counted.
|
|
54
|
+
*/
|
|
55
|
+
export declare function countScenarios(bodyLines: string[]): number;
|
package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, fence-aware requirement-reading helpers.
|
|
3
|
+
*
|
|
4
|
+
* The requirement reader used to be implemented twice — once for main specs
|
|
5
|
+
* (`MarkdownParser.parseRequirements`) and once for change deltas
|
|
6
|
+
* (`Validator.extractRequirementText` / `countScenarios`) — and the two drifted
|
|
7
|
+
* apart. These helpers are the single source of truth for requirement-body
|
|
8
|
+
* extraction, scenario counting, and `SHALL`/`MUST` detection in
|
|
9
|
+
* `validate <change>`, `validate <spec>`, and `archive`.
|
|
10
|
+
*/
|
|
11
|
+
// Re-exported so existing importers keep working; the single implementation
|
|
12
|
+
// lives in code-fence.ts.
|
|
13
|
+
export { buildCodeFenceMask } from './code-fence.js';
|
|
14
|
+
import { buildCodeFenceMask } from './code-fence.js';
|
|
15
|
+
/** Lines that look like `**ID**: ...` / `**Priority**: ...` metadata. */
|
|
16
|
+
const METADATA_LINE = /^\*\*[^*]+\*\*:/;
|
|
17
|
+
/** Any markdown header line — the boundary where a requirement body ends. */
|
|
18
|
+
const HEADER_LINE = /^#{1,6}\s/;
|
|
19
|
+
/**
|
|
20
|
+
* A level-4 header. Deliberately matches ANY `####` header, not only
|
|
21
|
+
* `#### Scenario:` — the spec path treats every level-4 child of a requirement
|
|
22
|
+
* as a scenario, so the delta counter must too (parity). The delta/loss path
|
|
23
|
+
* reuses this exact constant via `scenarioHeaderAt` in requirement-blocks.ts;
|
|
24
|
+
* keep both paths on it rather than reintroducing a separate `Scenario:` regex.
|
|
25
|
+
*/
|
|
26
|
+
export const SCENARIO_HEADER = /^####\s+/;
|
|
27
|
+
/**
|
|
28
|
+
* The one predicate for normative-keyword detection. Matches `SHALL` or `MUST`
|
|
29
|
+
* as whole words so the change-delta reader and the schema-based reader accept
|
|
30
|
+
* and reject identical text.
|
|
31
|
+
*/
|
|
32
|
+
export function containsShallOrMust(text) {
|
|
33
|
+
return /\b(SHALL|MUST)\b/.test(text);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract the full requirement body from the lines that follow a
|
|
37
|
+
* `### Requirement:` header (the lines may include scenarios and fenced code).
|
|
38
|
+
*
|
|
39
|
+
* Captures every body line from the start up to the first header found on a
|
|
40
|
+
* non-fenced line — usually the first `#### Scenario:`, but also a stray `###`
|
|
41
|
+
* divider the delta reader absorbed into the block — skipping blank lines and
|
|
42
|
+
* any line inside a fenced code block. `**metadata**:` lines are skipped only
|
|
43
|
+
* when other body text remains: a requirement written entirely as
|
|
44
|
+
* `**Constraint**: The system MUST ...` keeps that line as its body. Captured
|
|
45
|
+
* lines are trimmed and joined with newlines so a requirement whose text wraps
|
|
46
|
+
* across lines — or whose `SHALL`/`MUST` lands on a later line — is read in
|
|
47
|
+
* full.
|
|
48
|
+
*/
|
|
49
|
+
export function extractRequirementBody(bodyLines) {
|
|
50
|
+
const mask = buildCodeFenceMask(bodyLines);
|
|
51
|
+
const captured = [];
|
|
52
|
+
const metadata = [];
|
|
53
|
+
for (let i = 0; i < bodyLines.length; i++) {
|
|
54
|
+
if (mask[i])
|
|
55
|
+
continue; // inside a fenced code block
|
|
56
|
+
const line = bodyLines[i];
|
|
57
|
+
if (HEADER_LINE.test(line))
|
|
58
|
+
break; // first scenario or stray divider
|
|
59
|
+
const trimmed = line.trim();
|
|
60
|
+
if (trimmed.length === 0)
|
|
61
|
+
continue; // blank
|
|
62
|
+
if (METADATA_LINE.test(trimmed)) {
|
|
63
|
+
metadata.push(trimmed); // **ID**: / **Priority**: ...
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
captured.push(trimmed);
|
|
67
|
+
}
|
|
68
|
+
if (captured.length > 0)
|
|
69
|
+
return captured.join('\n');
|
|
70
|
+
return metadata.join('\n'); // metadata-only body: the metadata IS the body
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parser/display fallback for a requirement block with no body text. This is
|
|
74
|
+
* what lets a bare `### The system SHALL ...` header remain readable on the
|
|
75
|
+
* spec path (the title is the requirement). Validator body-keyword checks for
|
|
76
|
+
* canonical `### Requirement:` blocks use `extractRequirementBody` directly so
|
|
77
|
+
* a keyword that appears only in the header still receives the #1156/#1280
|
|
78
|
+
* body-keyword hint.
|
|
79
|
+
*/
|
|
80
|
+
export function extractRequirementText(headerTitle, bodyLines) {
|
|
81
|
+
return extractRequirementBody(bodyLines) || headerTitle.trim();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Count the real scenarios in a requirement block: `#### ` headers on non-fenced
|
|
85
|
+
* lines. A `#### Scenario:` that lives inside a fenced example is not a real
|
|
86
|
+
* scenario and is not counted.
|
|
87
|
+
*/
|
|
88
|
+
export function countScenarios(bodyLines) {
|
|
89
|
+
const mask = buildCodeFenceMask(bodyLines);
|
|
90
|
+
let count = 0;
|
|
91
|
+
for (let i = 0; i < bodyLines.length; i++) {
|
|
92
|
+
if (mask[i])
|
|
93
|
+
continue;
|
|
94
|
+
if (SCENARIO_HEADER.test(bodyLines[i]))
|
|
95
|
+
count++;
|
|
96
|
+
}
|
|
97
|
+
return count;
|
|
98
|
+
}
|
package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface MainSpecStructureIssue {
|
|
2
|
+
kind: 'delta-header' | 'requirement-outside-requirements' | 'duplicate-requirement';
|
|
3
|
+
line: number;
|
|
4
|
+
header: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function findMainSpecStructureIssues(content: string): MainSpecStructureIssue[];
|
|
8
|
+
export declare function stripFencedCodeBlocksPreservingLines(content: string): string;
|