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,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a path contains glob pattern characters.
|
|
3
|
+
*/
|
|
4
|
+
export declare function isGlobPattern(pattern: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Resolves an artifact's `generates` value to an absolute path under the change
|
|
7
|
+
* directory, asserting the result stays confined below it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveArtifactOutputPath(changeDir: string, generates: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Resolves an artifact's output path(s) to concrete files that currently exist.
|
|
12
|
+
* Returns absolute file paths. Glob matches are sorted for deterministic output.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveArtifactOutputs(changeDir: string, generates: string): string[];
|
|
15
|
+
/**
|
|
16
|
+
* Checks if an artifact has at least one resolved output file.
|
|
17
|
+
*/
|
|
18
|
+
export declare function artifactOutputExists(changeDir: string, generates: string): boolean;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// RECONSTRUCTED (store-free, dependency-free) from
|
|
2
|
+
// @fission-ai/openspec@1.8.0 src/core/artifact-graph/outputs.ts
|
|
3
|
+
//
|
|
4
|
+
// The upstream module resolves an artifact's `generates` path (a literal path
|
|
5
|
+
// or a glob) to concrete files under the change directory, using `fast-glob`
|
|
6
|
+
// plus a hardened directory-traversal guard. To keep this slice free of the
|
|
7
|
+
// `fast-glob` dependency we reimplement the same PUBLIC surface
|
|
8
|
+
// (isGlobPattern / resolveArtifactOutputPath / resolveArtifactOutputs /
|
|
9
|
+
// artifactOutputExists) with a small internal glob matcher that covers the
|
|
10
|
+
// patterns OpenSpec schemas actually use (`proposal.md`, `specs/**/*.md`, ...).
|
|
11
|
+
// Path confinement below the change directory is preserved via
|
|
12
|
+
// FileSystemUtils.assertPathWithin.
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import { FileSystemUtils } from './file-system.js';
|
|
16
|
+
/**
|
|
17
|
+
* Checks if a path contains glob pattern characters.
|
|
18
|
+
*/
|
|
19
|
+
export function isGlobPattern(pattern) {
|
|
20
|
+
return pattern.includes('*') || pattern.includes('?') || pattern.includes('[');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolves an artifact's `generates` value to an absolute path under the change
|
|
24
|
+
* directory, asserting the result stays confined below it.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveArtifactOutputPath(changeDir, generates) {
|
|
27
|
+
const outputPath = path.join(changeDir, generates);
|
|
28
|
+
FileSystemUtils.assertPathWithin(changeDir, outputPath);
|
|
29
|
+
return outputPath;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compiles a POSIX-style glob (relative, `/`-separated) into an anchored
|
|
33
|
+
* regular expression. Supports `**` (any number of path segments, including
|
|
34
|
+
* zero), `*` (any run of non-separator chars), `?` (a single non-separator
|
|
35
|
+
* char), and literal text. This is intentionally minimal: it matches the
|
|
36
|
+
* `generates` patterns OpenSpec schemas emit, not the full glob grammar.
|
|
37
|
+
*/
|
|
38
|
+
function globToRegExp(pattern) {
|
|
39
|
+
let re = '';
|
|
40
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
41
|
+
const ch = pattern[i];
|
|
42
|
+
if (ch === '*') {
|
|
43
|
+
if (pattern[i + 1] === '*') {
|
|
44
|
+
// `**` (optionally followed by `/`) matches across path separators.
|
|
45
|
+
i++;
|
|
46
|
+
if (pattern[i + 1] === '/') {
|
|
47
|
+
i++;
|
|
48
|
+
re += '(?:.*/)?';
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
re += '.*';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
re += '[^/]*';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (ch === '?') {
|
|
59
|
+
re += '[^/]';
|
|
60
|
+
}
|
|
61
|
+
else if ('\\^$.|+()[]{}'.includes(ch)) {
|
|
62
|
+
re += '\\' + ch;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
re += ch;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return new RegExp('^' + re + '$');
|
|
69
|
+
}
|
|
70
|
+
/** Recursively lists every file below `dir`, returned as absolute paths. */
|
|
71
|
+
function walkFiles(dir) {
|
|
72
|
+
const out = [];
|
|
73
|
+
let entries;
|
|
74
|
+
try {
|
|
75
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const full = path.join(dir, entry.name);
|
|
82
|
+
if (entry.isDirectory()) {
|
|
83
|
+
out.push(...walkFiles(full));
|
|
84
|
+
}
|
|
85
|
+
else if (entry.isFile()) {
|
|
86
|
+
out.push(full);
|
|
87
|
+
}
|
|
88
|
+
else if (entry.isSymbolicLink()) {
|
|
89
|
+
try {
|
|
90
|
+
const stat = fs.statSync(full);
|
|
91
|
+
if (stat.isFile())
|
|
92
|
+
out.push(full);
|
|
93
|
+
else if (stat.isDirectory())
|
|
94
|
+
out.push(...walkFiles(full));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Broken symlink — skip.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Resolves an artifact's output path(s) to concrete files that currently exist.
|
|
105
|
+
* Returns absolute file paths. Glob matches are sorted for deterministic output.
|
|
106
|
+
*/
|
|
107
|
+
export function resolveArtifactOutputs(changeDir, generates) {
|
|
108
|
+
const outputPath = resolveArtifactOutputPath(changeDir, generates);
|
|
109
|
+
if (!isGlobPattern(generates)) {
|
|
110
|
+
try {
|
|
111
|
+
return fs.statSync(outputPath).isFile() ? [outputPath] : [];
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const regex = globToRegExp(FileSystemUtils.toPosixPath(generates));
|
|
118
|
+
const matches = [];
|
|
119
|
+
for (const file of walkFiles(changeDir)) {
|
|
120
|
+
const rel = FileSystemUtils.toPosixPath(path.relative(changeDir, file));
|
|
121
|
+
if (!regex.test(rel))
|
|
122
|
+
continue;
|
|
123
|
+
try {
|
|
124
|
+
FileSystemUtils.assertPathWithin(changeDir, file);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
matches.push(file);
|
|
130
|
+
}
|
|
131
|
+
return Array.from(new Set(matches)).sort();
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Checks if an artifact has at least one resolved output file.
|
|
135
|
+
*/
|
|
136
|
+
export function artifactOutputExists(changeDir, generates) {
|
|
137
|
+
return resolveArtifactOutputs(changeDir, generates).length > 0;
|
|
138
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import type { SchemaYaml } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Error thrown when loading a schema fails.
|
|
5
|
+
*/
|
|
6
|
+
export declare class SchemaLoadError extends Error {
|
|
7
|
+
readonly schemaPath: string;
|
|
8
|
+
readonly cause?: Error | undefined;
|
|
9
|
+
constructor(message: string, schemaPath: string, cause?: Error | undefined);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Gets the package's built-in schemas directory path.
|
|
13
|
+
* Uses import.meta.url to resolve relative to the current module.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPackageSchemasDir(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Gets the user's schema override directory path.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getUserSchemasDir(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Gets the project-local schemas directory path.
|
|
22
|
+
* @param projectRoot - The project root directory
|
|
23
|
+
* @returns The path to the project's schemas directory
|
|
24
|
+
*/
|
|
25
|
+
export declare function getProjectSchemasDir(projectRoot: string): string;
|
|
26
|
+
export declare function isSchemaDir(parentDir: string, entry: fs.Dirent): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves a schema name to its directory path.
|
|
29
|
+
*
|
|
30
|
+
* Resolution order (when projectRoot is provided):
|
|
31
|
+
* 1. Project-local: <projectRoot>/openspec/schemas/<name>/schema.yaml
|
|
32
|
+
* 2. User override: ${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml
|
|
33
|
+
* 3. Package built-in: <package>/schemas/<name>/schema.yaml
|
|
34
|
+
*
|
|
35
|
+
* When projectRoot is not provided, only user override and package built-in are checked
|
|
36
|
+
* (backward compatible behavior).
|
|
37
|
+
*
|
|
38
|
+
* @param name - Schema name (e.g., "spec-driven")
|
|
39
|
+
* @param projectRoot - Optional project root directory for project-local schema resolution
|
|
40
|
+
* @returns The path to the schema directory, or null if not found
|
|
41
|
+
*/
|
|
42
|
+
export declare function getSchemaDir(name: string, projectRoot?: string): string | null;
|
|
43
|
+
/**
|
|
44
|
+
* Resolves a schema name to a SchemaYaml object.
|
|
45
|
+
*
|
|
46
|
+
* Resolution order (when projectRoot is provided):
|
|
47
|
+
* 1. Project-local: <projectRoot>/openspec/schemas/<name>/schema.yaml
|
|
48
|
+
* 2. User override: ${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml
|
|
49
|
+
* 3. Package built-in: <package>/schemas/<name>/schema.yaml
|
|
50
|
+
*
|
|
51
|
+
* When projectRoot is not provided, only user override and package built-in are checked
|
|
52
|
+
* (backward compatible behavior).
|
|
53
|
+
*
|
|
54
|
+
* @param name - Schema name (e.g., "spec-driven")
|
|
55
|
+
* @param projectRoot - Optional project root directory for project-local schema resolution
|
|
56
|
+
* @returns The resolved schema object
|
|
57
|
+
* @throws Error if schema is not found in any location
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveSchema(name: string, projectRoot?: string): SchemaYaml;
|
|
60
|
+
/**
|
|
61
|
+
* Lists all available schema names.
|
|
62
|
+
* Combines project-local, user override, and package built-in schemas.
|
|
63
|
+
*
|
|
64
|
+
* @param projectRoot - Optional project root directory for project-local schema resolution
|
|
65
|
+
*/
|
|
66
|
+
export declare function listSchemas(projectRoot?: string): string[];
|
|
67
|
+
/**
|
|
68
|
+
* Schema info with metadata (name, description, artifacts).
|
|
69
|
+
*/
|
|
70
|
+
export interface SchemaInfo {
|
|
71
|
+
name: string;
|
|
72
|
+
description: string;
|
|
73
|
+
artifacts: string[];
|
|
74
|
+
source: 'project' | 'user' | 'package';
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Lists all available schemas with their descriptions and artifact lists.
|
|
78
|
+
* Useful for agent skills to present schema selection to users.
|
|
79
|
+
*
|
|
80
|
+
* @param projectRoot - Optional project root directory for project-local schema resolution
|
|
81
|
+
*/
|
|
82
|
+
export declare function listSchemasWithInfo(projectRoot?: string): SchemaInfo[];
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// Vendored from @fission-ai/openspec@1.8.0
|
|
2
|
+
// src/core/artifact-graph/resolver.ts
|
|
3
|
+
// ONLY change vs upstream: import paths rewritten so the slice resolves within
|
|
4
|
+
// this vendored engine directory:
|
|
5
|
+
// ../global-config.js -> ./global-config.js (store-free replacement)
|
|
6
|
+
// ../../utils/file-system.js -> ./file-system.js
|
|
7
|
+
// Behaviour is otherwise unchanged. Note: getPackageSchemasDir points at a
|
|
8
|
+
// <package>/schemas dir that this slice does not ship; the existsSync guards in
|
|
9
|
+
// resolveSchema/listSchemas make a missing package-schemas dir a no-op.
|
|
10
|
+
import * as fs from 'node:fs';
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { getGlobalDataDir } from './global-config.js';
|
|
14
|
+
import { FileSystemUtils } from './file-system.js';
|
|
15
|
+
import { parseSchema, SchemaValidationError } from './schema.js';
|
|
16
|
+
/**
|
|
17
|
+
* Error thrown when loading a schema fails.
|
|
18
|
+
*/
|
|
19
|
+
export class SchemaLoadError extends Error {
|
|
20
|
+
schemaPath;
|
|
21
|
+
cause;
|
|
22
|
+
constructor(message, schemaPath, cause) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.schemaPath = schemaPath;
|
|
25
|
+
this.cause = cause;
|
|
26
|
+
this.name = 'SchemaLoadError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Gets the package's built-in schemas directory path.
|
|
31
|
+
* Uses import.meta.url to resolve relative to the current module.
|
|
32
|
+
*/
|
|
33
|
+
export function getPackageSchemasDir() {
|
|
34
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
35
|
+
// Navigate from dist/core/artifact-graph/ to package root's schemas/
|
|
36
|
+
return path.join(path.dirname(currentFile), '..', '..', '..', 'schemas');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Gets the user's schema override directory path.
|
|
40
|
+
*/
|
|
41
|
+
export function getUserSchemasDir() {
|
|
42
|
+
return path.join(getGlobalDataDir(), 'schemas');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Gets the project-local schemas directory path.
|
|
46
|
+
* @param projectRoot - The project root directory
|
|
47
|
+
* @returns The path to the project's schemas directory
|
|
48
|
+
*/
|
|
49
|
+
export function getProjectSchemasDir(projectRoot) {
|
|
50
|
+
return path.join(projectRoot, 'openspec', 'schemas');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Determines whether a directory entry represents a schema directory candidate.
|
|
54
|
+
*
|
|
55
|
+
* Returns true for real directories and for symlinks whose target is a
|
|
56
|
+
* directory. `fs.Dirent.isDirectory()` reports the raw entry type, so a symlink
|
|
57
|
+
* (even one pointing at a directory) has `isDirectory() === false`; we
|
|
58
|
+
* dereference such entries via `fs.statSync` to admit symlinked schema dirs
|
|
59
|
+
* while still rejecting symlinks-to-files and broken/dangling symlinks.
|
|
60
|
+
*
|
|
61
|
+
* @param parentDir - The directory containing the entry
|
|
62
|
+
* @param entry - The directory entry from `fs.readdirSync(..., { withFileTypes: true })`
|
|
63
|
+
*/
|
|
64
|
+
/**
|
|
65
|
+
* Directories `schema fork` creates transiently while swapping a fork into
|
|
66
|
+
* place: a staging copy (`.fork-staging-<rand>`, created via mkdtemp) and a
|
|
67
|
+
* backup of the previous destination (`<name>.fork-backup-<pid>-<ts>`). Either
|
|
68
|
+
* can briefly coexist with real schemas in the schemas dir, so discovery must
|
|
69
|
+
* never surface them. Real schema names are kebab-case (no dots), so excluding
|
|
70
|
+
* these dot-bearing temp names can never hide a legitimate schema.
|
|
71
|
+
*/
|
|
72
|
+
function isOwnedForkTempDir(name) {
|
|
73
|
+
return name.startsWith('.fork-staging-') || name.includes('.fork-backup-');
|
|
74
|
+
}
|
|
75
|
+
export function isSchemaDir(parentDir, entry) {
|
|
76
|
+
if (isOwnedForkTempDir(entry.name)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
if (entry.isDirectory()) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (entry.isSymbolicLink()) {
|
|
83
|
+
try {
|
|
84
|
+
// statSync follows the link; isDirectory() reflects the target type.
|
|
85
|
+
return fs.statSync(path.join(parentDir, entry.name)).isDirectory();
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Broken symlink (dangling target) — statSync throws; treat as non-dir.
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Returns a schema directory only when its schema file stays within that
|
|
96
|
+
* directory's canonical trust boundary. The directory itself may be a symlink;
|
|
97
|
+
* external user schema links are an intentionally supported workflow.
|
|
98
|
+
*/
|
|
99
|
+
function getSchemaCandidateDir(schemasDir, name) {
|
|
100
|
+
const schemaDir = path.join(schemasDir, name);
|
|
101
|
+
const schemaPath = path.join(schemaDir, 'schema.yaml');
|
|
102
|
+
if (!fs.existsSync(schemaPath)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
FileSystemUtils.assertPathWithin(schemaDir, schemaPath);
|
|
107
|
+
return schemaDir;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolves a schema name to its directory path.
|
|
115
|
+
*
|
|
116
|
+
* Resolution order (when projectRoot is provided):
|
|
117
|
+
* 1. Project-local: <projectRoot>/openspec/schemas/<name>/schema.yaml
|
|
118
|
+
* 2. User override: ${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml
|
|
119
|
+
* 3. Package built-in: <package>/schemas/<name>/schema.yaml
|
|
120
|
+
*
|
|
121
|
+
* When projectRoot is not provided, only user override and package built-in are checked
|
|
122
|
+
* (backward compatible behavior).
|
|
123
|
+
*
|
|
124
|
+
* @param name - Schema name (e.g., "spec-driven")
|
|
125
|
+
* @param projectRoot - Optional project root directory for project-local schema resolution
|
|
126
|
+
* @returns The path to the schema directory, or null if not found
|
|
127
|
+
*/
|
|
128
|
+
export function getSchemaDir(name, projectRoot) {
|
|
129
|
+
if (name.length === 0 ||
|
|
130
|
+
name === '.' ||
|
|
131
|
+
name === '..' ||
|
|
132
|
+
/[\\/]/u.test(name) ||
|
|
133
|
+
/^[A-Za-z]:/u.test(name) ||
|
|
134
|
+
path.posix.isAbsolute(name) ||
|
|
135
|
+
path.win32.isAbsolute(name)) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
// 1. Check project-local directory (if projectRoot provided)
|
|
139
|
+
if (projectRoot) {
|
|
140
|
+
const projectDir = getSchemaCandidateDir(getProjectSchemasDir(projectRoot), name);
|
|
141
|
+
if (projectDir) {
|
|
142
|
+
return projectDir;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// 2. Check user override directory
|
|
146
|
+
const userDir = getSchemaCandidateDir(getUserSchemasDir(), name);
|
|
147
|
+
if (userDir) {
|
|
148
|
+
return userDir;
|
|
149
|
+
}
|
|
150
|
+
// 3. Check package built-in directory
|
|
151
|
+
const packageDir = getSchemaCandidateDir(getPackageSchemasDir(), name);
|
|
152
|
+
if (packageDir) {
|
|
153
|
+
return packageDir;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Resolves a schema name to a SchemaYaml object.
|
|
159
|
+
*
|
|
160
|
+
* Resolution order (when projectRoot is provided):
|
|
161
|
+
* 1. Project-local: <projectRoot>/openspec/schemas/<name>/schema.yaml
|
|
162
|
+
* 2. User override: ${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml
|
|
163
|
+
* 3. Package built-in: <package>/schemas/<name>/schema.yaml
|
|
164
|
+
*
|
|
165
|
+
* When projectRoot is not provided, only user override and package built-in are checked
|
|
166
|
+
* (backward compatible behavior).
|
|
167
|
+
*
|
|
168
|
+
* @param name - Schema name (e.g., "spec-driven")
|
|
169
|
+
* @param projectRoot - Optional project root directory for project-local schema resolution
|
|
170
|
+
* @returns The resolved schema object
|
|
171
|
+
* @throws Error if schema is not found in any location
|
|
172
|
+
*/
|
|
173
|
+
export function resolveSchema(name, projectRoot) {
|
|
174
|
+
// Normalize name (remove .yaml extension if provided)
|
|
175
|
+
const normalizedName = name.replace(/\.ya?ml$/, '');
|
|
176
|
+
const schemaDir = getSchemaDir(normalizedName, projectRoot);
|
|
177
|
+
if (!schemaDir) {
|
|
178
|
+
const availableSchemas = listSchemas(projectRoot);
|
|
179
|
+
throw new Error(`Schema '${normalizedName}' not found. Available schemas: ${availableSchemas.join(', ')}`);
|
|
180
|
+
}
|
|
181
|
+
const schemaPath = path.join(schemaDir, 'schema.yaml');
|
|
182
|
+
// Load and parse the schema
|
|
183
|
+
let content;
|
|
184
|
+
try {
|
|
185
|
+
content = fs.readFileSync(schemaPath, 'utf-8');
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
const ioError = err instanceof Error ? err : new Error(String(err));
|
|
189
|
+
throw new SchemaLoadError(`Failed to read schema at '${schemaPath}': ${ioError.message}`, schemaPath, ioError);
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
return parseSchema(content);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
if (err instanceof SchemaValidationError) {
|
|
196
|
+
throw new SchemaLoadError(`Invalid schema at '${schemaPath}': ${err.message}`, schemaPath, err);
|
|
197
|
+
}
|
|
198
|
+
const parseError = err instanceof Error ? err : new Error(String(err));
|
|
199
|
+
throw new SchemaLoadError(`Failed to parse schema at '${schemaPath}': ${parseError.message}`, schemaPath, parseError);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Lists all available schema names.
|
|
204
|
+
* Combines project-local, user override, and package built-in schemas.
|
|
205
|
+
*
|
|
206
|
+
* @param projectRoot - Optional project root directory for project-local schema resolution
|
|
207
|
+
*/
|
|
208
|
+
export function listSchemas(projectRoot) {
|
|
209
|
+
const schemas = new Set();
|
|
210
|
+
// Add package built-in schemas
|
|
211
|
+
const packageDir = getPackageSchemasDir();
|
|
212
|
+
if (fs.existsSync(packageDir)) {
|
|
213
|
+
for (const entry of fs.readdirSync(packageDir, { withFileTypes: true })) {
|
|
214
|
+
if (isSchemaDir(packageDir, entry)) {
|
|
215
|
+
const schemaPath = path.join(packageDir, entry.name, 'schema.yaml');
|
|
216
|
+
if (fs.existsSync(schemaPath)) {
|
|
217
|
+
schemas.add(entry.name);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Add user override schemas (may override package schemas)
|
|
223
|
+
const userDir = getUserSchemasDir();
|
|
224
|
+
if (fs.existsSync(userDir)) {
|
|
225
|
+
for (const entry of fs.readdirSync(userDir, { withFileTypes: true })) {
|
|
226
|
+
if (isSchemaDir(userDir, entry)) {
|
|
227
|
+
const schemaPath = path.join(userDir, entry.name, 'schema.yaml');
|
|
228
|
+
if (fs.existsSync(schemaPath)) {
|
|
229
|
+
schemas.add(entry.name);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Add project-local schemas (if projectRoot provided)
|
|
235
|
+
if (projectRoot) {
|
|
236
|
+
const projectDir = getProjectSchemasDir(projectRoot);
|
|
237
|
+
if (fs.existsSync(projectDir)) {
|
|
238
|
+
for (const entry of fs.readdirSync(projectDir, { withFileTypes: true })) {
|
|
239
|
+
if (isSchemaDir(projectDir, entry)) {
|
|
240
|
+
const schemaPath = path.join(projectDir, entry.name, 'schema.yaml');
|
|
241
|
+
if (fs.existsSync(schemaPath)) {
|
|
242
|
+
schemas.add(entry.name);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return Array.from(schemas).sort();
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Lists all available schemas with their descriptions and artifact lists.
|
|
252
|
+
* Useful for agent skills to present schema selection to users.
|
|
253
|
+
*
|
|
254
|
+
* @param projectRoot - Optional project root directory for project-local schema resolution
|
|
255
|
+
*/
|
|
256
|
+
export function listSchemasWithInfo(projectRoot) {
|
|
257
|
+
const schemas = [];
|
|
258
|
+
const seenNames = new Set();
|
|
259
|
+
// Add project-local schemas first (highest priority, if projectRoot provided)
|
|
260
|
+
if (projectRoot) {
|
|
261
|
+
const projectDir = getProjectSchemasDir(projectRoot);
|
|
262
|
+
if (fs.existsSync(projectDir)) {
|
|
263
|
+
for (const entry of fs.readdirSync(projectDir, { withFileTypes: true })) {
|
|
264
|
+
if (isSchemaDir(projectDir, entry)) {
|
|
265
|
+
const schemaPath = path.join(projectDir, entry.name, 'schema.yaml');
|
|
266
|
+
if (fs.existsSync(schemaPath)) {
|
|
267
|
+
try {
|
|
268
|
+
const schema = parseSchema(fs.readFileSync(schemaPath, 'utf-8'));
|
|
269
|
+
schemas.push({
|
|
270
|
+
name: entry.name,
|
|
271
|
+
description: schema.description || '',
|
|
272
|
+
artifacts: schema.artifacts.map((a) => a.id),
|
|
273
|
+
source: 'project',
|
|
274
|
+
});
|
|
275
|
+
seenNames.add(entry.name);
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// Skip invalid schemas
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Add user override schemas (if not overridden by project)
|
|
286
|
+
const userDir = getUserSchemasDir();
|
|
287
|
+
if (fs.existsSync(userDir)) {
|
|
288
|
+
for (const entry of fs.readdirSync(userDir, { withFileTypes: true })) {
|
|
289
|
+
if (isSchemaDir(userDir, entry) && !seenNames.has(entry.name)) {
|
|
290
|
+
const schemaPath = path.join(userDir, entry.name, 'schema.yaml');
|
|
291
|
+
if (fs.existsSync(schemaPath)) {
|
|
292
|
+
try {
|
|
293
|
+
const schema = parseSchema(fs.readFileSync(schemaPath, 'utf-8'));
|
|
294
|
+
schemas.push({
|
|
295
|
+
name: entry.name,
|
|
296
|
+
description: schema.description || '',
|
|
297
|
+
artifacts: schema.artifacts.map((a) => a.id),
|
|
298
|
+
source: 'user',
|
|
299
|
+
});
|
|
300
|
+
seenNames.add(entry.name);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
// Skip invalid schemas
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Add package built-in schemas (if not overridden by project or user)
|
|
310
|
+
const packageDir = getPackageSchemasDir();
|
|
311
|
+
if (fs.existsSync(packageDir)) {
|
|
312
|
+
for (const entry of fs.readdirSync(packageDir, { withFileTypes: true })) {
|
|
313
|
+
if (isSchemaDir(packageDir, entry) && !seenNames.has(entry.name)) {
|
|
314
|
+
const schemaPath = path.join(packageDir, entry.name, 'schema.yaml');
|
|
315
|
+
if (fs.existsSync(schemaPath)) {
|
|
316
|
+
try {
|
|
317
|
+
const schema = parseSchema(fs.readFileSync(schemaPath, 'utf-8'));
|
|
318
|
+
schemas.push({
|
|
319
|
+
name: entry.name,
|
|
320
|
+
description: schema.description || '',
|
|
321
|
+
artifacts: schema.artifacts.map((a) => a.id),
|
|
322
|
+
source: 'package',
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
// Skip invalid schemas
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return schemas.sort((a, b) => a.name.localeCompare(b.name));
|
|
333
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type SchemaYaml } from './types.js';
|
|
2
|
+
export declare class SchemaValidationError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Loads and validates an artifact schema from a YAML file.
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadSchema(filePath: string): SchemaYaml;
|
|
9
|
+
/**
|
|
10
|
+
* Parses and validates an artifact schema from YAML content.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseSchema(yamlContent: string): SchemaYaml;
|