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,150 @@
|
|
|
1
|
+
// `openspec new change <id> [--schema <name>] [--json]` — store-free.
|
|
2
|
+
//
|
|
3
|
+
// Reconstructed from src/commands/workflow/new-change.ts + src/utils/change-utils.ts,
|
|
4
|
+
// minus store/root-selection and the project-config schema fallback. Schema
|
|
5
|
+
// resolution is: explicit --schema -> 'spec-driven'. Creating a change also
|
|
6
|
+
// scaffolds the surrounding root (specs/, changes/archive/, config.yaml) exactly
|
|
7
|
+
// like upstream, so a fresh openspec/ is left healthy. JSON shape matches
|
|
8
|
+
// upstream: { change: { id, path, metadataPath, schema } }.
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import { writeChangeMetadata, METADATA_FILENAME, countTasksFromContent, } from '../../vendor/openspec/engine/index.js';
|
|
12
|
+
import { getAvailableChanges, planningHomeStub, printableId, printJson, validateSchemaExists } from './shared.js';
|
|
13
|
+
const DEFAULT_WIP_CAP = 1;
|
|
14
|
+
const WIP_CAP_FILE = '.freeharness/sdd.json';
|
|
15
|
+
/**
|
|
16
|
+
* Reads `wip_cap` from `.freeharness/sdd.json` at the project root: absent file
|
|
17
|
+
* means the default silently; an unreadable or unparseable file, or a `wip_cap`
|
|
18
|
+
* that is not a positive integer, means the default plus one INFO line naming
|
|
19
|
+
* the file. Read synchronously with a try/catch, never `require` or `import`.
|
|
20
|
+
*/
|
|
21
|
+
function readWipCap(projectRoot) {
|
|
22
|
+
let raw;
|
|
23
|
+
try {
|
|
24
|
+
raw = fs.readFileSync(path.join(projectRoot, WIP_CAP_FILE), 'utf-8');
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
const code = error.code;
|
|
28
|
+
if (code === 'ENOENT' || code === 'ENOTDIR')
|
|
29
|
+
return { cap: DEFAULT_WIP_CAP, info: null };
|
|
30
|
+
return { cap: DEFAULT_WIP_CAP, info: `INFO ${WIP_CAP_FILE}: wip_cap invalid, using ${DEFAULT_WIP_CAP}` };
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
const cap = parsed.wip_cap;
|
|
35
|
+
if (typeof cap === 'number' && Number.isInteger(cap) && cap > 0) {
|
|
36
|
+
return { cap, info: null };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// falls through to the malformed-default below
|
|
41
|
+
}
|
|
42
|
+
return { cap: DEFAULT_WIP_CAP, info: `INFO ${WIP_CAP_FILE}: wip_cap invalid, using ${DEFAULT_WIP_CAP}` };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* In-flight change ids (excluding archive) whose tasks.md has an open task
|
|
46
|
+
* (unchecked, no disposition) or has no tasks.md at all.
|
|
47
|
+
*/
|
|
48
|
+
function inFlightChangeIds(projectRoot) {
|
|
49
|
+
const changesDir = path.join(projectRoot, 'openspec', 'changes');
|
|
50
|
+
const ids = [];
|
|
51
|
+
for (const id of getAvailableChanges(projectRoot)) {
|
|
52
|
+
const tasksPath = path.join(changesDir, id, 'tasks.md');
|
|
53
|
+
let hasOpenTasks = true;
|
|
54
|
+
if (fs.existsSync(tasksPath)) {
|
|
55
|
+
hasOpenTasks = countTasksFromContent(fs.readFileSync(tasksPath, 'utf-8')).open > 0;
|
|
56
|
+
}
|
|
57
|
+
if (hasOpenTasks)
|
|
58
|
+
ids.push(id);
|
|
59
|
+
}
|
|
60
|
+
return ids;
|
|
61
|
+
}
|
|
62
|
+
const DEFAULT_SCHEMA = 'spec-driven';
|
|
63
|
+
const KEBAB_ID_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
64
|
+
function validateChangeName(name) {
|
|
65
|
+
if (!name)
|
|
66
|
+
throw new Error('Change name cannot be empty');
|
|
67
|
+
if (name.length > 200)
|
|
68
|
+
throw new Error('Change name is too long (200 characters max)');
|
|
69
|
+
if (!KEBAB_ID_REGEX.test(name)) {
|
|
70
|
+
if (/[A-Z]/.test(name))
|
|
71
|
+
throw new Error('Change name must be lowercase (use kebab-case)');
|
|
72
|
+
if (/\s/.test(name))
|
|
73
|
+
throw new Error('Change name cannot contain spaces (use hyphens instead)');
|
|
74
|
+
if (/_/.test(name))
|
|
75
|
+
throw new Error('Change name cannot contain underscores (use hyphens instead)');
|
|
76
|
+
if (name.startsWith('-'))
|
|
77
|
+
throw new Error('Change name cannot start with a hyphen');
|
|
78
|
+
if (name.endsWith('-'))
|
|
79
|
+
throw new Error('Change name cannot end with a hyphen');
|
|
80
|
+
if (/--/.test(name))
|
|
81
|
+
throw new Error('Change name cannot contain consecutive hyphens');
|
|
82
|
+
throw new Error('Change name must follow kebab-case convention (e.g., add-auth, refactor-db)');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function formatLocalDate(date = new Date()) {
|
|
86
|
+
const year = date.getFullYear();
|
|
87
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
88
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
89
|
+
return `${year}-${month}-${day}`;
|
|
90
|
+
}
|
|
91
|
+
export function newChangeHandler(projectRoot, name, options) {
|
|
92
|
+
if (!name)
|
|
93
|
+
throw new Error('Missing required argument <name>');
|
|
94
|
+
validateChangeName(name);
|
|
95
|
+
const schemaName = options.schema ?? DEFAULT_SCHEMA;
|
|
96
|
+
validateSchemaExists(schemaName, projectRoot);
|
|
97
|
+
const openspecDir = path.join(projectRoot, 'openspec');
|
|
98
|
+
const changeDir = path.join(openspecDir, 'changes', name);
|
|
99
|
+
if (fs.existsSync(changeDir)) {
|
|
100
|
+
throw new Error(`Change '${name}' already exists at ${changeDir}`);
|
|
101
|
+
}
|
|
102
|
+
// WIP advisory: read before the new change directory exists, so it is never
|
|
103
|
+
// counted among the in-flight changes it might be advised against joining.
|
|
104
|
+
// A count of zero prints nothing, not even the malformed-config INFO line: the advisory has
|
|
105
|
+
// nothing to say.
|
|
106
|
+
const inFlight = inFlightChangeIds(projectRoot);
|
|
107
|
+
const wipCap = inFlight.length > 0 ? readWipCap(projectRoot) : { cap: DEFAULT_WIP_CAP, info: null };
|
|
108
|
+
fs.mkdirSync(changeDir, { recursive: true });
|
|
109
|
+
fs.mkdirSync(path.join(openspecDir, 'specs'), { recursive: true });
|
|
110
|
+
fs.mkdirSync(path.join(openspecDir, 'changes', 'archive'), { recursive: true });
|
|
111
|
+
const configPath = path.join(openspecDir, 'config.yaml');
|
|
112
|
+
const configYmlPath = path.join(openspecDir, 'config.yml');
|
|
113
|
+
if (!fs.existsSync(configPath) && !fs.existsSync(configYmlPath)) {
|
|
114
|
+
fs.writeFileSync(configPath, `schema: ${DEFAULT_SCHEMA}\n`, 'utf-8');
|
|
115
|
+
}
|
|
116
|
+
writeChangeMetadata(changeDir, { schema: schemaName, created: formatLocalDate() }, projectRoot);
|
|
117
|
+
if (options.description) {
|
|
118
|
+
fs.writeFileSync(path.join(changeDir, 'README.md'), `# ${name}\n\n${options.description}\n`, 'utf-8');
|
|
119
|
+
}
|
|
120
|
+
const wipAdvisory = inFlight.length >= wipCap.cap
|
|
121
|
+
? { count: inFlight.length, cap: wipCap.cap, changes: inFlight.map(printableId) }
|
|
122
|
+
: null;
|
|
123
|
+
const notices = wipCap.info ? [wipCap.info] : [];
|
|
124
|
+
const payload = {
|
|
125
|
+
change: {
|
|
126
|
+
id: name,
|
|
127
|
+
path: changeDir,
|
|
128
|
+
metadataPath: path.join(changeDir, METADATA_FILENAME),
|
|
129
|
+
schema: schemaName,
|
|
130
|
+
},
|
|
131
|
+
planningHome: planningHomeStub(projectRoot),
|
|
132
|
+
changeRoot: changeDir,
|
|
133
|
+
wip_advisory: wipAdvisory,
|
|
134
|
+
notices,
|
|
135
|
+
};
|
|
136
|
+
if (options.json) {
|
|
137
|
+
printJson(payload);
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
console.log(`Created change '${name}' at ${changeDir}/`);
|
|
141
|
+
console.log(`Schema: ${schemaName}`);
|
|
142
|
+
console.log(`Next: openspec status --change ${name}`);
|
|
143
|
+
for (const notice of notices)
|
|
144
|
+
console.log(notice);
|
|
145
|
+
if (wipAdvisory) {
|
|
146
|
+
console.log(`WIP advisory: ${wipAdvisory.count} change(s) in flight (cap ${wipAdvisory.cap}): ${wipAdvisory.changes.join(', ')}; ` +
|
|
147
|
+
'finish or archive before opening another');
|
|
148
|
+
}
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// `openspec schemas --json` over the vendored engine.
|
|
2
|
+
//
|
|
3
|
+
// Reconstructed from src/commands/workflow/schemas.ts, minus store/root-selection
|
|
4
|
+
// and chalk. JSON output is the SchemaInfo[] array upstream emits verbatim.
|
|
5
|
+
import { listSchemasWithInfo } from '../../vendor/openspec/engine/index.js';
|
|
6
|
+
import { printJson } from './shared.js';
|
|
7
|
+
export function schemasHandler(projectRoot, options) {
|
|
8
|
+
const schemas = listSchemasWithInfo(projectRoot);
|
|
9
|
+
if (options.json) {
|
|
10
|
+
printJson(schemas);
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
console.log('Available schemas:');
|
|
14
|
+
console.log();
|
|
15
|
+
for (const schema of schemas) {
|
|
16
|
+
const sourceLabel = schema.source === 'project' ? ' (project)' : schema.source === 'user' ? ' (user override)' : '';
|
|
17
|
+
console.log(` ${schema.name}${sourceLabel}`);
|
|
18
|
+
console.log(` ${schema.description}`);
|
|
19
|
+
console.log(` Artifacts: ${schema.artifacts.join(' -> ')}`);
|
|
20
|
+
console.log();
|
|
21
|
+
}
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Emits a value as pretty JSON on stdout (the single-document `--json` contract). */
|
|
2
|
+
export declare function printJson(payload: unknown): void;
|
|
3
|
+
/**
|
|
4
|
+
* A task's numbering label for a message (the leading `N.N` of its
|
|
5
|
+
* description), falling back to its line number when the description carries
|
|
6
|
+
* none. Shared by every reader that names a task in an issue or a blocker.
|
|
7
|
+
*/
|
|
8
|
+
export declare function taskLabel(task: {
|
|
9
|
+
description: string;
|
|
10
|
+
line: number;
|
|
11
|
+
}): string;
|
|
12
|
+
/** The changes directory for a project root. */
|
|
13
|
+
export declare function changesDirFor(projectRoot: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Store-free stand-in for upstream's `planningHome` (#1703, OpenSpec 1.12).
|
|
16
|
+
* Upstream resolves the planning home against a registered store; this slice
|
|
17
|
+
* has no store, so the planning home is always the local repository root. The
|
|
18
|
+
* /opsx skills compose main-spec paths from `planningHome.root` and archive
|
|
19
|
+
* paths from `planningHome.changesDir`, so both are carried.
|
|
20
|
+
*/
|
|
21
|
+
export interface PlanningHomeStub {
|
|
22
|
+
kind: 'local';
|
|
23
|
+
root: string;
|
|
24
|
+
changesDir: string;
|
|
25
|
+
defaultSchema: string;
|
|
26
|
+
}
|
|
27
|
+
/** Builds the planningHome stub for a resolved project root. */
|
|
28
|
+
export declare function planningHomeStub(projectRoot: string): PlanningHomeStub;
|
|
29
|
+
/**
|
|
30
|
+
* Lists active change directory names under openspec/changes/, excluding the
|
|
31
|
+
* archive directory and hidden entries.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getAvailableChanges(projectRoot: string): string[];
|
|
34
|
+
/**
|
|
35
|
+
* A change id as a command prints it: a directory name is data, so a control character in it
|
|
36
|
+
* can never break the printed line in two or reach the agent reading the output as a line of
|
|
37
|
+
* its own.
|
|
38
|
+
*/
|
|
39
|
+
export declare function printableId(id: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Resolves a `depends_on` / `supersedes` change id against the project: `"in-flight"`
|
|
42
|
+
* when it names a directory under `openspec/changes/`, `"archived"` when it names one
|
|
43
|
+
* under `openspec/changes/archive/`, else `null`. An id with a path separator, a `..`
|
|
44
|
+
* segment or the reserved `archive` name never resolves, so a list entry cannot escape
|
|
45
|
+
* either tree or alias the archive directory itself. The entry is stat'd without following
|
|
46
|
+
* links, so a symlink planted in the changes tree is not a change and a writer such as
|
|
47
|
+
* `follow-up add` never lands at its target.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveChangeId(projectRoot: string, id: string): 'in-flight' | 'archived' | null;
|
|
50
|
+
/**
|
|
51
|
+
* Validates that a change exists; throws with the available-changes list when
|
|
52
|
+
* it does not. Mirrors upstream `validateChangeExists`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function validateChangeExists(changeName: string | undefined, projectRoot: string): string;
|
|
55
|
+
/** Validates that a schema resolves; throws with the available list otherwise. */
|
|
56
|
+
export declare function validateSchemaExists(schemaName: string, projectRoot?: string): string;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Shared helpers for the store-free `openspec` CLI shim.
|
|
2
|
+
//
|
|
3
|
+
// These reimplement, over the vendored engine, the small validation/discovery
|
|
4
|
+
// helpers the upstream `src/commands/workflow/shared.ts` provided — minus the
|
|
5
|
+
// store/root-selection coupling (root resolution is the shim's job, see
|
|
6
|
+
// src/root-resolver.ts).
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { getSchemaDir, listSchemas } from '../../vendor/openspec/engine/index.js';
|
|
10
|
+
/** Emits a value as pretty JSON on stdout (the single-document `--json` contract). */
|
|
11
|
+
export function printJson(payload) {
|
|
12
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
13
|
+
}
|
|
14
|
+
const TASK_ID = /^(\d+(?:\.\d+)+(?:[A-Za-z]+)?)(?=\s|$)/;
|
|
15
|
+
/**
|
|
16
|
+
* A task's numbering label for a message (the leading `N.N` of its
|
|
17
|
+
* description), falling back to its line number when the description carries
|
|
18
|
+
* none. Shared by every reader that names a task in an issue or a blocker.
|
|
19
|
+
*/
|
|
20
|
+
export function taskLabel(task) {
|
|
21
|
+
return task.description.match(TASK_ID)?.[1] ?? `line ${task.line}`;
|
|
22
|
+
}
|
|
23
|
+
/** The changes directory for a project root. */
|
|
24
|
+
export function changesDirFor(projectRoot) {
|
|
25
|
+
return path.join(projectRoot, 'openspec', 'changes');
|
|
26
|
+
}
|
|
27
|
+
/** Builds the planningHome stub for a resolved project root. */
|
|
28
|
+
export function planningHomeStub(projectRoot) {
|
|
29
|
+
return {
|
|
30
|
+
kind: 'local',
|
|
31
|
+
root: projectRoot,
|
|
32
|
+
changesDir: changesDirFor(projectRoot),
|
|
33
|
+
defaultSchema: 'spec-driven',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Lists active change directory names under openspec/changes/, excluding the
|
|
38
|
+
* archive directory and hidden entries.
|
|
39
|
+
*/
|
|
40
|
+
export function getAvailableChanges(projectRoot) {
|
|
41
|
+
const dir = changesDirFor(projectRoot);
|
|
42
|
+
try {
|
|
43
|
+
return fs
|
|
44
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
45
|
+
.filter((e) => e.isDirectory() && e.name !== 'archive' && !e.name.startsWith('.'))
|
|
46
|
+
.map((e) => e.name);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error.code === 'ENOENT')
|
|
50
|
+
return [];
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A change id as a command prints it: a directory name is data, so a control character in it
|
|
56
|
+
* can never break the printed line in two or reach the agent reading the output as a line of
|
|
57
|
+
* its own.
|
|
58
|
+
*/
|
|
59
|
+
export function printableId(id) {
|
|
60
|
+
return id.replace(/[\x00-\x1f\x7f]/g, '');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolves a `depends_on` / `supersedes` change id against the project: `"in-flight"`
|
|
64
|
+
* when it names a directory under `openspec/changes/`, `"archived"` when it names one
|
|
65
|
+
* under `openspec/changes/archive/`, else `null`. An id with a path separator, a `..`
|
|
66
|
+
* segment or the reserved `archive` name never resolves, so a list entry cannot escape
|
|
67
|
+
* either tree or alias the archive directory itself. The entry is stat'd without following
|
|
68
|
+
* links, so a symlink planted in the changes tree is not a change and a writer such as
|
|
69
|
+
* `follow-up add` never lands at its target.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveChangeId(projectRoot, id) {
|
|
72
|
+
if (!id || id === '.' || id === '..' || id === 'archive' || id.includes('/') || id.includes('\\') || id.includes('\0')) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const changesDir = changesDirFor(projectRoot);
|
|
76
|
+
if (isRealDirectory(path.join(changesDir, id)))
|
|
77
|
+
return 'in-flight';
|
|
78
|
+
if (isRealDirectory(path.join(changesDir, 'archive', id)))
|
|
79
|
+
return 'archived';
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/** True for a directory that is not a symlink; false for anything else, including an absent path. */
|
|
83
|
+
function isRealDirectory(entry) {
|
|
84
|
+
try {
|
|
85
|
+
return fs.lstatSync(entry).isDirectory();
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Rejects change names that would escape the changes directory. */
|
|
92
|
+
function validateChangeLookupName(changeName) {
|
|
93
|
+
if (changeName === '.' || changeName === '..')
|
|
94
|
+
return 'Change name cannot be a relative path segment';
|
|
95
|
+
if (changeName.includes('/') || changeName.includes('\\'))
|
|
96
|
+
return 'Change name cannot contain path separators';
|
|
97
|
+
if (changeName.includes('\0'))
|
|
98
|
+
return 'Change name cannot contain null characters';
|
|
99
|
+
if (changeName.startsWith('.'))
|
|
100
|
+
return 'Change name cannot start with a dot';
|
|
101
|
+
if (changeName === 'archive')
|
|
102
|
+
return "'archive' is reserved for archived changes";
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Validates that a change exists; throws with the available-changes list when
|
|
107
|
+
* it does not. Mirrors upstream `validateChangeExists`.
|
|
108
|
+
*/
|
|
109
|
+
export function validateChangeExists(changeName, projectRoot) {
|
|
110
|
+
if (!changeName) {
|
|
111
|
+
const available = getAvailableChanges(projectRoot);
|
|
112
|
+
if (available.length === 0) {
|
|
113
|
+
throw new Error('No changes found. Create one with: openspec new change <name>');
|
|
114
|
+
}
|
|
115
|
+
throw new Error(`Missing required option --change. Available changes:\n ${available.join('\n ')}`);
|
|
116
|
+
}
|
|
117
|
+
const lookupError = validateChangeLookupName(changeName);
|
|
118
|
+
if (lookupError) {
|
|
119
|
+
throw new Error(`Invalid change name '${changeName}': ${lookupError}`);
|
|
120
|
+
}
|
|
121
|
+
const changePath = path.join(changesDirFor(projectRoot), changeName);
|
|
122
|
+
const exists = fs.existsSync(changePath) && fs.statSync(changePath).isDirectory();
|
|
123
|
+
if (!exists) {
|
|
124
|
+
const available = getAvailableChanges(projectRoot);
|
|
125
|
+
if (available.length === 0) {
|
|
126
|
+
throw new Error(`Change '${changeName}' not found. No changes exist. Create one with: openspec new change <name>`);
|
|
127
|
+
}
|
|
128
|
+
throw new Error(`Change '${changeName}' not found. Available changes:\n ${available.join('\n ')}`);
|
|
129
|
+
}
|
|
130
|
+
return changeName;
|
|
131
|
+
}
|
|
132
|
+
/** Validates that a schema resolves; throws with the available list otherwise. */
|
|
133
|
+
export function validateSchemaExists(schemaName, projectRoot) {
|
|
134
|
+
const schemaDir = getSchemaDir(schemaName, projectRoot);
|
|
135
|
+
if (!schemaDir) {
|
|
136
|
+
const available = listSchemas(projectRoot);
|
|
137
|
+
throw new Error(`Schema '${schemaName}' not found. Available schemas:\n ${available.join('\n ')}`);
|
|
138
|
+
}
|
|
139
|
+
return schemaName;
|
|
140
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ChangeStatus } from '../../vendor/openspec/engine/index.js';
|
|
2
|
+
export interface StatusOptions {
|
|
3
|
+
change?: string;
|
|
4
|
+
schema?: string;
|
|
5
|
+
json?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function statusHandler(projectRoot: string, options: StatusOptions): Promise<number>;
|
|
8
|
+
export declare function printStatusText(status: ChangeStatus): void;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// `openspec status --change <id> [--json]` over the vendored engine.
|
|
2
|
+
//
|
|
3
|
+
// Reconstructed from src/commands/workflow/status.ts, minus store/root-selection
|
|
4
|
+
// and the ora spinner. The JSON shape preserves the fields the /opsx skills read
|
|
5
|
+
// (artifacts[].status, artifacts[].requires, applyRequires, isPlanningComplete /
|
|
6
|
+
// isComplete). The store-only `root` field is omitted; `planningHome` is the
|
|
7
|
+
// store-free stub from shared.ts (V4-P4, #1703).
|
|
8
|
+
import { loadChangeContext, formatChangeStatus, getTaskProgressForChange, } from '../../vendor/openspec/engine/index.js';
|
|
9
|
+
import { changesDirFor, getAvailableChanges, planningHomeStub, printJson, validateChangeExists, validateSchemaExists, } from './shared.js';
|
|
10
|
+
export async function statusHandler(projectRoot, options) {
|
|
11
|
+
if (!options.change) {
|
|
12
|
+
const available = getAvailableChanges(projectRoot);
|
|
13
|
+
if (available.length === 0) {
|
|
14
|
+
if (options.json) {
|
|
15
|
+
printJson({ changes: [], message: 'No active changes.' });
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
console.log('No active changes. Create one with: openspec new change <name>');
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Missing required option --change. Available changes:\n ${available.join('\n ')}`);
|
|
22
|
+
}
|
|
23
|
+
const changeName = validateChangeExists(options.change, projectRoot);
|
|
24
|
+
if (options.schema) {
|
|
25
|
+
validateSchemaExists(options.schema, projectRoot);
|
|
26
|
+
}
|
|
27
|
+
const context = loadChangeContext(projectRoot, changeName, options.schema);
|
|
28
|
+
const status = formatChangeStatus(context);
|
|
29
|
+
// Task-level counts (done/open/dispositioned) are orthogonal to the planning
|
|
30
|
+
// artifacts' status above: a dispositioned task is resolved without being
|
|
31
|
+
// checked, so the tasks artifact is complete for a reader's purposes exactly
|
|
32
|
+
// when `open` reaches zero.
|
|
33
|
+
const taskProgress = await getTaskProgressForChange(changesDirFor(projectRoot), changeName, projectRoot);
|
|
34
|
+
if (options.json) {
|
|
35
|
+
printJson({
|
|
36
|
+
...status,
|
|
37
|
+
done: taskProgress.done,
|
|
38
|
+
open: taskProgress.open,
|
|
39
|
+
dispositioned: taskProgress.dispositioned,
|
|
40
|
+
planningHome: planningHomeStub(projectRoot),
|
|
41
|
+
});
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
printStatusText(status);
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
function indicator(status) {
|
|
48
|
+
switch (status) {
|
|
49
|
+
case 'done':
|
|
50
|
+
return '[x]';
|
|
51
|
+
case 'skipped':
|
|
52
|
+
return '[~]';
|
|
53
|
+
case 'ready':
|
|
54
|
+
return '[ ]';
|
|
55
|
+
case 'blocked':
|
|
56
|
+
return '[-]';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function printStatusText(status) {
|
|
60
|
+
const doneCount = status.artifacts.filter((a) => a.status === 'done').length;
|
|
61
|
+
const skippedCount = status.artifacts.filter((a) => a.status === 'skipped').length;
|
|
62
|
+
const total = status.artifacts.length - skippedCount;
|
|
63
|
+
console.log(`Change: ${status.changeName}`);
|
|
64
|
+
console.log(`Schema: ${status.schemaName}`);
|
|
65
|
+
if (status.changeRoot) {
|
|
66
|
+
console.log(`Change root: ${status.changeRoot}`);
|
|
67
|
+
}
|
|
68
|
+
const skippedSuffix = skippedCount > 0 ? ` (${skippedCount} skipped)` : '';
|
|
69
|
+
console.log(`Progress: ${doneCount}/${total} artifacts complete${skippedSuffix}`);
|
|
70
|
+
console.log();
|
|
71
|
+
for (const artifact of status.artifacts) {
|
|
72
|
+
let line = `${indicator(artifact.status)} ${artifact.id}`;
|
|
73
|
+
if (artifact.status === 'skipped') {
|
|
74
|
+
line += ' (skipped: change declares skip_specs)';
|
|
75
|
+
}
|
|
76
|
+
if (artifact.status === 'blocked' && artifact.missingDeps && artifact.missingDeps.length > 0) {
|
|
77
|
+
line += ` (blocked by: ${artifact.missingDeps.join(', ')})`;
|
|
78
|
+
}
|
|
79
|
+
console.log(line);
|
|
80
|
+
}
|
|
81
|
+
if (status.isPlanningComplete) {
|
|
82
|
+
console.log();
|
|
83
|
+
console.log('All planning artifacts complete!');
|
|
84
|
+
}
|
|
85
|
+
}
|