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,460 @@
|
|
|
1
|
+
// `openspec instructions <artifact> --change <id> [--json]` over the vendored
|
|
2
|
+
// engine, plus the two special targets the /opsx skills use: `apply` and
|
|
3
|
+
// `archive`.
|
|
4
|
+
//
|
|
5
|
+
// Reconstructed from src/commands/workflow/instructions.ts, minus store /
|
|
6
|
+
// root-selection, the referenced-store index, and project-config-driven
|
|
7
|
+
// context/rules/operation-inputs. Artifact instructions are faithful; `apply`
|
|
8
|
+
// reimplements the schema-aware apply state machine (blocked / all_done / ready)
|
|
9
|
+
// against the change's tracked-tasks file; `archive` returns the archive-inputs brief:
|
|
10
|
+
// the project context and the operation guidance from `openspec/config.yaml`
|
|
11
|
+
// (`operations.archive.guidance`, the upstream shape) merged with the schema's own
|
|
12
|
+
// `archive.guidance` list (the FreeHarness spec-driven override carries the compound step).
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import { parse as parseYaml } from 'yaml';
|
|
16
|
+
import { loadChangeContext, generateInstructions, resolveSchema, resolveSchemaForChange, getSchemaDir, resolveArtifactOutputPath, resolveArtifactOutputs, parseTaskLines, METADATA_FILENAME, } from '../../vendor/openspec/engine/index.js';
|
|
17
|
+
import { planningHomeStub, printJson, validateChangeExists, validateSchemaExists } from './shared.js';
|
|
18
|
+
const SPECIAL_TARGETS = new Set(['apply', 'archive']);
|
|
19
|
+
export function instructionsHandler(projectRoot, artifactId, options) {
|
|
20
|
+
const changeName = validateChangeExists(options.change, projectRoot);
|
|
21
|
+
if (options.schema) {
|
|
22
|
+
validateSchemaExists(options.schema, projectRoot);
|
|
23
|
+
}
|
|
24
|
+
if (artifactId === 'apply') {
|
|
25
|
+
return applyInstructions(projectRoot, changeName, options);
|
|
26
|
+
}
|
|
27
|
+
if (artifactId === 'archive') {
|
|
28
|
+
return archiveInstructions(projectRoot, changeName, options);
|
|
29
|
+
}
|
|
30
|
+
const context = loadChangeContext(projectRoot, changeName, options.schema);
|
|
31
|
+
if (!artifactId) {
|
|
32
|
+
const validIds = context.graph.getAllArtifacts().map((a) => a.id);
|
|
33
|
+
throw new Error(`Missing required argument <artifact>. Valid artifacts:\n ${validIds.join('\n ')}\n apply\n archive`);
|
|
34
|
+
}
|
|
35
|
+
const artifact = context.graph.getArtifact(artifactId);
|
|
36
|
+
if (!artifact) {
|
|
37
|
+
const validIds = context.graph.getAllArtifacts().map((a) => a.id);
|
|
38
|
+
throw new Error(`Artifact '${artifactId}' not found in schema '${context.schemaName}'. Valid artifacts:\n ${validIds.join('\n ')}`);
|
|
39
|
+
}
|
|
40
|
+
const instructions = generateInstructions(context, artifactId);
|
|
41
|
+
const isBlocked = instructions.dependencies.some((d) => !d.done);
|
|
42
|
+
if (options.json) {
|
|
43
|
+
printJson({ ...instructions, planningHome: planningHomeStub(projectRoot) });
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
printInstructionsText(instructions, isBlocked);
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
export function printInstructionsText(instructions, isBlocked) {
|
|
50
|
+
const { artifactId, changeName, schemaName, changeDir, resolvedOutputPath, description, instruction, template, dependencies, unlocks, } = instructions;
|
|
51
|
+
console.log(`<artifact id="${artifactId}" change="${changeName}" schema="${schemaName}">`);
|
|
52
|
+
console.log();
|
|
53
|
+
if (instructions.skipped) {
|
|
54
|
+
console.log('<warning>');
|
|
55
|
+
console.log(instructions.warning ?? 'This artifact is skipped (skip_specs is set in .openspec.yaml).');
|
|
56
|
+
console.log('</warning>');
|
|
57
|
+
console.log();
|
|
58
|
+
console.log('</artifact>');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (isBlocked) {
|
|
62
|
+
const missing = dependencies.filter((d) => !d.done).map((d) => d.id);
|
|
63
|
+
console.log('<warning>');
|
|
64
|
+
console.log('This artifact has unmet dependencies. Complete them first or proceed with caution.');
|
|
65
|
+
console.log(`Missing: ${missing.join(', ')}`);
|
|
66
|
+
console.log('</warning>');
|
|
67
|
+
console.log();
|
|
68
|
+
}
|
|
69
|
+
console.log('<task>');
|
|
70
|
+
console.log(`Create the ${artifactId} artifact for change "${changeName}".`);
|
|
71
|
+
console.log(description);
|
|
72
|
+
console.log('</task>');
|
|
73
|
+
console.log();
|
|
74
|
+
if (dependencies.length > 0) {
|
|
75
|
+
console.log('<dependencies>');
|
|
76
|
+
console.log('Read the current contents of these files before creating this artifact (re-read them from disk even if you saw them earlier - they may have been edited):');
|
|
77
|
+
console.log();
|
|
78
|
+
for (const dep of dependencies) {
|
|
79
|
+
if (dep.skipped) {
|
|
80
|
+
console.log(`<dependency id="${dep.id}" status="skipped">`);
|
|
81
|
+
console.log(' <description>Skipped: the change declares skip_specs, so this artifact has no files to read.</description>');
|
|
82
|
+
console.log('</dependency>');
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const status = dep.done ? 'done' : 'missing';
|
|
86
|
+
const fullPath = path.join(changeDir, dep.path);
|
|
87
|
+
console.log(`<dependency id="${dep.id}" status="${status}">`);
|
|
88
|
+
console.log(` <path>${fullPath}</path>`);
|
|
89
|
+
console.log(` <description>${dep.description}</description>`);
|
|
90
|
+
console.log('</dependency>');
|
|
91
|
+
}
|
|
92
|
+
console.log('</dependencies>');
|
|
93
|
+
console.log();
|
|
94
|
+
}
|
|
95
|
+
console.log('<output>');
|
|
96
|
+
console.log(`Write to: ${resolvedOutputPath}`);
|
|
97
|
+
console.log('</output>');
|
|
98
|
+
console.log();
|
|
99
|
+
if (instruction) {
|
|
100
|
+
console.log('<instruction>');
|
|
101
|
+
console.log(instruction.trim());
|
|
102
|
+
console.log('</instruction>');
|
|
103
|
+
console.log();
|
|
104
|
+
}
|
|
105
|
+
console.log('<template>');
|
|
106
|
+
console.log('<!-- Use this as the structure for your output file. Fill in the sections. -->');
|
|
107
|
+
console.log(template.trim());
|
|
108
|
+
console.log('</template>');
|
|
109
|
+
console.log();
|
|
110
|
+
if (unlocks.length > 0) {
|
|
111
|
+
console.log('<unlocks>');
|
|
112
|
+
console.log(`Completing this artifact enables: ${unlocks.join(', ')}`);
|
|
113
|
+
console.log('</unlocks>');
|
|
114
|
+
console.log();
|
|
115
|
+
}
|
|
116
|
+
console.log('</artifact>');
|
|
117
|
+
}
|
|
118
|
+
function toTaskItems(parsed) {
|
|
119
|
+
const tasks = [];
|
|
120
|
+
for (const task of parsed) {
|
|
121
|
+
if (task.description.length === 0)
|
|
122
|
+
continue;
|
|
123
|
+
tasks.push({ id: `${tasks.length + 1}`, description: task.description, done: task.done });
|
|
124
|
+
}
|
|
125
|
+
return tasks;
|
|
126
|
+
}
|
|
127
|
+
const SPECS_PATH_PREFIX = 'specs/';
|
|
128
|
+
/** Whether an artifact's `generates` path lands inside the specs/ tree. */
|
|
129
|
+
function isSpecsArtifactPath(generates) {
|
|
130
|
+
return generates.replace(/^(?:\.\/)+/, '').startsWith(SPECS_PATH_PREFIX);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Every artifact still to build before apply's own gate is satisfied, in the
|
|
134
|
+
* schema's declared build order - the transitive closure of `apply.requires`.
|
|
135
|
+
*
|
|
136
|
+
* Apply blocks on the first artifact in `requiredArtifactIds` that is not yet
|
|
137
|
+
* complete, so naming only that one hides the rest of the chain: a change
|
|
138
|
+
* holding only a proposal, whose schema chains specs -> design -> tasks,
|
|
139
|
+
* would be told only "Missing artifacts: tasks" - an instruction that, taken
|
|
140
|
+
* literally, skips every artifact tasks itself depends on.
|
|
141
|
+
*/
|
|
142
|
+
/**
|
|
143
|
+
* Every artifact still to build (`missing`), plus a warning for any id in the
|
|
144
|
+
* `requires` chain that names no artifact in the schema. An unknown id used to
|
|
145
|
+
* be dropped by the `if (!artifact) continue` below with no trace anywhere in
|
|
146
|
+
* the instructions payload - a typo in a schema's `requires` list read as an
|
|
147
|
+
* empty, satisfied dependency instead of the configuration error it is.
|
|
148
|
+
*/
|
|
149
|
+
function collectMissingPrerequisites(requiredArtifactIds, schema, buildOrder, completed) {
|
|
150
|
+
const byId = new Map(schema.artifacts.map((artifact) => [artifact.id, artifact]));
|
|
151
|
+
const missing = new Set();
|
|
152
|
+
const unknownIds = [];
|
|
153
|
+
const queue = [...requiredArtifactIds];
|
|
154
|
+
const seen = new Set(queue);
|
|
155
|
+
while (queue.length > 0) {
|
|
156
|
+
const id = queue.shift();
|
|
157
|
+
const artifact = byId.get(id);
|
|
158
|
+
if (!artifact) {
|
|
159
|
+
unknownIds.push(id);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (!completed.has(id))
|
|
163
|
+
missing.add(id);
|
|
164
|
+
for (const dependency of artifact.requires) {
|
|
165
|
+
if (seen.has(dependency))
|
|
166
|
+
continue;
|
|
167
|
+
seen.add(dependency);
|
|
168
|
+
queue.push(dependency);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const order = new Map(buildOrder.map((id, index) => [id, index]));
|
|
172
|
+
// An id absent from `order` (the build order the schema graph produced) is
|
|
173
|
+
// itself a schema defect, not a "comes first" signal - sorting it to the
|
|
174
|
+
// front (the old `?? 0`) silently promoted a broken reference ahead of every
|
|
175
|
+
// real artifact. Pushing it to the end keeps real artifacts in their proper
|
|
176
|
+
// order and leaves the broken one visibly last.
|
|
177
|
+
const missingSorted = [...missing].sort((a, b) => (order.get(a) ?? Number.MAX_SAFE_INTEGER) - (order.get(b) ?? Number.MAX_SAFE_INTEGER));
|
|
178
|
+
const warnings = unknownIds.length > 0
|
|
179
|
+
? [`Schema declares a dependency on an artifact id not defined in this schema: ${unknownIds.join(', ')}.`]
|
|
180
|
+
: [];
|
|
181
|
+
return { missing: missingSorted, warnings };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Non-blocking problems with an otherwise-ready-or-complete change.
|
|
185
|
+
*
|
|
186
|
+
* Apply gates on the schema's `apply.requires` alone, so a change whose tasks
|
|
187
|
+
* file was written ahead of its specs reads as ready even though it has no
|
|
188
|
+
* delta spec at all - the exact state `openspec validate` rejects. Naming the
|
|
189
|
+
* gap here, instead of blocking on it, is what keeps apply from being the one
|
|
190
|
+
* surface that green-lights implementation ahead of every other check.
|
|
191
|
+
*
|
|
192
|
+
* Fires only once apply's own gate is satisfied: for a change still missing a
|
|
193
|
+
* required artifact, the absent specs are the next step, not a warning. A
|
|
194
|
+
* schema with no spec-producing artifact, or a change carrying `skip_specs`,
|
|
195
|
+
* never triggers it.
|
|
196
|
+
*/
|
|
197
|
+
function collectApplyWarnings(state, schema, changeDir, changeName, skippedArtifacts) {
|
|
198
|
+
if (state === 'blocked')
|
|
199
|
+
return [];
|
|
200
|
+
const specArtifacts = schema.artifacts.filter((artifact) => isSpecsArtifactPath(artifact.generates));
|
|
201
|
+
if (specArtifacts.length === 0)
|
|
202
|
+
return [];
|
|
203
|
+
if (specArtifacts.some((artifact) => skippedArtifacts?.has(artifact.id)))
|
|
204
|
+
return [];
|
|
205
|
+
const hasDeltas = specArtifacts.some((artifact) => resolveArtifactOutputs(changeDir, artifact.generates).length > 0);
|
|
206
|
+
if (hasDeltas)
|
|
207
|
+
return [];
|
|
208
|
+
const metadataPath = path.join(changeDir, METADATA_FILENAME);
|
|
209
|
+
// Names the artifact this schema actually declares rather than the literal
|
|
210
|
+
// "specs": a schema whose spec-producing artifact is, say, "contracts" would
|
|
211
|
+
// otherwise be told to run a command naming an artifact it does not have.
|
|
212
|
+
// With more than one such artifact there is no single right answer, so the
|
|
213
|
+
// id becomes a placeholder instead of a guess.
|
|
214
|
+
const specTarget = specArtifacts.length === 1 ? specArtifacts[0].id : '<artifact-id>';
|
|
215
|
+
return [
|
|
216
|
+
`This change has no delta specs and does not declare \`skip_specs: true\`, so \`openspec validate ${changeName}\` fails on it. ` +
|
|
217
|
+
`Write the delta specs before implementing (\`openspec instructions ${specTarget} --change ${changeName}\`), ` +
|
|
218
|
+
`or add \`skip_specs: true\` to ${metadataPath} if this change really changes no specified behavior.`,
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
function buildApplyInstructions(projectRoot, changeName, schemaName) {
|
|
222
|
+
const context = loadChangeContext(projectRoot, changeName, schemaName);
|
|
223
|
+
const changeDir = context.changeDir;
|
|
224
|
+
const schema = resolveSchema(context.schemaName, projectRoot);
|
|
225
|
+
const applyConfig = schema.apply;
|
|
226
|
+
const requiredArtifactIds = applyConfig?.requires ?? schema.artifacts.map((a) => a.id);
|
|
227
|
+
const tracksFile = applyConfig?.tracks ?? null;
|
|
228
|
+
const schemaInstruction = applyConfig?.instruction ?? null;
|
|
229
|
+
const missingArtifacts = [];
|
|
230
|
+
for (const artifactId of requiredArtifactIds) {
|
|
231
|
+
if (context.skippedArtifacts?.has(artifactId))
|
|
232
|
+
continue;
|
|
233
|
+
const artifact = schema.artifacts.find((a) => a.id === artifactId);
|
|
234
|
+
if (artifact && resolveArtifactOutputs(changeDir, artifact.generates).length === 0) {
|
|
235
|
+
missingArtifacts.push(artifactId);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const contextFiles = {};
|
|
239
|
+
for (const artifact of schema.artifacts) {
|
|
240
|
+
const outputs = resolveArtifactOutputs(changeDir, artifact.generates);
|
|
241
|
+
if (outputs.length > 0)
|
|
242
|
+
contextFiles[artifact.id] = outputs;
|
|
243
|
+
}
|
|
244
|
+
let parsedTasks = [];
|
|
245
|
+
let tracksFileExists = false;
|
|
246
|
+
if (tracksFile) {
|
|
247
|
+
const tracksPath = resolveArtifactOutputPath(changeDir, tracksFile);
|
|
248
|
+
tracksFileExists = fs.existsSync(tracksPath);
|
|
249
|
+
if (tracksFileExists) {
|
|
250
|
+
parsedTasks = parseTaskLines(fs.readFileSync(tracksPath, 'utf-8'));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const tasks = toTaskItems(parsedTasks);
|
|
254
|
+
const total = parsedTasks.length;
|
|
255
|
+
const complete = parsedTasks.filter((t) => t.done).length;
|
|
256
|
+
const remaining = total - complete;
|
|
257
|
+
let state;
|
|
258
|
+
let instruction;
|
|
259
|
+
if (missingArtifacts.length > 0) {
|
|
260
|
+
state = 'blocked';
|
|
261
|
+
instruction = `Cannot apply this change yet. Missing artifacts: ${missingArtifacts.join(', ')}.\nUse the openspec-continue-change skill to create the missing artifacts first.`;
|
|
262
|
+
}
|
|
263
|
+
else if (tracksFile && !tracksFileExists) {
|
|
264
|
+
state = 'blocked';
|
|
265
|
+
instruction = `The ${path.basename(tracksFile)} file is missing and must be created.\nUse openspec-continue-change to generate the tracking file.`;
|
|
266
|
+
}
|
|
267
|
+
else if (tracksFile && tracksFileExists && tasks.length === 0) {
|
|
268
|
+
state = 'blocked';
|
|
269
|
+
instruction = `The ${path.basename(tracksFile)} file exists but contains no tasks to work on.\nAdd tasks or regenerate it with openspec-continue-change.`;
|
|
270
|
+
}
|
|
271
|
+
else if (tracksFile && remaining === 0 && total > 0) {
|
|
272
|
+
state = 'all_done';
|
|
273
|
+
instruction = 'All tasks are complete! This change is ready to be archived.';
|
|
274
|
+
}
|
|
275
|
+
else if (!tracksFile) {
|
|
276
|
+
state = 'ready';
|
|
277
|
+
instruction = schemaInstruction?.trim() ?? 'All required artifacts complete. Proceed with implementation.';
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
state = 'ready';
|
|
281
|
+
instruction =
|
|
282
|
+
schemaInstruction?.trim() ??
|
|
283
|
+
'Read context files, work through pending tasks, mark complete as you go.\nPause if you hit blockers or need clarification.';
|
|
284
|
+
}
|
|
285
|
+
// Computed regardless of state: a "ready" change (apply.requires satisfied)
|
|
286
|
+
// can still be missing the artifacts its own required artifact transitively
|
|
287
|
+
// depends on - the schema's own gate does not require them, so this is the
|
|
288
|
+
// only place that names the rest of the chain.
|
|
289
|
+
const { missing: missingPrerequisites, warnings: unknownIdWarnings } = collectMissingPrerequisites(requiredArtifactIds, schema, context.graph.getBuildOrder(), context.completed);
|
|
290
|
+
const warnings = [
|
|
291
|
+
...unknownIdWarnings,
|
|
292
|
+
...collectApplyWarnings(state, schema, changeDir, changeName, context.skippedArtifacts),
|
|
293
|
+
];
|
|
294
|
+
return {
|
|
295
|
+
changeName,
|
|
296
|
+
changeDir,
|
|
297
|
+
schemaName: context.schemaName,
|
|
298
|
+
contextFiles,
|
|
299
|
+
progress: { total, complete, remaining },
|
|
300
|
+
tasks,
|
|
301
|
+
state,
|
|
302
|
+
...(missingArtifacts.length > 0 ? { missingArtifacts } : {}),
|
|
303
|
+
...(missingPrerequisites.length > 0 ? { missingPrerequisites } : {}),
|
|
304
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
305
|
+
instruction,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Whether the "Not created yet, in build order" line would repeat information
|
|
310
|
+
* the "Missing artifacts" line already gave in full.
|
|
311
|
+
*
|
|
312
|
+
* Compared as sets, not lengths: two equal-length lists naming different ids
|
|
313
|
+
* (e.g. a schema whose transitive chain swaps one artifact for another at the
|
|
314
|
+
* same depth) are NOT the same information, and a length-only compare (the
|
|
315
|
+
* prior `missingPrerequisites.length <= missingArtifacts.length`) would
|
|
316
|
+
* silently drop the second list even though it names something new.
|
|
317
|
+
* Exported for direct unit coverage of the comparison rule.
|
|
318
|
+
*/
|
|
319
|
+
export function isChainAlreadyNamed(state, missingArtifacts, missingPrerequisites) {
|
|
320
|
+
if (state !== 'blocked' || missingArtifacts === undefined)
|
|
321
|
+
return false;
|
|
322
|
+
const missingArtifactsSet = new Set(missingArtifacts);
|
|
323
|
+
return (missingPrerequisites ?? []).every((id) => missingArtifactsSet.has(id));
|
|
324
|
+
}
|
|
325
|
+
function applyInstructions(projectRoot, changeName, options) {
|
|
326
|
+
const instructions = buildApplyInstructions(projectRoot, changeName, options.schema);
|
|
327
|
+
if (options.json) {
|
|
328
|
+
printJson({ ...instructions, planningHome: planningHomeStub(projectRoot) });
|
|
329
|
+
return 0;
|
|
330
|
+
}
|
|
331
|
+
console.log(`## Apply: ${instructions.changeName}`);
|
|
332
|
+
console.log(`Schema: ${instructions.schemaName}`);
|
|
333
|
+
console.log();
|
|
334
|
+
if (instructions.state === 'blocked' && instructions.missingArtifacts) {
|
|
335
|
+
console.log('### Blocked');
|
|
336
|
+
console.log(`Missing artifacts: ${instructions.missingArtifacts.join(', ')}`);
|
|
337
|
+
console.log();
|
|
338
|
+
}
|
|
339
|
+
// Named once: when blocked and the chain says nothing past `missingArtifacts`,
|
|
340
|
+
// the line above already names it in full.
|
|
341
|
+
const chainAlreadyNamed = isChainAlreadyNamed(instructions.state, instructions.missingArtifacts, instructions.missingPrerequisites);
|
|
342
|
+
if (instructions.missingPrerequisites && instructions.missingPrerequisites.length > 0 && !chainAlreadyNamed) {
|
|
343
|
+
console.log(`Not created yet, in build order: ${instructions.missingPrerequisites.join(', ')}`);
|
|
344
|
+
console.log();
|
|
345
|
+
}
|
|
346
|
+
if (instructions.warnings && instructions.warnings.length > 0) {
|
|
347
|
+
console.log('### Warnings');
|
|
348
|
+
for (const warning of instructions.warnings) {
|
|
349
|
+
console.log(`- ${warning}`);
|
|
350
|
+
}
|
|
351
|
+
console.log();
|
|
352
|
+
}
|
|
353
|
+
const contextEntries = Object.entries(instructions.contextFiles);
|
|
354
|
+
if (contextEntries.length > 0) {
|
|
355
|
+
console.log('### Context Files');
|
|
356
|
+
for (const [id, files] of contextEntries) {
|
|
357
|
+
for (const file of files)
|
|
358
|
+
console.log(`- ${id}: ${file}`);
|
|
359
|
+
}
|
|
360
|
+
console.log();
|
|
361
|
+
}
|
|
362
|
+
if (instructions.progress.total > 0 || instructions.tasks.length > 0) {
|
|
363
|
+
console.log('### Progress');
|
|
364
|
+
console.log(`${instructions.progress.complete}/${instructions.progress.total} complete`);
|
|
365
|
+
console.log();
|
|
366
|
+
}
|
|
367
|
+
if (instructions.tasks.length > 0) {
|
|
368
|
+
console.log('### Tasks');
|
|
369
|
+
for (const task of instructions.tasks) {
|
|
370
|
+
console.log(`- ${task.done ? '[x]' : '[ ]'} ${task.description}`);
|
|
371
|
+
}
|
|
372
|
+
console.log();
|
|
373
|
+
}
|
|
374
|
+
console.log('### Instruction');
|
|
375
|
+
console.log(instructions.instruction);
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Reads a string list at `keys` inside a parsed YAML document; non-lists yield [].
|
|
380
|
+
* @param doc Parsed document.
|
|
381
|
+
* @param keys Path of keys.
|
|
382
|
+
* @returns The strings found.
|
|
383
|
+
*/
|
|
384
|
+
function stringList(doc, keys) {
|
|
385
|
+
let cur = doc;
|
|
386
|
+
for (const k of keys) {
|
|
387
|
+
if (!cur || typeof cur !== 'object' || Array.isArray(cur))
|
|
388
|
+
return [];
|
|
389
|
+
cur = cur[k];
|
|
390
|
+
}
|
|
391
|
+
return Array.isArray(cur) ? cur.filter((x) => typeof x === 'string' && x.trim().length > 0) : [];
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Parses a YAML file when present; a missing or malformed file yields undefined.
|
|
395
|
+
* @param file Path.
|
|
396
|
+
* @returns Parsed document.
|
|
397
|
+
*/
|
|
398
|
+
function readYamlIfPresent(file) {
|
|
399
|
+
if (!fs.existsSync(file))
|
|
400
|
+
return undefined;
|
|
401
|
+
try {
|
|
402
|
+
return parseYaml(fs.readFileSync(file, 'utf8'));
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Builds the archive inputs: project context (`openspec/config.yaml` `context`), the
|
|
410
|
+
* project's `operations.archive.guidance`, then the schema's `archive.guidance`.
|
|
411
|
+
* @param projectRoot Project root.
|
|
412
|
+
* @param changeName Change id.
|
|
413
|
+
* @param explicitSchema `--schema` override.
|
|
414
|
+
* @returns The brief the archive skill prints.
|
|
415
|
+
*/
|
|
416
|
+
export function buildArchiveInstructions(projectRoot, changeName, explicitSchema) {
|
|
417
|
+
const config = readYamlIfPresent(path.join(projectRoot, 'openspec', 'config.yaml'));
|
|
418
|
+
const contextRaw = config && typeof config === 'object' ? config.context : undefined;
|
|
419
|
+
const context = typeof contextRaw === 'string' && contextRaw.trim().length > 0 ? contextRaw : undefined;
|
|
420
|
+
const guidance = stringList(config, ['operations', 'archive', 'guidance']);
|
|
421
|
+
const changeDir = path.join(projectRoot, 'openspec', 'changes', changeName);
|
|
422
|
+
const schemaName = resolveSchemaForChange(changeDir, explicitSchema, projectRoot);
|
|
423
|
+
const schemaDir = getSchemaDir(schemaName, projectRoot);
|
|
424
|
+
if (schemaDir) {
|
|
425
|
+
for (const g of stringList(readYamlIfPresent(path.join(schemaDir, 'schema.yaml')), ['archive', 'guidance'])) {
|
|
426
|
+
if (!guidance.includes(g))
|
|
427
|
+
guidance.push(g);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
changeName,
|
|
432
|
+
...(context !== undefined ? { context } : {}),
|
|
433
|
+
...(guidance.length > 0 ? { operationGuidance: guidance } : {}),
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function archiveInstructions(projectRoot, changeName, options) {
|
|
437
|
+
const payload = buildArchiveInstructions(projectRoot, changeName, options.schema);
|
|
438
|
+
if (options.json) {
|
|
439
|
+
printJson({ ...payload, planningHome: planningHomeStub(projectRoot) });
|
|
440
|
+
return 0;
|
|
441
|
+
}
|
|
442
|
+
console.log(`## Archive Inputs: ${changeName}`);
|
|
443
|
+
console.log();
|
|
444
|
+
if (payload.context) {
|
|
445
|
+
console.log('### Project Context (required instruction input)');
|
|
446
|
+
console.log(payload.context);
|
|
447
|
+
console.log();
|
|
448
|
+
}
|
|
449
|
+
if (payload.operationGuidance) {
|
|
450
|
+
console.log('### Operation Guidance (advisory)');
|
|
451
|
+
for (const g of payload.operationGuidance)
|
|
452
|
+
console.log(`- ${g}`);
|
|
453
|
+
console.log();
|
|
454
|
+
}
|
|
455
|
+
if (!payload.context && !payload.operationGuidance) {
|
|
456
|
+
console.log('No project context or operation guidance configured.');
|
|
457
|
+
}
|
|
458
|
+
return 0;
|
|
459
|
+
}
|
|
460
|
+
export { SPECIAL_TARGETS };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// `openspec list [--json]` over the vendored engine.
|
|
2
|
+
//
|
|
3
|
+
// Reconstructed from src/core/list.ts (changes mode), minus store/root-selection.
|
|
4
|
+
// JSON shape matches upstream: { changes: [{ name, completedTasks, totalTasks,
|
|
5
|
+
// lastModified, status }] }, sorted most-recently-modified first by default.
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import { getTaskProgressForChange, formatTaskStatus } from '../../vendor/openspec/engine/index.js';
|
|
9
|
+
import { changesDirFor, printableId, printJson } from './shared.js';
|
|
10
|
+
/** Most recent mtime of any file under a directory (recursive). */
|
|
11
|
+
function getLastModified(dirPath) {
|
|
12
|
+
let latest = null;
|
|
13
|
+
const walk = (dir) => {
|
|
14
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
15
|
+
const full = path.join(dir, entry.name);
|
|
16
|
+
if (entry.isDirectory()) {
|
|
17
|
+
walk(full);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
const stat = fs.statSync(full);
|
|
21
|
+
if (latest === null || stat.mtime > latest)
|
|
22
|
+
latest = stat.mtime;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
walk(dirPath);
|
|
27
|
+
return latest ?? fs.statSync(dirPath).mtime;
|
|
28
|
+
}
|
|
29
|
+
export async function listHandler(projectRoot, options) {
|
|
30
|
+
const { json = false, sort = 'recent' } = options;
|
|
31
|
+
const changesDir = changesDirFor(projectRoot);
|
|
32
|
+
let entries;
|
|
33
|
+
try {
|
|
34
|
+
entries = fs.readdirSync(changesDir, { withFileTypes: true });
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.code === 'ENOENT')
|
|
38
|
+
entries = [];
|
|
39
|
+
else
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
const changeDirs = entries
|
|
43
|
+
.filter((e) => e.isDirectory() && e.name !== 'archive' && !e.name.startsWith('.'))
|
|
44
|
+
.map((e) => e.name);
|
|
45
|
+
if (changeDirs.length === 0) {
|
|
46
|
+
if (json)
|
|
47
|
+
printJson({ changes: [] });
|
|
48
|
+
else
|
|
49
|
+
console.log('No active changes found.');
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
const changes = [];
|
|
53
|
+
for (const name of changeDirs) {
|
|
54
|
+
const progress = await getTaskProgressForChange(changesDir, name, projectRoot);
|
|
55
|
+
changes.push({
|
|
56
|
+
name,
|
|
57
|
+
completedTasks: progress.completed,
|
|
58
|
+
totalTasks: progress.total,
|
|
59
|
+
openTasks: progress.open,
|
|
60
|
+
dispositionedTasks: progress.dispositioned,
|
|
61
|
+
lastModified: getLastModified(path.join(changesDir, name)),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (sort === 'recent') {
|
|
65
|
+
changes.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
changes.sort((a, b) => a.name.localeCompare(b.name));
|
|
69
|
+
}
|
|
70
|
+
if (json) {
|
|
71
|
+
printJson({
|
|
72
|
+
changes: changes.map((c) => ({
|
|
73
|
+
name: c.name,
|
|
74
|
+
completedTasks: c.completedTasks,
|
|
75
|
+
totalTasks: c.totalTasks,
|
|
76
|
+
lastModified: c.lastModified.toISOString(),
|
|
77
|
+
status: c.totalTasks === 0 ? 'no-tasks' : c.openTasks === 0 ? 'complete' : 'in-progress',
|
|
78
|
+
})),
|
|
79
|
+
});
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
console.log('Changes:');
|
|
83
|
+
const names = new Map(changes.map((c) => [c.name, printableId(c.name)]));
|
|
84
|
+
const nameWidth = Math.max(...[...names.values()].map((n) => n.length));
|
|
85
|
+
for (const change of changes) {
|
|
86
|
+
const status = formatTaskStatus({
|
|
87
|
+
total: change.totalTasks,
|
|
88
|
+
completed: change.completedTasks,
|
|
89
|
+
open: change.openTasks,
|
|
90
|
+
dispositioned: change.dispositionedTasks,
|
|
91
|
+
});
|
|
92
|
+
console.log(` ${names.get(change.name).padEnd(nameWidth)} ${status}`);
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|