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,19 @@
|
|
|
1
|
+
import type { PromptPort, SelectItem } from "@freeharness/core";
|
|
2
|
+
export declare class ReadlinePrompt implements PromptPort {
|
|
3
|
+
confirm(message: string): Promise<boolean>;
|
|
4
|
+
/**
|
|
5
|
+
* Prints the items numbered and reads a comma-separated list of numbers or ids.
|
|
6
|
+
* @param message Prompt line printed above the list.
|
|
7
|
+
* @param items Selectable items.
|
|
8
|
+
* @returns The selected ids in list order; empty on no TTY, empty input or `none`.
|
|
9
|
+
*/
|
|
10
|
+
select(message: string, items: SelectItem[]): Promise<string[]>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolves a typed selection (`1,3`, `context7,github`, `all`) to item ids, ignoring
|
|
14
|
+
* unknown entries.
|
|
15
|
+
* @param answer Raw input.
|
|
16
|
+
* @param items Selectable items.
|
|
17
|
+
* @returns Selected ids in list order, deduplicated.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseSelection(answer: string, items: SelectItem[]): string[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// PromptPort adapter: a dependency-free y/N confirmation and a numbered multi-select over
|
|
2
|
+
// stdin. When stdin is not a TTY and --yes / --select were not passed, confirm defaults to
|
|
3
|
+
// No and select to nothing (safe: nothing runs, nothing is enabled).
|
|
4
|
+
import readline from "node:readline";
|
|
5
|
+
export class ReadlinePrompt {
|
|
6
|
+
async confirm(message) {
|
|
7
|
+
if (!process.stdin.isTTY)
|
|
8
|
+
return false;
|
|
9
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
10
|
+
const answer = await new Promise((resolve) => rl.question(`${message} [y/N] `, resolve));
|
|
11
|
+
rl.close();
|
|
12
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Prints the items numbered and reads a comma-separated list of numbers or ids.
|
|
16
|
+
* @param message Prompt line printed above the list.
|
|
17
|
+
* @param items Selectable items.
|
|
18
|
+
* @returns The selected ids in list order; empty on no TTY, empty input or `none`.
|
|
19
|
+
*/
|
|
20
|
+
async select(message, items) {
|
|
21
|
+
if (!process.stdin.isTTY || items.length === 0)
|
|
22
|
+
return [];
|
|
23
|
+
console.log(message);
|
|
24
|
+
items.forEach((it, i) => console.log(` ${String(i + 1).padStart(2)}) ${it.id.padEnd(14)} ${it.label}`));
|
|
25
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
26
|
+
const answer = await new Promise((resolve) => rl.question("Select (numbers or ids, comma-separated; empty = none): ", resolve));
|
|
27
|
+
rl.close();
|
|
28
|
+
return parseSelection(answer, items);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a typed selection (`1,3`, `context7,github`, `all`) to item ids, ignoring
|
|
33
|
+
* unknown entries.
|
|
34
|
+
* @param answer Raw input.
|
|
35
|
+
* @param items Selectable items.
|
|
36
|
+
* @returns Selected ids in list order, deduplicated.
|
|
37
|
+
*/
|
|
38
|
+
export function parseSelection(answer, items) {
|
|
39
|
+
const tokens = answer.split(",").map((t) => t.trim()).filter(Boolean);
|
|
40
|
+
if (tokens.length === 0 || tokens.some((t) => /^none$/i.test(t)))
|
|
41
|
+
return [];
|
|
42
|
+
if (tokens.some((t) => /^all$/i.test(t)))
|
|
43
|
+
return items.map((i) => i.id);
|
|
44
|
+
const chosen = new Set();
|
|
45
|
+
for (const t of tokens) {
|
|
46
|
+
const n = parseInt(t, 10);
|
|
47
|
+
if (Number.isFinite(n) && /^\d+$/.test(t) && items[n - 1])
|
|
48
|
+
chosen.add(items[n - 1].id);
|
|
49
|
+
else if (items.some((i) => i.id === t))
|
|
50
|
+
chosen.add(t);
|
|
51
|
+
}
|
|
52
|
+
return items.map((i) => i.id).filter((id) => chosen.has(id));
|
|
53
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AdoptOptions, AdoptReport, SyncPort, SyncReport } from "@freeharness/core";
|
|
2
|
+
/**
|
|
3
|
+
* Renders a unified diff (3 lines of context) between two texts.
|
|
4
|
+
* @param oldText Vendored content ("" when missing).
|
|
5
|
+
* @param newText Upstream content ("" when missing).
|
|
6
|
+
* @param oldName Header name for the vendored side.
|
|
7
|
+
* @param newName Header name for the upstream side.
|
|
8
|
+
* @returns Unified diff text; a one-line summary when the inputs are too large.
|
|
9
|
+
*/
|
|
10
|
+
export declare function unifiedDiff(oldText: string, newText: string, oldName: string, newName: string): string;
|
|
11
|
+
/** Read-only upstream check + explicit path-scoped adoption over the same tracked manifest. */
|
|
12
|
+
export declare class DiffSync implements SyncPort {
|
|
13
|
+
private readonly canonicalDir;
|
|
14
|
+
constructor(canonicalDir: string);
|
|
15
|
+
private resolve;
|
|
16
|
+
check(target: string): Promise<SyncReport>;
|
|
17
|
+
/**
|
|
18
|
+
* Adopts upstream versions of the tracked paths (never engine code).
|
|
19
|
+
* Dry run: builds the previews and the would-be report, writes nothing.
|
|
20
|
+
* Write: copies the selected entries, then bumps the PROVENANCE baseline to the fork HEAD.
|
|
21
|
+
* @param target sdd | memory.
|
|
22
|
+
* @param options Label filter, dry-run, force-divergent.
|
|
23
|
+
* @returns The adoption report.
|
|
24
|
+
*/
|
|
25
|
+
adopt(target: string, options: AdoptOptions): Promise<AdoptReport>;
|
|
26
|
+
}
|
|
27
|
+
/** PROVENANCE baseline vs the upstream fork checkout, for `doctor`. */
|
|
28
|
+
export interface BaselineStatus {
|
|
29
|
+
target: string;
|
|
30
|
+
baseline: string;
|
|
31
|
+
fork: string;
|
|
32
|
+
forkFound: boolean;
|
|
33
|
+
/** Fork HEAD (short 8) when the fork is a git checkout. */
|
|
34
|
+
head?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reports where a vendored engine's PROVENANCE baseline stands against the fork HEAD,
|
|
38
|
+
* without comparing files (that is `sync check`).
|
|
39
|
+
* @param canonicalDir Resolved canonical directory (locates the repo and PROVENANCE).
|
|
40
|
+
* @param target sdd | memory.
|
|
41
|
+
* @returns The baseline status; `head` undefined when the fork is missing or not git.
|
|
42
|
+
*/
|
|
43
|
+
export declare function baselineStatus(canonicalDir: string, target: string): BaselineStatus;
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
// SyncPort adapter: report how our vendored trees differ from the current upstream
|
|
2
|
+
// fork checkout, limited to the vendored paths, flagging stable-contract changes
|
|
3
|
+
// (`check`, read-only), and adopt the upstream versions of those tracked paths on
|
|
4
|
+
// explicit request (`adopt`: preview -> copy -> PROVENANCE baseline bump). Engine code
|
|
5
|
+
// is never touched by either: it is a manual, build-verified port.
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { UPSTREAM_PLUGIN_NAME } from "../memory/vendored.js";
|
|
10
|
+
// packages/cli/canonical -> up 3 = the FreeHarness repo root.
|
|
11
|
+
function repoRoot(canonicalDir) {
|
|
12
|
+
return path.resolve(canonicalDir, "..", "..", "..");
|
|
13
|
+
}
|
|
14
|
+
// Fork checkout: env override, else a sibling of the FreeHarness repo.
|
|
15
|
+
function forkDir(target, repo) {
|
|
16
|
+
const env = process.env[`FREEHARNESS_UPSTREAM_${target.toUpperCase()}`];
|
|
17
|
+
if (env)
|
|
18
|
+
return env;
|
|
19
|
+
const sibling = target === "sdd" ? "OpenSpec" : UPSTREAM_PLUGIN_NAME;
|
|
20
|
+
return path.resolve(repo, "..", sibling);
|
|
21
|
+
}
|
|
22
|
+
function baselineFrom(provenance) {
|
|
23
|
+
if (!fs.existsSync(provenance))
|
|
24
|
+
return "unknown";
|
|
25
|
+
const m = /baseline_commit:\s*"?([0-9a-f]+)"?/i.exec(fs.readFileSync(provenance, "utf8"));
|
|
26
|
+
return m ? m[1] : "unknown";
|
|
27
|
+
}
|
|
28
|
+
function listFiles(dir) {
|
|
29
|
+
const out = [];
|
|
30
|
+
const walk = (rel) => {
|
|
31
|
+
const abs = path.join(dir, rel);
|
|
32
|
+
if (!fs.existsSync(abs))
|
|
33
|
+
return;
|
|
34
|
+
for (const e of fs.readdirSync(abs, { withFileTypes: true })) {
|
|
35
|
+
if (e.name === ".DS_Store")
|
|
36
|
+
continue;
|
|
37
|
+
const child = path.join(rel, e.name);
|
|
38
|
+
if (e.isDirectory())
|
|
39
|
+
walk(child);
|
|
40
|
+
else
|
|
41
|
+
out.push(child);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
walk("");
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function isDir(p) {
|
|
48
|
+
return fs.existsSync(p) && fs.statSync(p).isDirectory();
|
|
49
|
+
}
|
|
50
|
+
// Compare a file or a directory; return the sub-paths that differ (or are missing).
|
|
51
|
+
function compare(entry) {
|
|
52
|
+
if (isDir(entry.vendored)) {
|
|
53
|
+
const rels = new Set([...listFiles(entry.vendored), ...listFiles(entry.upstream)]);
|
|
54
|
+
const diffs = [];
|
|
55
|
+
for (const rel of rels) {
|
|
56
|
+
const a = path.join(entry.vendored, rel);
|
|
57
|
+
const b = path.join(entry.upstream, rel);
|
|
58
|
+
if (!fs.existsSync(a) || !fs.existsSync(b) || fs.readFileSync(a, "utf8") !== fs.readFileSync(b, "utf8")) {
|
|
59
|
+
diffs.push(`${entry.label}/${rel}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return diffs;
|
|
63
|
+
}
|
|
64
|
+
if (!fs.existsSync(entry.vendored) || !fs.existsSync(entry.upstream))
|
|
65
|
+
return [entry.label];
|
|
66
|
+
return fs.readFileSync(entry.vendored, "utf8") !== fs.readFileSync(entry.upstream, "utf8") ? [entry.label] : [];
|
|
67
|
+
}
|
|
68
|
+
function provenanceFile(target, repo) {
|
|
69
|
+
return path.join(repo, target === "sdd" ? "packages/sdd/PROVENANCE.yaml" : "packages/memory/PROVENANCE.yaml");
|
|
70
|
+
}
|
|
71
|
+
function manifest(target, repo, fork) {
|
|
72
|
+
if (target === "sdd") {
|
|
73
|
+
const v = path.join(repo, "packages/sdd/vendor/openspec");
|
|
74
|
+
return [
|
|
75
|
+
{ label: "schema.yaml", vendored: path.join(v, "assets/schema.upstream.yaml"), upstream: path.join(fork, "schemas/spec-driven/schema.yaml"), contract: true },
|
|
76
|
+
{ label: "skills", vendored: path.join(v, "assets/skills"), upstream: path.join(fork, "skills"), contract: true },
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
if (target === "memory") {
|
|
80
|
+
const v = path.join(repo, "packages/memory/vendor/claude-mem");
|
|
81
|
+
return [
|
|
82
|
+
{
|
|
83
|
+
label: "plugin/hooks/hooks.json", vendored: path.join(v, "plugin/hooks/hooks.json"), upstream: path.join(fork, "plugin/hooks/hooks.json"), contract: true,
|
|
84
|
+
divergent: "rewritten to a deterministic ${CLAUDE_PLUGIN_ROOT} command (memory-slim); compare upstream events/matchers/verbs by hand",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
label: "plugin/.mcp.json", vendored: path.join(v, "plugin/.mcp.json"), upstream: path.join(fork, "plugin/.mcp.json"), contract: true,
|
|
88
|
+
divergent: "rewritten to spawn ${CLAUDE_PLUGIN_ROOT}/scripts/mcp-server.cjs directly (memory-slim)",
|
|
89
|
+
},
|
|
90
|
+
{ label: "mem-search/SKILL.md", vendored: path.join(v, "plugin/skills/mem-search/SKILL.md"), upstream: path.join(fork, "plugin/skills/mem-search/SKILL.md"), contract: true },
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
throw new Error(`Unknown sync target: ${target}. Valid: sdd, memory.`);
|
|
94
|
+
}
|
|
95
|
+
// ---- unified diff (in-process, dependency-free; files here are small text contracts) ----
|
|
96
|
+
/** Above this many line-pairs the LCS table is skipped and the preview is a size summary. */
|
|
97
|
+
const MAX_DIFF_CELLS = 4_000_000;
|
|
98
|
+
const CONTEXT = 3;
|
|
99
|
+
/**
|
|
100
|
+
* Computes an edit script between two line arrays via a classic LCS table.
|
|
101
|
+
* @param a Old lines.
|
|
102
|
+
* @param b New lines.
|
|
103
|
+
* @returns Ops in order (` ` equal, `-` removed, `+` added).
|
|
104
|
+
*/
|
|
105
|
+
function lineOps(a, b) {
|
|
106
|
+
const n = a.length;
|
|
107
|
+
const m = b.length;
|
|
108
|
+
// lcs[i][j] = LCS length of a[i..] and b[j..]; flat Uint32 to keep it cheap.
|
|
109
|
+
const w = m + 1;
|
|
110
|
+
const lcs = new Uint32Array((n + 1) * w);
|
|
111
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
112
|
+
for (let j = m - 1; j >= 0; j--) {
|
|
113
|
+
lcs[i * w + j] = a[i] === b[j] ? lcs[(i + 1) * w + j + 1] + 1 : Math.max(lcs[(i + 1) * w + j], lcs[i * w + j + 1]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const ops = [];
|
|
117
|
+
let i = 0;
|
|
118
|
+
let j = 0;
|
|
119
|
+
while (i < n && j < m) {
|
|
120
|
+
if (a[i] === b[j]) {
|
|
121
|
+
ops.push({ kind: " ", line: a[i] });
|
|
122
|
+
i++;
|
|
123
|
+
j++;
|
|
124
|
+
}
|
|
125
|
+
else if (lcs[(i + 1) * w + j] >= lcs[i * w + j + 1]) {
|
|
126
|
+
ops.push({ kind: "-", line: a[i] });
|
|
127
|
+
i++;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
ops.push({ kind: "+", line: b[j] });
|
|
131
|
+
j++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
while (i < n)
|
|
135
|
+
ops.push({ kind: "-", line: a[i++] });
|
|
136
|
+
while (j < m)
|
|
137
|
+
ops.push({ kind: "+", line: b[j++] });
|
|
138
|
+
return ops;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Renders a unified diff (3 lines of context) between two texts.
|
|
142
|
+
* @param oldText Vendored content ("" when missing).
|
|
143
|
+
* @param newText Upstream content ("" when missing).
|
|
144
|
+
* @param oldName Header name for the vendored side.
|
|
145
|
+
* @param newName Header name for the upstream side.
|
|
146
|
+
* @returns Unified diff text; a one-line summary when the inputs are too large.
|
|
147
|
+
*/
|
|
148
|
+
export function unifiedDiff(oldText, newText, oldName, newName) {
|
|
149
|
+
const lines = (t) => (t === "" ? [] : t.replace(/\n$/, "").split("\n"));
|
|
150
|
+
const a = lines(oldText);
|
|
151
|
+
const b = lines(newText);
|
|
152
|
+
const header = `--- ${oldName}\n+++ ${newName}\n`;
|
|
153
|
+
if (a.length * b.length > MAX_DIFF_CELLS)
|
|
154
|
+
return `${header}@@ large file: ${a.length} -> ${b.length} lines (diff omitted; compare by hand) @@\n`;
|
|
155
|
+
const ops = lineOps(a, b);
|
|
156
|
+
// Annotate each op with its old/new line numbers, then keep the ops within CONTEXT of a change.
|
|
157
|
+
const oldNo = [];
|
|
158
|
+
const newNo = [];
|
|
159
|
+
let ol = 1;
|
|
160
|
+
let nl = 1;
|
|
161
|
+
for (const op of ops) {
|
|
162
|
+
oldNo.push(ol);
|
|
163
|
+
newNo.push(nl);
|
|
164
|
+
if (op.kind !== "+")
|
|
165
|
+
ol++;
|
|
166
|
+
if (op.kind !== "-")
|
|
167
|
+
nl++;
|
|
168
|
+
}
|
|
169
|
+
const keep = new Array(ops.length).fill(false);
|
|
170
|
+
for (let k = 0; k < ops.length; k++) {
|
|
171
|
+
if (ops[k].kind === " ")
|
|
172
|
+
continue;
|
|
173
|
+
for (let c = Math.max(0, k - CONTEXT); c <= Math.min(ops.length - 1, k + CONTEXT); c++)
|
|
174
|
+
keep[c] = true;
|
|
175
|
+
}
|
|
176
|
+
const out = [];
|
|
177
|
+
let k = 0;
|
|
178
|
+
while (k < ops.length) {
|
|
179
|
+
if (!keep[k]) {
|
|
180
|
+
k++;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const start = k;
|
|
184
|
+
while (k < ops.length && keep[k])
|
|
185
|
+
k++;
|
|
186
|
+
const hunk = ops.slice(start, k);
|
|
187
|
+
const oldCount = hunk.filter((o) => o.kind !== "+").length;
|
|
188
|
+
const newCount = hunk.filter((o) => o.kind !== "-").length;
|
|
189
|
+
const oldStart = oldCount === 0 ? oldNo[start] - 1 : oldNo[start];
|
|
190
|
+
const newStart = newCount === 0 ? newNo[start] - 1 : newNo[start];
|
|
191
|
+
out.push(`@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`);
|
|
192
|
+
for (const op of hunk)
|
|
193
|
+
out.push(`${op.kind}${op.line}`);
|
|
194
|
+
}
|
|
195
|
+
return header + out.join("\n") + (out.length > 0 ? "\n" : "");
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Builds the previews for one tracked entry: one per differing file (dirs) or one for the file.
|
|
199
|
+
* @param entry Manifest entry.
|
|
200
|
+
* @returns Previews with `added` / `removed` markers for one-sided files.
|
|
201
|
+
*/
|
|
202
|
+
function previewsFor(entry) {
|
|
203
|
+
const one = (label, vendored, upstream) => {
|
|
204
|
+
const vExists = fs.existsSync(vendored);
|
|
205
|
+
const uExists = fs.existsSync(upstream);
|
|
206
|
+
const marker = !vExists ? " (added upstream)" : !uExists ? " (removed upstream — vendored copy will be deleted)" : "";
|
|
207
|
+
const diff = unifiedDiff(vExists ? fs.readFileSync(vendored, "utf8") : "", uExists ? fs.readFileSync(upstream, "utf8") : "", `vendored/${label}`, `upstream/${label}`);
|
|
208
|
+
return { label: `${label}${marker}`, diff };
|
|
209
|
+
};
|
|
210
|
+
if (isDir(entry.vendored) || isDir(entry.upstream)) {
|
|
211
|
+
return compare(entry).sort().map((full) => {
|
|
212
|
+
const rel = full.slice(entry.label.length + 1);
|
|
213
|
+
return one(full, path.join(entry.vendored, rel), path.join(entry.upstream, rel));
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return compare(entry).length > 0 ? [one(entry.label, entry.vendored, entry.upstream)] : [];
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Copies one entry from upstream over the vendored tree (dir: per file, deleting vendored
|
|
220
|
+
* files that no longer exist upstream). A single file missing upstream is left in place.
|
|
221
|
+
* @param entry Manifest entry.
|
|
222
|
+
* @returns A warning when the upstream side is missing, else undefined.
|
|
223
|
+
*/
|
|
224
|
+
function copyEntry(entry) {
|
|
225
|
+
if (isDir(entry.upstream)) {
|
|
226
|
+
const upstreamFiles = new Set(listFiles(entry.upstream));
|
|
227
|
+
for (const rel of listFiles(entry.vendored)) {
|
|
228
|
+
if (!upstreamFiles.has(rel))
|
|
229
|
+
fs.rmSync(path.join(entry.vendored, rel), { force: true });
|
|
230
|
+
}
|
|
231
|
+
for (const rel of upstreamFiles) {
|
|
232
|
+
const dest = path.join(entry.vendored, rel);
|
|
233
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
234
|
+
fs.copyFileSync(path.join(entry.upstream, rel), dest);
|
|
235
|
+
}
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
if (!fs.existsSync(entry.upstream))
|
|
239
|
+
return `${entry.label}: removed upstream; vendored copy kept (delete by hand if intended)`;
|
|
240
|
+
fs.mkdirSync(path.dirname(entry.vendored), { recursive: true });
|
|
241
|
+
fs.copyFileSync(entry.upstream, entry.vendored);
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Reads the fork's HEAD (short 8) and package.json version, when it is a git checkout.
|
|
246
|
+
* @param fork Fork checkout dir.
|
|
247
|
+
* @returns `{commit, version}`; commit undefined when not a git checkout.
|
|
248
|
+
*/
|
|
249
|
+
function forkHead(fork) {
|
|
250
|
+
const r = spawnSync("git", ["-C", fork, "rev-parse", "--short=8", "HEAD"], { encoding: "utf8" });
|
|
251
|
+
const commit = r.status === 0 ? r.stdout.trim() : undefined;
|
|
252
|
+
let version;
|
|
253
|
+
const pkg = path.join(fork, "package.json");
|
|
254
|
+
if (fs.existsSync(pkg)) {
|
|
255
|
+
try {
|
|
256
|
+
const v = JSON.parse(fs.readFileSync(pkg, "utf8")).version;
|
|
257
|
+
if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v))
|
|
258
|
+
version = v;
|
|
259
|
+
}
|
|
260
|
+
catch { /* not a package.json we can read; keep the version */ }
|
|
261
|
+
}
|
|
262
|
+
return { commit: commit && /^[0-9a-f]{7,40}$/.test(commit) ? commit : undefined, version };
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Rewrites only the `baseline_commit` (and `baseline_version`) values in PROVENANCE.yaml,
|
|
266
|
+
* preserving the rest of the file byte-for-byte.
|
|
267
|
+
* @param file PROVENANCE.yaml path.
|
|
268
|
+
* @param commit New baseline commit.
|
|
269
|
+
* @param version New baseline version (optional).
|
|
270
|
+
*/
|
|
271
|
+
function bumpBaseline(file, commit, version) {
|
|
272
|
+
let text = fs.readFileSync(file, "utf8");
|
|
273
|
+
text = text.replace(/^(\s*baseline_commit:\s*)"?[0-9a-f]+"?/im, `$1"${commit}"`);
|
|
274
|
+
if (version)
|
|
275
|
+
text = text.replace(/^(\s*baseline_version:\s*)"?[^"\n]+"?/im, `$1"${version}"`);
|
|
276
|
+
fs.writeFileSync(file, text, "utf8");
|
|
277
|
+
}
|
|
278
|
+
/** Read-only upstream check + explicit path-scoped adoption over the same tracked manifest. */
|
|
279
|
+
export class DiffSync {
|
|
280
|
+
canonicalDir;
|
|
281
|
+
constructor(canonicalDir) {
|
|
282
|
+
this.canonicalDir = canonicalDir;
|
|
283
|
+
}
|
|
284
|
+
resolve(target) {
|
|
285
|
+
const repo = repoRoot(this.canonicalDir);
|
|
286
|
+
const fork = forkDir(target, repo);
|
|
287
|
+
const entries = manifest(target, repo, fork); // unknown target errors before the fork lookup
|
|
288
|
+
if (!fs.existsSync(fork)) {
|
|
289
|
+
throw new Error(`Upstream fork not found for '${target}' at ${fork}. Set FREEHARNESS_UPSTREAM_${target.toUpperCase()} to the fork checkout.`);
|
|
290
|
+
}
|
|
291
|
+
return { repo, fork, entries, provenance: provenanceFile(target, repo) };
|
|
292
|
+
}
|
|
293
|
+
async check(target) {
|
|
294
|
+
const { entries, provenance } = this.resolve(target);
|
|
295
|
+
const baseline = baselineFrom(provenance);
|
|
296
|
+
const changedPaths = [];
|
|
297
|
+
const contractAffected = [];
|
|
298
|
+
const divergent = {};
|
|
299
|
+
for (const entry of entries) {
|
|
300
|
+
const diffs = compare(entry);
|
|
301
|
+
for (const d of diffs) {
|
|
302
|
+
changedPaths.push(d);
|
|
303
|
+
if (entry.divergent)
|
|
304
|
+
divergent[d] = entry.divergent;
|
|
305
|
+
else if (entry.contract)
|
|
306
|
+
contractAffected.push(d);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return { target, baseline, changedPaths, contractAffected, divergent };
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Adopts upstream versions of the tracked paths (never engine code).
|
|
313
|
+
* Dry run: builds the previews and the would-be report, writes nothing.
|
|
314
|
+
* Write: copies the selected entries, then bumps the PROVENANCE baseline to the fork HEAD.
|
|
315
|
+
* @param target sdd | memory.
|
|
316
|
+
* @param options Label filter, dry-run, force-divergent.
|
|
317
|
+
* @returns The adoption report.
|
|
318
|
+
*/
|
|
319
|
+
async adopt(target, options) {
|
|
320
|
+
const { fork, entries, provenance } = this.resolve(target);
|
|
321
|
+
const known = entries.map((e) => e.label);
|
|
322
|
+
for (const p of options.paths ?? []) {
|
|
323
|
+
if (!known.includes(p))
|
|
324
|
+
throw new Error(`Unknown tracked path '${p}' for '${target}'. Tracked: ${known.join(", ")}`);
|
|
325
|
+
}
|
|
326
|
+
const selected = entries.filter((e) => !options.paths || options.paths.includes(e.label));
|
|
327
|
+
const baselineBefore = baselineFrom(provenance);
|
|
328
|
+
const warnings = [];
|
|
329
|
+
const adopted = [];
|
|
330
|
+
const skippedDivergent = [];
|
|
331
|
+
const previews = [];
|
|
332
|
+
const toCopy = [];
|
|
333
|
+
for (const entry of selected) {
|
|
334
|
+
if (compare(entry).length === 0)
|
|
335
|
+
continue;
|
|
336
|
+
if (entry.divergent && !options.forceDivergent) {
|
|
337
|
+
skippedDivergent.push(entry.label);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
previews.push(...previewsFor(entry));
|
|
341
|
+
adopted.push(entry.label);
|
|
342
|
+
toCopy.push(entry);
|
|
343
|
+
}
|
|
344
|
+
const head = forkHead(fork);
|
|
345
|
+
let baselineAfter = baselineBefore;
|
|
346
|
+
if (!head.commit)
|
|
347
|
+
warnings.push(`fork at ${fork} is not a git checkout; PROVENANCE baseline kept at ${baselineBefore}`);
|
|
348
|
+
else
|
|
349
|
+
baselineAfter = head.commit;
|
|
350
|
+
// Dry run reports the would-be baseline; nothing-to-adopt writes nothing (baseline kept).
|
|
351
|
+
if (options.dryRun)
|
|
352
|
+
return { target, adopted, skippedDivergent, baselineBefore, baselineAfter, previews, warnings };
|
|
353
|
+
if (adopted.length === 0)
|
|
354
|
+
return { target, adopted, skippedDivergent, baselineBefore, baselineAfter: baselineBefore, previews, warnings };
|
|
355
|
+
for (const entry of toCopy) {
|
|
356
|
+
const w = copyEntry(entry);
|
|
357
|
+
if (w)
|
|
358
|
+
warnings.push(w);
|
|
359
|
+
}
|
|
360
|
+
if (head.commit && fs.existsSync(provenance))
|
|
361
|
+
bumpBaseline(provenance, head.commit, head.version);
|
|
362
|
+
else if (head.commit)
|
|
363
|
+
warnings.push(`${provenance} not found; baseline not recorded`);
|
|
364
|
+
return { target, adopted, skippedDivergent, baselineBefore, baselineAfter, previews, warnings };
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Reports where a vendored engine's PROVENANCE baseline stands against the fork HEAD,
|
|
369
|
+
* without comparing files (that is `sync check`).
|
|
370
|
+
* @param canonicalDir Resolved canonical directory (locates the repo and PROVENANCE).
|
|
371
|
+
* @param target sdd | memory.
|
|
372
|
+
* @returns The baseline status; `head` undefined when the fork is missing or not git.
|
|
373
|
+
*/
|
|
374
|
+
export function baselineStatus(canonicalDir, target) {
|
|
375
|
+
const repo = repoRoot(canonicalDir);
|
|
376
|
+
const fork = forkDir(target, repo);
|
|
377
|
+
const baseline = baselineFrom(provenanceFile(target, repo));
|
|
378
|
+
const forkFound = fs.existsSync(fork);
|
|
379
|
+
return { target, baseline, fork, forkFound, head: forkFound ? forkHead(fork).commit : undefined };
|
|
380
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Scope } from "@freeharness/core";
|
|
2
|
+
import { UPSTREAM_PLUGIN_NAME } from "../adapters/memory/vendored.js";
|
|
3
|
+
import { type InstallManifest } from "../infra/manifest.js";
|
|
4
|
+
import { type MemoryDiagnosis } from "../adapters/memory/doctor.js";
|
|
5
|
+
/** The vendored `openspec` bin replaces it. */
|
|
6
|
+
export declare const LEGACY_OPENSPEC_PACKAGE = "@fission-ai/openspec";
|
|
7
|
+
/** A global link of it shadows the ~/.local/bin symlink. */
|
|
8
|
+
export declare const CLI_PACKAGE = "freeharness";
|
|
9
|
+
/** Backups confirm separately. */
|
|
10
|
+
export type CleanupGroup = "memory" | "backups" | "legacy" | "duplicates" | "npm" | "retired";
|
|
11
|
+
/** Retired by a rename or a fold, bare names under `~/.claude`. */
|
|
12
|
+
export declare const RETIRED: {
|
|
13
|
+
readonly skills: readonly ["local-code-intel"];
|
|
14
|
+
readonly agents: readonly ["mle-reviewer", "mle-evaluator"];
|
|
15
|
+
};
|
|
16
|
+
/** One retired name found on disk, with its manifest-recorded flag. */
|
|
17
|
+
export interface RetiredFinding {
|
|
18
|
+
kind: "skill" | "agent";
|
|
19
|
+
name: string;
|
|
20
|
+
/** Skill dir or agent file. */
|
|
21
|
+
path: string;
|
|
22
|
+
/** Manifest lists `path` (agent) or a file under it (skill dir). */
|
|
23
|
+
recorded: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Whose entries a `hooks` item prunes. */
|
|
26
|
+
export type HookOwner = "freeharness" | typeof UPSTREAM_PLUGIN_NAME;
|
|
27
|
+
/** One leftover the cleanup knows how to remove. */
|
|
28
|
+
export interface CleanupItem {
|
|
29
|
+
kind: "dir" | "file" | "hooks" | "npm-global" | "settings-key";
|
|
30
|
+
/** Filesystem path, or the package name for `npm-global`. */
|
|
31
|
+
target: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
group: CleanupGroup;
|
|
34
|
+
/** Dirs only. */
|
|
35
|
+
bytes?: number;
|
|
36
|
+
/** Set when detected but not removable now; the reason why. */
|
|
37
|
+
refused?: string;
|
|
38
|
+
/** `hooks` items: whose entries are pruned (default: FreeHarness). */
|
|
39
|
+
hookOwner?: HookOwner;
|
|
40
|
+
/** `npm-global` items: try `npm unlink -g` first, then `npm uninstall -g`. */
|
|
41
|
+
unlink?: boolean;
|
|
42
|
+
/** `settings-key` items: the `enabledPlugins` key removed from `target`. */
|
|
43
|
+
settingsKey?: string;
|
|
44
|
+
}
|
|
45
|
+
/** What a cleanup pass would (or did) do. */
|
|
46
|
+
export interface CleanupPlan {
|
|
47
|
+
scope: Scope;
|
|
48
|
+
items: CleanupItem[];
|
|
49
|
+
/** Informational lines: seen, but out of the fixed list. */
|
|
50
|
+
notes: string[];
|
|
51
|
+
}
|
|
52
|
+
/** Outcome of `applyCleanup`. */
|
|
53
|
+
export interface CleanupResult {
|
|
54
|
+
removed: string[];
|
|
55
|
+
/** Refused, or not confirmed. */
|
|
56
|
+
skipped: string[];
|
|
57
|
+
/** Non-fatal failures (an npm uninstall that exited non-zero). */
|
|
58
|
+
warnings: string[];
|
|
59
|
+
}
|
|
60
|
+
/** Optional dependencies, injectable for tests. */
|
|
61
|
+
export interface CleanupDeps {
|
|
62
|
+
/** Reuse: doctor already computed one. */
|
|
63
|
+
memory?: MemoryDiagnosis;
|
|
64
|
+
/** Names the agents/skills a project may duplicate. */
|
|
65
|
+
canonicalDir?: string;
|
|
66
|
+
}
|
|
67
|
+
/** Sums the size (bytes) of every file under a directory. */
|
|
68
|
+
export declare function dirBytes(dir: string): number;
|
|
69
|
+
/** Renders a byte count for the preview (e.g. "12.3 MB"). */
|
|
70
|
+
export declare function humanBytes(n: number): string;
|
|
71
|
+
/** Top-level packages of the global npm install: name -> version ("?" when unreported), null when npm is unavailable. */
|
|
72
|
+
export declare function globalNpmPackages(): Record<string, string> | null;
|
|
73
|
+
/** The `<npm prefix -g>/bin/freeharness` shadowing ours on PATH (an old `npm link -g`); undefined when absent, identical to ours, or npm is unavailable. */
|
|
74
|
+
export declare function globalNpmFreeharnessBin(): string | undefined;
|
|
75
|
+
/** Claude-mem data backups written by `freeharness memory backup` (`<dataDir>-backup-<stamp>`), oldest first. */
|
|
76
|
+
export declare function memoryBackupDirs(): string[];
|
|
77
|
+
/** Drops install-manifest entries whose file no longer exists (else every removal is "drift"); 0 without a manifest. */
|
|
78
|
+
export declare function pruneManifest(scope: Scope, projectRoot: string): number;
|
|
79
|
+
/** Retired skill/agent names still present under a `.claude` dir: `update`'s additive merge never deletes them; an absent path is skipped. */
|
|
80
|
+
export declare function retiredFindings(claudeDir: string, manifest: InstallManifest | undefined): RetiredFinding[];
|
|
81
|
+
/** Builds the cleanup plan for a scope without touching anything; `items` is empty when there is nothing to do. */
|
|
82
|
+
export declare function planCleanup(scope: Scope, projectRoot: string, deps?: CleanupDeps): Promise<CleanupPlan>;
|
|
83
|
+
/** Removes the confirmed items of a plan (`confirmed` defaults to all); refused items are always skipped. */
|
|
84
|
+
export declare function applyCleanup(plan: CleanupPlan, confirmed?: (item: CleanupItem) => boolean): CleanupResult;
|
|
85
|
+
/** Renders a plan item for the preview / doctor output, one line, without the OK/WARN prefix. */
|
|
86
|
+
export declare function describeItem(item: CleanupItem): string;
|