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,15 @@
|
|
|
1
|
+
export declare const GLOBAL_DATA_DIR_NAME = "openspec";
|
|
2
|
+
export interface GlobalDataDirOptions {
|
|
3
|
+
env?: NodeJS.ProcessEnv;
|
|
4
|
+
platform?: NodeJS.Platform;
|
|
5
|
+
homedir?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Gets the global data directory path following the XDG Base Directory
|
|
9
|
+
* Specification. Used for user data like schema overrides.
|
|
10
|
+
*
|
|
11
|
+
* - All platforms: $XDG_DATA_HOME/openspec/ if XDG_DATA_HOME is set
|
|
12
|
+
* - Unix/macOS fallback: ~/.local/share/openspec/
|
|
13
|
+
* - Windows fallback: %LOCALAPPDATA%/openspec/
|
|
14
|
+
*/
|
|
15
|
+
export declare function getGlobalDataDir(options?: GlobalDataDirOptions): string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Store-free replacement for @fission-ai/openspec@1.8.0 src/core/global-config.ts.
|
|
2
|
+
//
|
|
3
|
+
// PATCH: the schema-validation slice only needs getGlobalDataDir() to locate the
|
|
4
|
+
// user's schema-override directory. The upstream file additionally carries config
|
|
5
|
+
// load/save and (in the fork) the multi-repo ~/.openspec store + worksets plumbing.
|
|
6
|
+
// None of that belongs in this slice, so this trimmed module exposes ONLY the
|
|
7
|
+
// XDG-based data-dir resolver, preserving upstream's exact resolution logic:
|
|
8
|
+
// ${XDG_DATA_HOME}/openspec -> else per-platform default:
|
|
9
|
+
// unix/macOS: ~/.local/share/openspec
|
|
10
|
+
// win32: %LOCALAPPDATA%/openspec (or ~/AppData/Local/openspec)
|
|
11
|
+
// No ~/.openspec store, no worksets.
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import * as os from 'node:os';
|
|
14
|
+
// Constant kept identical to upstream.
|
|
15
|
+
export const GLOBAL_DATA_DIR_NAME = 'openspec';
|
|
16
|
+
function joinGlobalDataPath(platform, ...segments) {
|
|
17
|
+
return platform === 'win32'
|
|
18
|
+
? path.win32.join(...segments)
|
|
19
|
+
: path.posix.join(...segments);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Gets the global data directory path following the XDG Base Directory
|
|
23
|
+
* Specification. Used for user data like schema overrides.
|
|
24
|
+
*
|
|
25
|
+
* - All platforms: $XDG_DATA_HOME/openspec/ if XDG_DATA_HOME is set
|
|
26
|
+
* - Unix/macOS fallback: ~/.local/share/openspec/
|
|
27
|
+
* - Windows fallback: %LOCALAPPDATA%/openspec/
|
|
28
|
+
*/
|
|
29
|
+
export function getGlobalDataDir(options = {}) {
|
|
30
|
+
const env = options.env ?? process.env;
|
|
31
|
+
const platform = options.platform ?? os.platform();
|
|
32
|
+
// XDG_DATA_HOME takes precedence on all platforms when explicitly set
|
|
33
|
+
const xdgDataHome = env.XDG_DATA_HOME;
|
|
34
|
+
if (xdgDataHome) {
|
|
35
|
+
return joinGlobalDataPath(platform, xdgDataHome, GLOBAL_DATA_DIR_NAME);
|
|
36
|
+
}
|
|
37
|
+
const homedir = options.homedir ?? os.homedir();
|
|
38
|
+
if (platform === 'win32') {
|
|
39
|
+
// Windows: use %LOCALAPPDATA%
|
|
40
|
+
const localAppData = env.LOCALAPPDATA;
|
|
41
|
+
if (localAppData) {
|
|
42
|
+
return joinGlobalDataPath(platform, localAppData, GLOBAL_DATA_DIR_NAME);
|
|
43
|
+
}
|
|
44
|
+
// Fallback for Windows if LOCALAPPDATA is not set
|
|
45
|
+
return joinGlobalDataPath(platform, homedir, 'AppData', 'Local', GLOBAL_DATA_DIR_NAME);
|
|
46
|
+
}
|
|
47
|
+
// Unix/macOS fallback: ~/.local/share
|
|
48
|
+
return joinGlobalDataPath(platform, homedir, '.local', 'share', GLOBAL_DATA_DIR_NAME);
|
|
49
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Artifact, SchemaYaml, CompletedSet, BlockedArtifacts } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Represents an artifact dependency graph.
|
|
4
|
+
* Provides methods for querying build order, ready artifacts, and completion status.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ArtifactGraph {
|
|
7
|
+
private artifacts;
|
|
8
|
+
private schema;
|
|
9
|
+
/** Artifact id -> its position in the schema's `artifacts:` list. */
|
|
10
|
+
private declarationOrder;
|
|
11
|
+
private constructor();
|
|
12
|
+
/**
|
|
13
|
+
* Orders artifact ids by where the schema declares them.
|
|
14
|
+
*
|
|
15
|
+
* The dependency graph leaves siblings tied - spec-driven's `specs` and
|
|
16
|
+
* `design` both require only `proposal`, so both become ready at the same
|
|
17
|
+
* time. Ties used to be broken alphabetically, which put `design` ahead of
|
|
18
|
+
* `specs` and made the CLI recommend the artifacts in an order that
|
|
19
|
+
* contradicted the schema's own documented sequence
|
|
20
|
+
* (proposal -> specs -> design -> tasks). Breaking ties by declaration order
|
|
21
|
+
* follows the sequence the schema author wrote, for built-in and custom
|
|
22
|
+
* schemas alike, and stays just as deterministic. Ids not in the schema sort
|
|
23
|
+
* last so the comparator stays total.
|
|
24
|
+
*/
|
|
25
|
+
private compareByDeclarationOrder;
|
|
26
|
+
/**
|
|
27
|
+
* Creates an ArtifactGraph from a YAML file path.
|
|
28
|
+
*/
|
|
29
|
+
static fromYaml(filePath: string): ArtifactGraph;
|
|
30
|
+
/**
|
|
31
|
+
* Creates an ArtifactGraph from YAML content string.
|
|
32
|
+
*/
|
|
33
|
+
static fromYamlContent(yamlContent: string): ArtifactGraph;
|
|
34
|
+
/**
|
|
35
|
+
* Creates an ArtifactGraph from a pre-validated schema object.
|
|
36
|
+
*/
|
|
37
|
+
static fromSchema(schema: SchemaYaml): ArtifactGraph;
|
|
38
|
+
/**
|
|
39
|
+
* Gets a single artifact by ID.
|
|
40
|
+
*/
|
|
41
|
+
getArtifact(id: string): Artifact | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Gets all artifacts in the graph.
|
|
44
|
+
*/
|
|
45
|
+
getAllArtifacts(): Artifact[];
|
|
46
|
+
/**
|
|
47
|
+
* Gets the schema name.
|
|
48
|
+
*/
|
|
49
|
+
getName(): string;
|
|
50
|
+
/**
|
|
51
|
+
* Gets the schema version.
|
|
52
|
+
*/
|
|
53
|
+
getVersion(): number;
|
|
54
|
+
/**
|
|
55
|
+
* Computes the topological build order using Kahn's algorithm.
|
|
56
|
+
* Returns artifact IDs in the order they should be built.
|
|
57
|
+
*/
|
|
58
|
+
getBuildOrder(): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Gets artifacts that are ready to be created (all dependencies completed).
|
|
61
|
+
*/
|
|
62
|
+
getNextArtifacts(completed: CompletedSet): string[];
|
|
63
|
+
/**
|
|
64
|
+
* Checks if all artifacts in the graph are completed.
|
|
65
|
+
*/
|
|
66
|
+
isComplete(completed: CompletedSet): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Gets blocked artifacts and their unmet dependencies.
|
|
69
|
+
*/
|
|
70
|
+
getBlocked(completed: CompletedSet): BlockedArtifacts;
|
|
71
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { loadSchema, parseSchema } from './schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* Represents an artifact dependency graph.
|
|
4
|
+
* Provides methods for querying build order, ready artifacts, and completion status.
|
|
5
|
+
*/
|
|
6
|
+
export class ArtifactGraph {
|
|
7
|
+
artifacts;
|
|
8
|
+
schema;
|
|
9
|
+
/** Artifact id -> its position in the schema's `artifacts:` list. */
|
|
10
|
+
declarationOrder;
|
|
11
|
+
constructor(schema) {
|
|
12
|
+
this.schema = schema;
|
|
13
|
+
this.artifacts = new Map(schema.artifacts.map(a => [a.id, a]));
|
|
14
|
+
this.declarationOrder = new Map(schema.artifacts.map((a, index) => [a.id, index]));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Orders artifact ids by where the schema declares them.
|
|
18
|
+
*
|
|
19
|
+
* The dependency graph leaves siblings tied - spec-driven's `specs` and
|
|
20
|
+
* `design` both require only `proposal`, so both become ready at the same
|
|
21
|
+
* time. Ties used to be broken alphabetically, which put `design` ahead of
|
|
22
|
+
* `specs` and made the CLI recommend the artifacts in an order that
|
|
23
|
+
* contradicted the schema's own documented sequence
|
|
24
|
+
* (proposal -> specs -> design -> tasks). Breaking ties by declaration order
|
|
25
|
+
* follows the sequence the schema author wrote, for built-in and custom
|
|
26
|
+
* schemas alike, and stays just as deterministic. Ids not in the schema sort
|
|
27
|
+
* last so the comparator stays total.
|
|
28
|
+
*/
|
|
29
|
+
compareByDeclarationOrder(a, b) {
|
|
30
|
+
return ((this.declarationOrder.get(a) ?? Number.MAX_SAFE_INTEGER) -
|
|
31
|
+
(this.declarationOrder.get(b) ?? Number.MAX_SAFE_INTEGER));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates an ArtifactGraph from a YAML file path.
|
|
35
|
+
*/
|
|
36
|
+
static fromYaml(filePath) {
|
|
37
|
+
const schema = loadSchema(filePath);
|
|
38
|
+
return new ArtifactGraph(schema);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates an ArtifactGraph from YAML content string.
|
|
42
|
+
*/
|
|
43
|
+
static fromYamlContent(yamlContent) {
|
|
44
|
+
const schema = parseSchema(yamlContent);
|
|
45
|
+
return new ArtifactGraph(schema);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates an ArtifactGraph from a pre-validated schema object.
|
|
49
|
+
*/
|
|
50
|
+
static fromSchema(schema) {
|
|
51
|
+
return new ArtifactGraph(schema);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Gets a single artifact by ID.
|
|
55
|
+
*/
|
|
56
|
+
getArtifact(id) {
|
|
57
|
+
return this.artifacts.get(id);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Gets all artifacts in the graph.
|
|
61
|
+
*/
|
|
62
|
+
getAllArtifacts() {
|
|
63
|
+
return Array.from(this.artifacts.values());
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Gets the schema name.
|
|
67
|
+
*/
|
|
68
|
+
getName() {
|
|
69
|
+
return this.schema.name;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Gets the schema version.
|
|
73
|
+
*/
|
|
74
|
+
getVersion() {
|
|
75
|
+
return this.schema.version;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Computes the topological build order using Kahn's algorithm.
|
|
79
|
+
* Returns artifact IDs in the order they should be built.
|
|
80
|
+
*/
|
|
81
|
+
getBuildOrder() {
|
|
82
|
+
const inDegree = new Map();
|
|
83
|
+
const dependents = new Map();
|
|
84
|
+
// Initialize all artifacts
|
|
85
|
+
for (const artifact of this.artifacts.values()) {
|
|
86
|
+
inDegree.set(artifact.id, artifact.requires.length);
|
|
87
|
+
dependents.set(artifact.id, []);
|
|
88
|
+
}
|
|
89
|
+
// Build reverse adjacency (who depends on whom)
|
|
90
|
+
for (const artifact of this.artifacts.values()) {
|
|
91
|
+
for (const req of artifact.requires) {
|
|
92
|
+
dependents.get(req).push(artifact.id);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Start with roots (in-degree 0), in declaration order for determinism
|
|
96
|
+
const queue = [...this.artifacts.keys()]
|
|
97
|
+
.filter(id => inDegree.get(id) === 0)
|
|
98
|
+
.sort((a, b) => this.compareByDeclarationOrder(a, b));
|
|
99
|
+
const result = [];
|
|
100
|
+
while (queue.length > 0) {
|
|
101
|
+
const current = queue.shift();
|
|
102
|
+
result.push(current);
|
|
103
|
+
// Collect newly ready artifacts, then sort before adding
|
|
104
|
+
const newlyReady = [];
|
|
105
|
+
for (const dep of dependents.get(current)) {
|
|
106
|
+
const newDegree = inDegree.get(dep) - 1;
|
|
107
|
+
inDegree.set(dep, newDegree);
|
|
108
|
+
if (newDegree === 0) {
|
|
109
|
+
newlyReady.push(dep);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Re-sort the whole queue, not just the new arrivals: an artifact that
|
|
113
|
+
// has been waiting can be declared after one that just became ready.
|
|
114
|
+
queue.push(...newlyReady);
|
|
115
|
+
queue.sort((a, b) => this.compareByDeclarationOrder(a, b));
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Gets artifacts that are ready to be created (all dependencies completed).
|
|
121
|
+
*/
|
|
122
|
+
getNextArtifacts(completed) {
|
|
123
|
+
const ready = [];
|
|
124
|
+
for (const artifact of this.artifacts.values()) {
|
|
125
|
+
if (completed.has(artifact.id)) {
|
|
126
|
+
continue; // Already completed
|
|
127
|
+
}
|
|
128
|
+
const allDepsCompleted = artifact.requires.every(req => completed.has(req));
|
|
129
|
+
if (allDepsCompleted) {
|
|
130
|
+
ready.push(artifact.id);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Declaration order: deterministic, and the first entry is the artifact the
|
|
134
|
+
// schema wants written next.
|
|
135
|
+
return ready.sort((a, b) => this.compareByDeclarationOrder(a, b));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Checks if all artifacts in the graph are completed.
|
|
139
|
+
*/
|
|
140
|
+
isComplete(completed) {
|
|
141
|
+
for (const artifact of this.artifacts.values()) {
|
|
142
|
+
if (!completed.has(artifact.id)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Gets blocked artifacts and their unmet dependencies.
|
|
150
|
+
*/
|
|
151
|
+
getBlocked(completed) {
|
|
152
|
+
const blocked = {};
|
|
153
|
+
for (const artifact of this.artifacts.values()) {
|
|
154
|
+
if (completed.has(artifact.id)) {
|
|
155
|
+
continue; // Already completed
|
|
156
|
+
}
|
|
157
|
+
const unmetDeps = artifact.requires.filter(req => !completed.has(req));
|
|
158
|
+
if (unmetDeps.length > 0) {
|
|
159
|
+
blocked[artifact.id] = unmetDeps.sort((a, b) => this.compareByDeclarationOrder(a, b));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return blocked;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { ArtifactSchema, SchemaYamlSchema, type Artifact, type SchemaYaml, type CompletedSet, type BlockedArtifacts, } from './types.js';
|
|
2
|
+
export { loadSchema, parseSchema, SchemaValidationError } from './schema.js';
|
|
3
|
+
export { resolveSchema, listSchemas, listSchemasWithInfo, getSchemaDir, getPackageSchemasDir, getUserSchemasDir, getProjectSchemasDir, SchemaLoadError, type SchemaInfo, } from './resolver.js';
|
|
4
|
+
export { getGlobalDataDir } from './global-config.js';
|
|
5
|
+
export { ArtifactGraph } from './graph.js';
|
|
6
|
+
export { detectCompleted } from './state.js';
|
|
7
|
+
export { artifactOutputExists, isGlobPattern, resolveArtifactOutputPath, resolveArtifactOutputs, } from './outputs.js';
|
|
8
|
+
export { readChangeMetadata, resolveSchemaForChange, writeChangeMetadata, ChangeMetadataSchema, ChangeMetadataError, METADATA_FILENAME, type ChangeMetadata, } from './change-metadata.js';
|
|
9
|
+
export { loadTemplate, loadChangeContext, generateInstructions, formatChangeStatus, TemplateLoadError, SKIP_SPECS_INSTRUCTIONS_WARNING, type ChangeContext, type LoadChangeContextOptions, type ArtifactInstructions, type DependencyInfo, type ArtifactStatus, type ChangeStatus, type ArtifactPathSummary, } from './instruction-loader.js';
|
|
10
|
+
export { getTaskProgressForChange, parseTaskLines, countTasksFromContent, formatTaskStatus, type TaskProgress, type ParsedTask, } from './task-progress.js';
|
|
11
|
+
export { buildActionContext, buildNextSteps, type ActionContext, } from './change-status-policy.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Barrel for the vendored artifact-graph engine slice.
|
|
2
|
+
// P3 inc.1 shipped schema/resolver/types only; P3 inc.4 adds the rest of the
|
|
3
|
+
// engine (graph/state/outputs + a store-free instruction-loader) so the CLI
|
|
4
|
+
// shim's `status`/`instructions`/`list` run entirely on vendored code.
|
|
5
|
+
export { ArtifactSchema, SchemaYamlSchema, } from './types.js';
|
|
6
|
+
export { loadSchema, parseSchema, SchemaValidationError } from './schema.js';
|
|
7
|
+
export { resolveSchema, listSchemas, listSchemasWithInfo, getSchemaDir, getPackageSchemasDir, getUserSchemasDir, getProjectSchemasDir, SchemaLoadError, } from './resolver.js';
|
|
8
|
+
export { getGlobalDataDir } from './global-config.js';
|
|
9
|
+
// Graph operations
|
|
10
|
+
export { ArtifactGraph } from './graph.js';
|
|
11
|
+
// State detection
|
|
12
|
+
export { detectCompleted } from './state.js';
|
|
13
|
+
export { artifactOutputExists, isGlobPattern, resolveArtifactOutputPath, resolveArtifactOutputs, } from './outputs.js';
|
|
14
|
+
// Change metadata (store-free)
|
|
15
|
+
export { readChangeMetadata, resolveSchemaForChange, writeChangeMetadata, ChangeMetadataSchema, ChangeMetadataError, METADATA_FILENAME, } from './change-metadata.js';
|
|
16
|
+
// Instruction loading + status (store-free)
|
|
17
|
+
export { loadTemplate, loadChangeContext, generateInstructions, formatChangeStatus, TemplateLoadError, SKIP_SPECS_INSTRUCTIONS_WARNING, } from './instruction-loader.js';
|
|
18
|
+
// Task progress (list)
|
|
19
|
+
export { getTaskProgressForChange, parseTaskLines, countTasksFromContent, formatTaskStatus, } from './task-progress.js';
|
|
20
|
+
// Change-status policy
|
|
21
|
+
export { buildActionContext, buildNextSteps, } from './change-status-policy.js';
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { ArtifactGraph } from './graph.js';
|
|
2
|
+
import { type ChangeMetadata } from './change-metadata.js';
|
|
3
|
+
import { type ActionContext } from './change-status-policy.js';
|
|
4
|
+
import type { CompletedSet } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Error thrown when loading a template fails.
|
|
7
|
+
*/
|
|
8
|
+
export declare class TemplateLoadError extends Error {
|
|
9
|
+
readonly templatePath: string;
|
|
10
|
+
constructor(message: string, templatePath: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Change context containing graph, completion state, and metadata.
|
|
14
|
+
*/
|
|
15
|
+
export interface ChangeContext {
|
|
16
|
+
graph: ArtifactGraph;
|
|
17
|
+
completed: CompletedSet;
|
|
18
|
+
schemaName: string;
|
|
19
|
+
changeName: string;
|
|
20
|
+
changeDir: string;
|
|
21
|
+
projectRoot: string;
|
|
22
|
+
metadata?: ChangeMetadata;
|
|
23
|
+
/**
|
|
24
|
+
* Artifact IDs counted as complete only because the change declares
|
|
25
|
+
* skip_specs, not because their files exist. Kept separate so status can
|
|
26
|
+
* render them as skipped rather than done.
|
|
27
|
+
*/
|
|
28
|
+
skippedArtifacts?: Set<string>;
|
|
29
|
+
}
|
|
30
|
+
export interface LoadChangeContextOptions {
|
|
31
|
+
changeDir?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The do-not-create signal attached to a skipped (skip_specs) artifact.
|
|
35
|
+
*/
|
|
36
|
+
export declare const SKIP_SPECS_INSTRUCTIONS_WARNING: string;
|
|
37
|
+
export interface DependencyInfo {
|
|
38
|
+
id: string;
|
|
39
|
+
done: boolean;
|
|
40
|
+
path: string;
|
|
41
|
+
description: string;
|
|
42
|
+
skipped?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface ArtifactInstructions {
|
|
45
|
+
changeName: string;
|
|
46
|
+
artifactId: string;
|
|
47
|
+
schemaName: string;
|
|
48
|
+
changeDir: string;
|
|
49
|
+
outputPath: string;
|
|
50
|
+
resolvedOutputPath: string;
|
|
51
|
+
existingOutputPaths: string[];
|
|
52
|
+
description: string;
|
|
53
|
+
instruction: string | undefined;
|
|
54
|
+
template: string;
|
|
55
|
+
dependencies: DependencyInfo[];
|
|
56
|
+
unlocks: string[];
|
|
57
|
+
skipped?: boolean;
|
|
58
|
+
warning?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface ArtifactStatus {
|
|
61
|
+
id: string;
|
|
62
|
+
outputPath: string;
|
|
63
|
+
status: 'done' | 'skipped' | 'ready' | 'blocked';
|
|
64
|
+
requires: string[];
|
|
65
|
+
missingDeps?: string[];
|
|
66
|
+
}
|
|
67
|
+
export interface ArtifactPathSummary {
|
|
68
|
+
outputPath: string;
|
|
69
|
+
resolvedOutputPath: string;
|
|
70
|
+
existingOutputPaths: string[];
|
|
71
|
+
}
|
|
72
|
+
export interface ChangeStatus {
|
|
73
|
+
changeName: string;
|
|
74
|
+
schemaName: string;
|
|
75
|
+
changeRoot: string;
|
|
76
|
+
artifactPaths: Record<string, ArtifactPathSummary>;
|
|
77
|
+
nextSteps: string[];
|
|
78
|
+
actionContext: ActionContext;
|
|
79
|
+
isPlanningComplete: boolean;
|
|
80
|
+
isComplete: boolean;
|
|
81
|
+
applyRequires: string[];
|
|
82
|
+
artifacts: ArtifactStatus[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Loads a template from a schema's templates directory.
|
|
86
|
+
*/
|
|
87
|
+
export declare function loadTemplate(schemaName: string, templatePath: string, projectRoot?: string): string;
|
|
88
|
+
/**
|
|
89
|
+
* Loads change context combining graph and completion state.
|
|
90
|
+
*
|
|
91
|
+
* Schema resolution order: explicit override -> `.openspec.yaml` metadata ->
|
|
92
|
+
* 'spec-driven'.
|
|
93
|
+
*/
|
|
94
|
+
export declare function loadChangeContext(projectRoot: string, changeName: string, schemaName?: string, options?: LoadChangeContextOptions): ChangeContext;
|
|
95
|
+
/**
|
|
96
|
+
* Generates enriched instructions for creating an artifact.
|
|
97
|
+
*/
|
|
98
|
+
export declare function generateInstructions(context: ChangeContext, artifactId: string): ArtifactInstructions;
|
|
99
|
+
/**
|
|
100
|
+
* Formats the status of all artifacts in a change.
|
|
101
|
+
*/
|
|
102
|
+
export declare function formatChangeStatus(context: ChangeContext): ChangeStatus;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// RECONSTRUCTED (store-free) from @fission-ai/openspec@1.8.0
|
|
2
|
+
// src/core/artifact-graph/instruction-loader.ts
|
|
3
|
+
//
|
|
4
|
+
// The upstream loader couples artifact instructions/status to a large store
|
|
5
|
+
// stack: project-config (context/rules), the referenced-store index, the
|
|
6
|
+
// multi-repo planning home, and the change-metadata/project-config schema
|
|
7
|
+
// fallback. This slice keeps the artifact-graph behaviour the /opsx skills
|
|
8
|
+
// depend on and drops the store coupling:
|
|
9
|
+
// - loadChangeContext / generateInstructions / formatChangeStatus preserve
|
|
10
|
+
// their output shapes for the fields the skills read (artifacts[].status,
|
|
11
|
+
// artifacts[].requires, applyRequires, isPlanningComplete/isComplete,
|
|
12
|
+
// dependencies, unlocks, template, skip_specs handling).
|
|
13
|
+
// - DROPPED vs upstream: `planningHome`, `references`, config-driven
|
|
14
|
+
// `context`/`rules`, and the config.yaml schema fallback. These are
|
|
15
|
+
// store/config surfaces outside the vendored slice.
|
|
16
|
+
// skip_specs handling and the declaration-order build sequence are faithful.
|
|
17
|
+
import * as fs from 'node:fs';
|
|
18
|
+
import * as path from 'node:path';
|
|
19
|
+
import { getSchemaDir, resolveSchema } from './resolver.js';
|
|
20
|
+
import { ArtifactGraph } from './graph.js';
|
|
21
|
+
import { detectCompleted } from './state.js';
|
|
22
|
+
import { resolveArtifactOutputPath, resolveArtifactOutputs } from './outputs.js';
|
|
23
|
+
import { readChangeMetadata, resolveSchemaForChange } from './change-metadata.js';
|
|
24
|
+
import { FileSystemUtils } from './file-system.js';
|
|
25
|
+
import { buildActionContext, buildNextSteps, } from './change-status-policy.js';
|
|
26
|
+
/**
|
|
27
|
+
* Error thrown when loading a template fails.
|
|
28
|
+
*/
|
|
29
|
+
export class TemplateLoadError extends Error {
|
|
30
|
+
templatePath;
|
|
31
|
+
constructor(message, templatePath) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.templatePath = templatePath;
|
|
34
|
+
this.name = 'TemplateLoadError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The do-not-create signal attached to a skipped (skip_specs) artifact.
|
|
39
|
+
*/
|
|
40
|
+
export const SKIP_SPECS_INSTRUCTIONS_WARNING = 'This change declares skip_specs: true in .openspec.yaml (no spec-level behavior changes), so this artifact is skipped.\n' +
|
|
41
|
+
'Do not create spec files - they will conflict with that marker. If requirements now change, remove skip_specs from .openspec.yaml and rerun this command.';
|
|
42
|
+
/**
|
|
43
|
+
* Loads a template from a schema's templates directory.
|
|
44
|
+
*/
|
|
45
|
+
export function loadTemplate(schemaName, templatePath, projectRoot) {
|
|
46
|
+
const schemaDir = getSchemaDir(schemaName, projectRoot);
|
|
47
|
+
if (!schemaDir) {
|
|
48
|
+
throw new TemplateLoadError(`Schema '${schemaName}' not found`, templatePath);
|
|
49
|
+
}
|
|
50
|
+
const templatesDir = path.join(schemaDir, 'templates');
|
|
51
|
+
const templatePathOnDisk = path.join(templatesDir, templatePath);
|
|
52
|
+
try {
|
|
53
|
+
FileSystemUtils.assertPathWithin(templatesDir, templatePathOnDisk);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new TemplateLoadError(error instanceof Error ? error.message : String(error), templatePathOnDisk);
|
|
57
|
+
}
|
|
58
|
+
if (!fs.existsSync(templatePathOnDisk)) {
|
|
59
|
+
throw new TemplateLoadError(`Template not found: ${templatePathOnDisk}`, templatePathOnDisk);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
return fs.readFileSync(templatePathOnDisk, 'utf-8');
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const ioError = err instanceof Error ? err : new Error(String(err));
|
|
66
|
+
throw new TemplateLoadError(`Failed to read template: ${ioError.message}`, templatePathOnDisk);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Loads change context combining graph and completion state.
|
|
71
|
+
*
|
|
72
|
+
* Schema resolution order: explicit override -> `.openspec.yaml` metadata ->
|
|
73
|
+
* 'spec-driven'.
|
|
74
|
+
*/
|
|
75
|
+
export function loadChangeContext(projectRoot, changeName, schemaName, options = {}) {
|
|
76
|
+
const changeDir = options.changeDir ?? path.join(projectRoot, 'openspec', 'changes', changeName);
|
|
77
|
+
const metadata = readChangeMetadata(changeDir, projectRoot) ?? undefined;
|
|
78
|
+
const resolvedSchemaName = resolveSchemaForChange(changeDir, schemaName, projectRoot, {
|
|
79
|
+
metadata: metadata ?? null,
|
|
80
|
+
});
|
|
81
|
+
const schema = resolveSchema(resolvedSchemaName, projectRoot);
|
|
82
|
+
const graph = ArtifactGraph.fromSchema(schema);
|
|
83
|
+
const completed = detectCompleted(graph, changeDir);
|
|
84
|
+
// A change that declares skip_specs has no spec deltas by design, so
|
|
85
|
+
// artifacts generating into specs/ count as complete; tracked separately so
|
|
86
|
+
// status renders them as skipped, not done.
|
|
87
|
+
const skippedArtifacts = new Set();
|
|
88
|
+
if (metadata?.skip_specs) {
|
|
89
|
+
for (const artifact of graph.getAllArtifacts()) {
|
|
90
|
+
const generates = artifact.generates.replace(/^(?:\.\/)+/, '');
|
|
91
|
+
if (generates.startsWith('specs/') && !completed.has(artifact.id)) {
|
|
92
|
+
completed.add(artifact.id);
|
|
93
|
+
skippedArtifacts.add(artifact.id);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
graph,
|
|
99
|
+
completed,
|
|
100
|
+
schemaName: resolvedSchemaName,
|
|
101
|
+
changeName,
|
|
102
|
+
changeDir,
|
|
103
|
+
projectRoot,
|
|
104
|
+
...(metadata ? { metadata } : {}),
|
|
105
|
+
...(skippedArtifacts.size > 0 ? { skippedArtifacts } : {}),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Generates enriched instructions for creating an artifact.
|
|
110
|
+
*/
|
|
111
|
+
export function generateInstructions(context, artifactId) {
|
|
112
|
+
const artifact = context.graph.getArtifact(artifactId);
|
|
113
|
+
if (!artifact) {
|
|
114
|
+
throw new Error(`Artifact '${artifactId}' not found in schema '${context.schemaName}'`);
|
|
115
|
+
}
|
|
116
|
+
const templateContent = loadTemplate(context.schemaName, artifact.template, context.projectRoot);
|
|
117
|
+
const dependencies = getDependencyInfo(artifact, context.graph, context.completed, context.skippedArtifacts);
|
|
118
|
+
const unlocks = getUnlockedArtifacts(context.graph, artifactId);
|
|
119
|
+
return {
|
|
120
|
+
changeName: context.changeName,
|
|
121
|
+
artifactId: artifact.id,
|
|
122
|
+
schemaName: context.schemaName,
|
|
123
|
+
changeDir: context.changeDir,
|
|
124
|
+
outputPath: artifact.generates,
|
|
125
|
+
resolvedOutputPath: resolveArtifactOutputPath(context.changeDir, artifact.generates),
|
|
126
|
+
existingOutputPaths: resolveArtifactOutputs(context.changeDir, artifact.generates),
|
|
127
|
+
description: artifact.description,
|
|
128
|
+
instruction: artifact.instruction,
|
|
129
|
+
...(context.skippedArtifacts?.has(artifact.id)
|
|
130
|
+
? { skipped: true, warning: SKIP_SPECS_INSTRUCTIONS_WARNING }
|
|
131
|
+
: {}),
|
|
132
|
+
template: templateContent,
|
|
133
|
+
dependencies,
|
|
134
|
+
unlocks,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function getDependencyInfo(artifact, graph, completed, skippedArtifacts) {
|
|
138
|
+
return artifact.requires.map((id) => {
|
|
139
|
+
const depArtifact = graph.getArtifact(id);
|
|
140
|
+
return {
|
|
141
|
+
id,
|
|
142
|
+
done: completed.has(id),
|
|
143
|
+
path: depArtifact?.generates ?? id,
|
|
144
|
+
description: depArtifact?.description ?? '',
|
|
145
|
+
...(skippedArtifacts?.has(id) ? { skipped: true } : {}),
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function getUnlockedArtifacts(graph, artifactId) {
|
|
150
|
+
const unlocks = [];
|
|
151
|
+
for (const artifact of graph.getAllArtifacts()) {
|
|
152
|
+
if (artifact.requires.includes(artifactId)) {
|
|
153
|
+
unlocks.push(artifact.id);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return unlocks;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Formats the status of all artifacts in a change.
|
|
160
|
+
*/
|
|
161
|
+
export function formatChangeStatus(context) {
|
|
162
|
+
const schema = resolveSchema(context.schemaName, context.projectRoot);
|
|
163
|
+
const applyRequires = schema.apply?.requires ?? schema.artifacts.map((a) => a.id);
|
|
164
|
+
const artifacts = context.graph.getAllArtifacts();
|
|
165
|
+
const ready = new Set(context.graph.getNextArtifacts(context.completed));
|
|
166
|
+
const blocked = context.graph.getBlocked(context.completed);
|
|
167
|
+
const artifactPaths = {};
|
|
168
|
+
const artifactStatuses = artifacts.map((artifact) => {
|
|
169
|
+
artifactPaths[artifact.id] = {
|
|
170
|
+
outputPath: artifact.generates,
|
|
171
|
+
resolvedOutputPath: resolveArtifactOutputPath(context.changeDir, artifact.generates),
|
|
172
|
+
existingOutputPaths: resolveArtifactOutputs(context.changeDir, artifact.generates),
|
|
173
|
+
};
|
|
174
|
+
if (context.skippedArtifacts?.has(artifact.id)) {
|
|
175
|
+
return {
|
|
176
|
+
id: artifact.id,
|
|
177
|
+
outputPath: artifact.generates,
|
|
178
|
+
status: 'skipped',
|
|
179
|
+
requires: artifact.requires,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (context.completed.has(artifact.id)) {
|
|
183
|
+
return {
|
|
184
|
+
id: artifact.id,
|
|
185
|
+
outputPath: artifact.generates,
|
|
186
|
+
status: 'done',
|
|
187
|
+
requires: artifact.requires,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (ready.has(artifact.id)) {
|
|
191
|
+
return {
|
|
192
|
+
id: artifact.id,
|
|
193
|
+
outputPath: artifact.generates,
|
|
194
|
+
status: 'ready',
|
|
195
|
+
requires: artifact.requires,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
id: artifact.id,
|
|
200
|
+
outputPath: artifact.generates,
|
|
201
|
+
status: 'blocked',
|
|
202
|
+
requires: artifact.requires,
|
|
203
|
+
missingDeps: blocked[artifact.id] ?? [],
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
const buildOrder = context.graph.getBuildOrder();
|
|
207
|
+
const orderMap = new Map(buildOrder.map((id, idx) => [id, idx]));
|
|
208
|
+
artifactStatuses.sort((a, b) => (orderMap.get(a.id) ?? 0) - (orderMap.get(b.id) ?? 0));
|
|
209
|
+
const isComplete = context.graph.isComplete(context.completed);
|
|
210
|
+
const artifactIds = artifactStatuses.map((artifact) => artifact.id);
|
|
211
|
+
return {
|
|
212
|
+
changeName: context.changeName,
|
|
213
|
+
schemaName: context.schemaName,
|
|
214
|
+
changeRoot: context.changeDir,
|
|
215
|
+
artifactPaths,
|
|
216
|
+
isPlanningComplete: isComplete,
|
|
217
|
+
isComplete,
|
|
218
|
+
applyRequires,
|
|
219
|
+
nextSteps: buildNextSteps({
|
|
220
|
+
changeName: context.changeName,
|
|
221
|
+
artifactStatuses,
|
|
222
|
+
allArtifactsComplete: isComplete,
|
|
223
|
+
}),
|
|
224
|
+
actionContext: buildActionContext({
|
|
225
|
+
projectRoot: context.projectRoot,
|
|
226
|
+
artifactIds,
|
|
227
|
+
}),
|
|
228
|
+
artifacts: artifactStatuses,
|
|
229
|
+
};
|
|
230
|
+
}
|