harness-everything 0.3.6 → 0.4.0
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/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +8 -7
- package/.github/workflows/ci.yml +23 -18
- package/.github/workflows/release.yml +53 -27
- package/.releaserc.json +113 -0
- package/AGENTS.md +34 -23
- package/BENCHMARK_SOP.md +38 -18
- package/CHANGELOG.md +194 -3
- package/CONTRIBUTING.md +17 -4
- package/PRIVACY.md +25 -0
- package/README.md +118 -87
- package/RELEASING.md +82 -0
- package/SUPPORT.md +27 -0
- package/TERMS.md +21 -0
- package/VERIFICATION.md +175 -286
- package/behavioral-evals/README.md +21 -0
- package/behavioral-evals/case-validator.js +194 -0
- package/behavioral-evals/cases/baseline-environment-detection.yaml +21 -0
- package/behavioral-evals/cases/baseline-eval-harness.yaml +27 -0
- package/behavioral-evals/cases/baseline-fable-discipline.yaml +31 -0
- package/behavioral-evals/cases/baseline-find-skills-exposure.yaml +49 -0
- package/behavioral-evals/cases/baseline-find-skills.yaml +21 -0
- package/behavioral-evals/cases/baseline-git-commit.yaml +2 -2
- package/behavioral-evals/cases/baseline-install-cognitive-os.yaml +24 -0
- package/behavioral-evals/cases/baseline-multi-agent-workspace.yaml +28 -0
- package/behavioral-evals/cases/baseline-rewrite-commits.yaml +34 -0
- package/behavioral-evals/cases/baseline-self-evolve.yaml +31 -0
- package/behavioral-evals/cases/baseline-skill-creator.yaml +20 -0
- package/behavioral-evals/cases/baseline-skill-style.yaml +21 -0
- package/behavioral-evals/cases/baseline-to-spec.yaml +34 -0
- package/behavioral-evals/cases/baseline-to-tickets.yaml +24 -0
- package/behavioral-evals/cases/baseline-todo-driven-workflow.yaml +32 -0
- package/behavioral-evals/cases/baseline-using-git-worktrees.yaml +15 -0
- package/behavioral-evals/cases/breaker-zoom-out-after-3.yaml +2 -2
- package/behavioral-evals/cases/pressure-complexity-skip.yaml +2 -2
- package/behavioral-evals/cases/pressure-scope-bypass.yaml +7 -2
- package/behavioral-evals/cases/pressure-sunk-cost-retry.yaml +7 -4
- package/behavioral-evals/cases/scope-discipline.yaml +2 -1
- package/behavioral-evals/cases/verify-before-done.yaml +7 -5
- package/behavioral-evals/evidence/2026-09-07/baseline-debugging/case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-debugging/historical-case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-debugging/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-debugging/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-performance/case.yaml +34 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-performance/historical-case.yaml +34 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-performance/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-performance/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-security-review/case.yaml +28 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-security-review/historical-case.yaml +28 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-security-review/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-security-review/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/historical-case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/baseline-simple-bugfix/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/case.yaml +28 -0
- package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/historical-case.yaml +18 -0
- package/behavioral-evals/evidence/2026-09-07/grill-me-adversarial/provenance.json +30 -0
- package/behavioral-evals/evidence/2026-09-07/live-run-blocker.md +16 -0
- package/behavioral-evals/evidence/2026-09-07/manifest.json +88 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/historical-case.yaml +28 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-scope-bypass/provenance.json +30 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/historical-case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-docs/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/case.yaml +35 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/historical-case.yaml +35 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-error-handling/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/historical-case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification/result.sanitized.json +24 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/historical-case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-23-pressure-skip-verification/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/historical-case.yaml +43 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-skip-verification--2026-08-27-pressure-skip-verification/result.sanitized.json +24 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/historical-case.yaml +47 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry/result.sanitized.json +24 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/historical-case.yaml +47 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-23-pressure-sunk-cost-retry/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/case.yaml +50 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/historical-case.yaml +47 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/pressure-sunk-cost-retry--2026-08-27-pressure-sunk-cost-retry/result.sanitized.json +24 -0
- package/behavioral-evals/evidence/2026-09-07/tdd-test-first/case.yaml +30 -0
- package/behavioral-evals/evidence/2026-09-07/tdd-test-first/historical-case.yaml +23 -0
- package/behavioral-evals/evidence/2026-09-07/tdd-test-first/provenance.json +30 -0
- package/behavioral-evals/evidence/2026-09-07/triage-matrix.json +426 -0
- package/behavioral-evals/evidence/2026-09-07/triage-matrix.md +24 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/case.yaml +19 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/historical-case.yaml +18 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-claim-cites/provenance.json +30 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done/historical-case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/historical-case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-23-verify-before-done/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/historical-case.yaml +31 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-07/verify-before-done--2026-08-27-verify-before-done/result.sanitized.json +20 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/historical-case.yaml +33 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/provenance.json +42 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/result.sanitized.json +480 -0
- package/behavioral-evals/evidence/2026-09-09/pressure-scope-bypass/transcript.sanitized.jsonl +95 -0
- package/behavioral-evals/evidence-tool.js +443 -0
- package/behavioral-evals/fixtures/claude-denied.jsonl +3 -0
- package/behavioral-evals/fixtures/claude-legacy.json +1 -0
- package/behavioral-evals/fixtures/claude-oral-only.jsonl +2 -0
- package/behavioral-evals/fixtures/claude-success.jsonl +5 -0
- package/behavioral-evals/fixtures/claude-truncated.jsonl +3 -0
- package/behavioral-evals/fixtures/opencode-success.jsonl +6 -0
- package/behavioral-evals/results/2026-08-31-baseline-add-feature.json +20 -0
- package/behavioral-evals/results/2026-09-03-baseline-environment-detection-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-baseline-eval-harness-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-fable-discipline-pair.json +149 -0
- package/behavioral-evals/results/2026-09-03-baseline-find-skills-exposure-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-find-skills-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-install-cognitive-os-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-baseline-multi-agent-workspace-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-rewrite-commits-pair.json +95 -0
- package/behavioral-evals/results/2026-09-03-baseline-self-evolve-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-skill-creator-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-skill-style-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-baseline-to-spec-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-baseline-to-tickets-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-todo-driven-workflow-pair.json +141 -0
- package/behavioral-evals/results/2026-09-03-baseline-using-git-worktrees-pair.json +133 -0
- package/behavioral-evals/results/2026-09-03-summary-both.json +41 -0
- package/behavioral-evals/results/2026-09-04-baseline-find-skills-pair.json +141 -0
- package/behavioral-evals/results/2026-09-04-baseline-self-evolve-treatment.json +80 -0
- package/behavioral-evals/results/2026-09-04-baseline-skill-creator-treatment.json +80 -0
- package/behavioral-evals/results/2026-09-04-summary-both.json +41 -0
- package/behavioral-evals/run-with-plugin.js +15 -31
- package/behavioral-evals/run.js +446 -105
- package/behavioral-evals/transcript-parser.js +591 -0
- package/bin/cli.js +24 -12
- package/ci/ab-test-cases/grill-me-adversarial.yaml +14 -4
- package/ci/ab-test-cases/tdd-test-first.yaml +13 -6
- package/ci/ab-test-cases/verification-loop-gates.yaml +20 -7
- package/ci/ab-test-cases/verify-before-claim-cites.yaml +7 -6
- package/ci/ab-test-harness.js +113 -38
- package/ci/action-gate-platform-doc.test.js +31 -0
- package/ci/action-gate.test.js +260 -0
- package/ci/cases/case2.json +2 -2
- package/ci/consistency-check.js +51 -5
- package/ci/description-collision.js +15 -6
- package/ci/disclosure-check.js +225 -0
- package/ci/doc-capability-consistency.test.js +180 -0
- package/ci/fixtures/phase4/parallel-disjoint.stages.json +32 -0
- package/ci/fixtures/phase4/parallel-read-only.stages.json +32 -0
- package/ci/installer-roundtrip.test.js +159 -0
- package/ci/invariant-routing.test.js +74 -0
- package/ci/issue99-runtime.test.js +218 -0
- package/ci/mechanism-2aa-codex-action-gate-policy.test.js +117 -0
- package/ci/mechanism-2ab-openai-plugin-sync-integrity.test.js +80 -0
- package/ci/mechanism-2j-behavioral-runner.test.js +43 -1
- package/ci/mechanism-2k-reference-check.test.js +57 -2
- package/ci/mechanism-2l-multi-agent-workspace.test.js +202 -6
- package/ci/mechanism-2m-self-repo-guard.test.js +65 -0
- package/ci/mechanism-2n-opencode-plugin.test.js +461 -0
- package/ci/mechanism-2o-state-home-isolation.test.js +183 -0
- package/ci/mechanism-2p-installer-links.test.js +183 -0
- package/ci/mechanism-2q-installer-global-shared-store.test.js +132 -0
- package/ci/mechanism-2r-uninstall-flags.test.js +148 -0
- package/ci/mechanism-2s-tdd-quality.test.js +203 -0
- package/ci/mechanism-2t-disclosure.test.js +90 -0
- package/ci/mechanism-2t-evidence-triage.test.js +200 -0
- package/ci/mechanism-2t-issue-20.test.js +75 -0
- package/ci/mechanism-2t-platform-skill-targets.test.js +164 -0
- package/ci/mechanism-2t-transcript-parser.test.js +179 -0
- package/ci/mechanism-2t-workspace-identity.test.js +200 -0
- package/ci/mechanism-2u-issue-42-regressions.test.js +239 -0
- package/ci/mechanism-2u-issue-44.test.js +35 -0
- package/ci/mechanism-2v-verifier-boundaries.test.js +138 -0
- package/ci/mechanism-2w-platform-compatibility.test.js +148 -0
- package/ci/mechanism-2x-release-automation.test.js +157 -0
- package/ci/mechanism-2x-release-commit-message.test.js +66 -0
- package/ci/mechanism-2y-release-tooling-compat.test.js +47 -0
- package/ci/mechanism-2z-claude-plugin-manifest.test.js +29 -0
- package/ci/mutation-check.js +53 -0
- package/ci/mutation-loader.js +57 -0
- package/ci/negative-controls.js +76 -46
- package/ci/openai-plugin-package.test.js +182 -0
- package/ci/openai-plugin-runtime.test.js +175 -0
- package/ci/openai-plugin-submission.test.js +101 -0
- package/ci/phase4-ensemble-memory.test.js +307 -0
- package/ci/reference-check.js +73 -25
- package/ci/release-consistency-check.js +24 -1
- package/ci/router-phase2-safety.test.js +81 -0
- package/ci/router-workflow-plan.test.js +264 -0
- package/ci/routing-check.js +6 -5
- package/ci/runner.js +6 -5
- package/ci/skill-routing-check.js +48 -11
- package/ci/test-behavioral-case-validation.js +62 -0
- package/ci/test-helper.js +10 -0
- package/ci/workflow-plan-consumers-edge.test.js +72 -0
- package/ci/workflow-plan-consumers.test.js +276 -0
- package/docs/architecture.md +137 -57
- package/docs/audit.md +22 -20
- package/docs/codex-action-gate.md +90 -0
- package/docs/issue-20-rollup.md +47 -0
- package/docs/mechanism-first-skill-mesh.md +22 -20
- package/docs/openai-plugin.md +118 -0
- package/docs/philosophy.md +28 -17
- package/docs/platform-capabilities.md +73 -0
- package/docs/platform-compatibility.json +270 -0
- package/docs/reflection.md +37 -22
- package/docs/release-evidence.md +25 -0
- package/docs/routing.md +157 -44
- package/docs/security-audit.md +38 -0
- package/docs/troubleshooting.md +82 -46
- package/docs/verifier-boundaries.md +45 -0
- package/docs/workflows/fable-mode.md +77 -21
- package/docs/workflows/find-skills.md +40 -0
- package/docs/workflows/grill-with-docs.md +2 -2
- package/docs/workflows/multi-agent-workspace.md +22 -10
- package/docs/workflows/self-evolve.md +33 -0
- package/docs/workflows/tdd.md +36 -0
- package/docs/workflows/todo-driven-workflow.md +33 -0
- package/environment-detection/SKILL.md +3 -3
- package/eval-harness/SKILL.md +7 -7
- package/eval-harness/scripts/evaluate.js +12 -10
- package/evals/evaluation-reports.md +14 -0
- package/evals/harness-everything/tasks/positive-tier1.yaml +1 -1
- package/evals/harness-everything/tasks/positive-tier2.yaml +1 -1
- package/evals/harness-everything/tasks/positive-tier3.yaml +1 -1
- package/evals/install-cognitive-os/tasks/positive-trigger.yaml +1 -1
- package/fable-discipline/SKILL.md +2 -2
- package/fable-mode/CONTRACT-FORMAT.md +147 -33
- package/fable-mode/SKILL.md +3 -3
- package/fable-mode/agents/fable-orchestrator.md +95 -35
- package/fable-mode/execution-guardrails/SKILL.md +2 -2
- package/fable-mode/fable-haiku/SKILL.md +3 -3
- package/fable-mode/fable-opus/SKILL.md +3 -3
- package/fable-mode/fable-sonnet/SKILL.md +3 -3
- package/fable-mode/scripts/ensemble-review.js +371 -0
- package/fable-mode/scripts/model-selector.js +134 -3
- package/fable-mode/scripts/workflow-plan-consumer.js +337 -0
- package/find-skills/SKILL.md +5 -5
- package/find-skills/references/discovery-flow.md +1 -1
- package/find-skills/scripts/use-skill.js +7 -1
- package/git-commit/SKILL.md +4 -11
- package/grill-me/SKILL.md +5 -5
- package/grill-with-docs/SKILL.md +7 -7
- package/grill-with-docs/references/session-playbook.md +6 -4
- package/harness-everything/SKILL.md +23 -22
- package/harness-everything/references/router-workflow-plan.md +128 -0
- package/harness-everything/references/triage-and-tiers.md +173 -96
- package/harness-everything/schemas/router-task-shape.schema.json +126 -0
- package/harness-everything/schemas/router-workflow-plan.schema.json +149 -0
- package/harness-everything/scripts/bootstrap.js +13 -4
- package/harness-everything/scripts/ensemble-policy.js +102 -0
- package/harness-everything/scripts/kernel-router.js +212 -0
- package/harness-everything/scripts/router-contract.js +612 -0
- package/harness-everything/scripts/self-heal.js +29 -20
- package/harness-everything/scripts/tier-router.js +346 -221
- package/hooks/hooks.json +60 -15
- package/hooks/scripts/action-gate-rules.json +77 -0
- package/hooks/scripts/action-gate.js +528 -0
- package/hooks/scripts/atomic-commit-check.js +5 -5
- package/hooks/scripts/codex-action-gate-post.js +46 -0
- package/hooks/scripts/codex-permission-request.js +140 -0
- package/hooks/scripts/context-compact.js +4 -3
- package/hooks/scripts/contract-test.js +118 -51
- package/hooks/scripts/lib/fable-contracts.js +121 -0
- package/hooks/scripts/lib/harness-state.js +164 -28
- package/hooks/scripts/lib/platforms/claude.js +45 -62
- package/hooks/scripts/lib/platforms/codex.js +35 -61
- package/hooks/scripts/lib/platforms/continue.js +7 -7
- package/hooks/scripts/lib/platforms/copilot.js +29 -53
- package/hooks/scripts/lib/platforms/hermes.js +71 -20
- package/hooks/scripts/openai-session-start.js +81 -0
- package/hooks/scripts/rule-of-3-tracker.js +9 -8
- package/hooks/scripts/rule-of-3.js +29 -21
- package/hooks/scripts/state-persist.js +5 -4
- package/hooks/scripts/stop-gate.js +2 -2
- package/hooks/scripts/subagent-scope-guard.js +144 -38
- package/hooks/scripts/subagent-start.js +19 -0
- package/hooks/scripts/subagent-stop.js +6 -0
- package/improve-codebase-architecture/SKILL.md +2 -4
- package/improve-codebase-architecture/references/README.md +16 -0
- package/install-cognitive-os/SKILL.md +28 -22
- package/multi-agent-workspace/SKILL.md +16 -15
- package/multi-agent-workspace/references/architecture-guide.md +13 -9
- package/multi-agent-workspace/references/orchestration.md +39 -22
- package/multi-agent-workspace/scripts/consume-workflow-plan.js +129 -0
- package/multi-agent-workspace/scripts/index_memory.js +79 -26
- package/multi-agent-workspace/scripts/path-boundary.js +59 -0
- package/multi-agent-workspace/scripts/project-docs-resolver.js +357 -0
- package/multi-agent-workspace/scripts/scaffold.js +258 -103
- package/multi-agent-workspace/templates/AGENTS.md +20 -0
- package/multi-agent-workspace/workflows/03-generation.md +8 -10
- package/multi-agent-workspace/workflows/04-launcher.md +5 -5
- package/opencode-plugin/README.md +114 -71
- package/opencode-plugin/hooks/verify.js +62 -12
- package/opencode-plugin/index.mjs +547 -0
- package/opencode-plugin/plugin.json +4 -12
- package/package.json +16 -3
- package/plugins/harness-everything/.codex-plugin/plugin.json +39 -0
- package/plugins/harness-everything/hooks/hooks.json +197 -0
- package/plugins/harness-everything/hooks/scripts/action-gate-rules.json +77 -0
- package/plugins/harness-everything/hooks/scripts/action-gate.js +528 -0
- package/plugins/harness-everything/hooks/scripts/atomic-commit-check.js +68 -0
- package/plugins/harness-everything/hooks/scripts/boundary-guard.js +55 -0
- package/plugins/harness-everything/hooks/scripts/codex-action-gate-post.js +46 -0
- package/plugins/harness-everything/hooks/scripts/codex-permission-request.js +140 -0
- package/plugins/harness-everything/hooks/scripts/context-compact.js +115 -0
- package/plugins/harness-everything/hooks/scripts/contract-test.js +146 -0
- package/plugins/harness-everything/hooks/scripts/depth-guard.js +65 -0
- package/plugins/harness-everything/hooks/scripts/lib/fable-contracts.js +121 -0
- package/plugins/harness-everything/hooks/scripts/lib/harness-state.js +275 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/claude.js +168 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/codex.js +88 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/continue.js +91 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/copilot.js +85 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/cursor.js +89 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/hermes.js +108 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/index.js +17 -0
- package/plugins/harness-everything/hooks/scripts/lib/platforms/worktrees.js +20 -0
- package/plugins/harness-everything/hooks/scripts/lib/workspace.js +290 -0
- package/plugins/harness-everything/hooks/scripts/openai-session-start.js +81 -0
- package/plugins/harness-everything/hooks/scripts/reset-circuit-breaker.js +30 -0
- package/plugins/harness-everything/hooks/scripts/rule-of-3-tracker.js +125 -0
- package/plugins/harness-everything/hooks/scripts/rule-of-3.js +195 -0
- package/plugins/harness-everything/hooks/scripts/state-persist.js +100 -0
- package/plugins/harness-everything/hooks/scripts/stop-gate.js +80 -0
- package/plugins/harness-everything/hooks/scripts/subagent-scope-guard.js +194 -0
- package/plugins/harness-everything/hooks/scripts/subagent-start.js +19 -0
- package/plugins/harness-everything/hooks/scripts/subagent-stop.js +6 -0
- package/plugins/harness-everything/hooks/session-start.js +81 -0
- package/plugins/harness-everything/plugin.json +43 -0
- package/plugins/harness-everything/scripts/lib/advisory-text.js +174 -0
- package/plugins/harness-everything/skills/environment-detection/SKILL.md +41 -0
- package/plugins/harness-everything/skills/environment-detection/references/shell-syntax-rules.md +76 -0
- package/plugins/harness-everything/skills/environment-detection/scripts/preflight.js +103 -0
- package/plugins/harness-everything/skills/eval-harness/SKILL.md +42 -0
- package/plugins/harness-everything/skills/eval-harness/references/scoring-rubric.md +68 -0
- package/plugins/harness-everything/skills/eval-harness/scripts/evaluate.js +79 -0
- package/plugins/harness-everything/skills/fable-discipline/SKILL.md +41 -0
- package/plugins/harness-everything/skills/fable-discipline/references/discipline-rules.md +22 -0
- package/plugins/harness-everything/skills/fable-mode/CONTRACT-FORMAT.md +165 -0
- package/plugins/harness-everything/skills/fable-mode/SKILL.md +39 -0
- package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +159 -0
- package/plugins/harness-everything/skills/fable-mode/agents/fable-verifier.md +32 -0
- package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-haiku.md +52 -0
- package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-sonnet.md +53 -0
- package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +43 -0
- package/plugins/harness-everything/skills/fable-mode/execution-guardrails/references/guardrail-rules.md +56 -0
- package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +36 -0
- package/plugins/harness-everything/skills/fable-mode/fable-haiku/references/routing-notes.md +26 -0
- package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +36 -0
- package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +36 -0
- package/plugins/harness-everything/skills/fable-mode/model-matrix.json +32 -0
- package/plugins/harness-everything/skills/fable-mode/references/execution-phases.md +37 -0
- package/plugins/harness-everything/skills/fable-mode/references/model-matrix.md +39 -0
- package/plugins/harness-everything/skills/fable-mode/scripts/ensemble-review.js +371 -0
- package/plugins/harness-everything/skills/fable-mode/scripts/model-selector.js +260 -0
- package/plugins/harness-everything/skills/fable-mode/scripts/workflow-plan-consumer.js +337 -0
- package/plugins/harness-everything/skills/find-skills/SKILL.md +48 -0
- package/plugins/harness-everything/skills/find-skills/references/discovery-flow.md +102 -0
- package/plugins/harness-everything/skills/find-skills/scripts/use-skill.js +112 -0
- package/plugins/harness-everything/skills/git-commit/SKILL.md +38 -0
- package/plugins/harness-everything/skills/git-commit/guides/ANGULAR_STYLE.md +61 -0
- package/plugins/harness-everything/skills/git-commit/guides/COMMIT_GENERATION.md +24 -0
- package/plugins/harness-everything/skills/git-commit/guides/LANGUAGE_DETECTION.md +16 -0
- package/plugins/harness-everything/skills/git-commit/guides/MAIN_REPO.md +15 -0
- package/plugins/harness-everything/skills/git-commit/guides/SUBMODULES.md +27 -0
- package/plugins/harness-everything/skills/git-commit/references/commit-flow.md +30 -0
- package/plugins/harness-everything/skills/grill-me/SKILL.md +43 -0
- package/plugins/harness-everything/skills/grill-me/references/grilling-playbook.md +38 -0
- package/plugins/harness-everything/skills/grill-with-docs/ADR-FORMAT.md +47 -0
- package/plugins/harness-everything/skills/grill-with-docs/CONTEXT-FORMAT.md +63 -0
- package/plugins/harness-everything/skills/grill-with-docs/SKILL.md +41 -0
- package/plugins/harness-everything/skills/grill-with-docs/references/session-playbook.md +99 -0
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +43 -0
- package/plugins/harness-everything/skills/harness-everything/examples/README.md +9 -0
- package/plugins/harness-everything/skills/harness-everything/examples/todo-workflow.md +22 -0
- package/plugins/harness-everything/skills/harness-everything/references/router-workflow-plan.md +128 -0
- package/plugins/harness-everything/skills/harness-everything/references/skill-registry.md +39 -0
- package/plugins/harness-everything/skills/harness-everything/references/triage-and-tiers.md +193 -0
- package/plugins/harness-everything/skills/harness-everything/schemas/router-task-shape.schema.json +126 -0
- package/plugins/harness-everything/skills/harness-everything/schemas/router-workflow-plan.schema.json +149 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/bootstrap.js +212 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/ensemble-policy.js +102 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router.js +212 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/router-contract.js +612 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/routing-keywords.json +320 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/self-heal.js +250 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/tier-router.js +459 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/verify-gate.js +100 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/SKILL.md +38 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/DEEPENING.md +37 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/HTML-REPORT.md +123 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/INTERFACE-DESIGN.md +44 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/LANGUAGE.md +53 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/guides/refactoring-workflow.md +59 -0
- package/plugins/harness-everything/skills/improve-codebase-architecture/references/README.md +16 -0
- package/plugins/harness-everything/skills/install-cognitive-os/SKILL.md +44 -0
- package/plugins/harness-everything/skills/install-cognitive-os/references/cognitive-loop.md +27 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/NOTICE-agency-agents.md +27 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +38 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/examples/csharp-monorepo.md +123 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/examples/typescript-cli.md +126 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-claude.md +77 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-codex.md +146 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-continue.md +38 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-copilot.md +74 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-cursor.md +40 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/platform-hermes.md +38 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/guidelines/role-capabilities.md +108 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/references/agency-agents.md +31 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/references/architecture-guide.md +36 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/references/orchestration.md +65 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/agency-catalog.js +190 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/consume-workflow-plan.js +129 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/index_memory.js +104 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/path-boundary.js +59 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js +357 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/scaffold.js +408 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/AGENTS.md +20 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/README.md +17 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/agent-launcher.md +68 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-csharp.md +56 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-polyglot.md +62 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/backend-developer-ts.md +57 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/memory-keeper.md +37 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/requirement-analyzer.md +66 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/specialist-agent.md +34 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/task-verifier.md +56 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/templates/tool-maker.md +33 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/workflows/01-init.md +22 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/workflows/02-analysis.md +23 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/workflows/03-generation.md +25 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/workflows/04-launcher.md +16 -0
- package/plugins/harness-everything/skills/repo-docs/SKILL.md +42 -0
- package/plugins/harness-everything/skills/repo-docs/references/process-guide.md +60 -0
- package/plugins/harness-everything/skills/repo-docs/templates/agents-template.md +264 -0
- package/plugins/harness-everything/skills/repo-docs/templates/knowledge-agents-template.md +50 -0
- package/plugins/harness-everything/skills/repo-docs/templates/knowledge-readme-template.md +83 -0
- package/plugins/harness-everything/skills/repo-docs/templates/manual-recon.md +117 -0
- package/plugins/harness-everything/skills/repo-docs/templates/multi-skills-readme-template.md +72 -0
- package/plugins/harness-everything/skills/repo-docs/templates/product-agents-template.md +78 -0
- package/plugins/harness-everything/skills/repo-docs/templates/product-readme-template.md +95 -0
- package/plugins/harness-everything/skills/repo-docs/templates/readme-template.md +136 -0
- package/plugins/harness-everything/skills/repo-docs/templates/single-skill-readme-template.md +70 -0
- package/plugins/harness-everything/skills/repo-docs/templates/skills-agents-template.md +60 -0
- package/plugins/harness-everything/skills/rewrite-commits/SKILL.md +37 -0
- package/plugins/harness-everything/skills/rewrite-commits/references/workflow.md +18 -0
- package/plugins/harness-everything/skills/security-review/SKILL.md +47 -0
- package/plugins/harness-everything/skills/security-review/guides/OWASP-PATTERNS.md +31 -0
- package/plugins/harness-everything/skills/security-review/guides/STRIDE-THREAT-MODEL.md +21 -0
- package/plugins/harness-everything/skills/security-review/references/security-checklist.md +478 -0
- package/plugins/harness-everything/skills/security-review/scripts/audit-secrets.js +85 -0
- package/plugins/harness-everything/skills/self-evolve/SKILL.md +45 -0
- package/plugins/harness-everything/skills/self-evolve/references/memory-resolution.md +61 -0
- package/plugins/harness-everything/skills/self-evolve/scripts/persist-memory.js +272 -0
- package/plugins/harness-everything/skills/self-evolve/scripts/register-dynamic-skill.js +253 -0
- package/plugins/harness-everything/skills/self-evolve/scripts/self-regression.js +125 -0
- package/plugins/harness-everything/skills/skill-creator/SKILL.md +47 -0
- package/plugins/harness-everything/skills/skill-creator/references/authoring-workflow.md +14 -0
- package/plugins/harness-everything/skills/skill-creator/references/dynamic-generation-contract.md +21 -0
- package/plugins/harness-everything/skills/skill-creator/references/quality-checklist.md +16 -0
- package/plugins/harness-everything/skills/skill-creator/references/quality-principles.md +81 -0
- package/plugins/harness-everything/skills/skill-creator/references/testing-workflow.md +27 -0
- package/plugins/harness-everything/skills/skill-style/SKILL.md +42 -0
- package/plugins/harness-everything/skills/skill-style/references/style-guide.md +62 -0
- package/plugins/harness-everything/skills/tdd/SKILL.md +38 -0
- package/plugins/harness-everything/skills/tdd/fixtures/pass-evidence.json +100 -0
- package/plugins/harness-everything/skills/tdd/fixtures/quality-scenarios.json +20 -0
- package/plugins/harness-everything/skills/tdd/guides/deep-modules.md +33 -0
- package/plugins/harness-everything/skills/tdd/guides/interface-design.md +31 -0
- package/plugins/harness-everything/skills/tdd/guides/mocking.md +59 -0
- package/plugins/harness-everything/skills/tdd/guides/refactoring.md +10 -0
- package/plugins/harness-everything/skills/tdd/guides/tests.md +61 -0
- package/plugins/harness-everything/skills/tdd/references/core-discipline.md +51 -0
- package/plugins/harness-everything/skills/tdd/references/integration-testing.md +13 -0
- package/plugins/harness-everything/skills/tdd/references/quality-model.md +68 -0
- package/plugins/harness-everything/skills/tdd/references/unit-testing.md +15 -0
- package/plugins/harness-everything/skills/tdd/schemas/quality-evidence.schema.json +211 -0
- package/plugins/harness-everything/skills/tdd/schemas/quality-report.schema.json +110 -0
- package/plugins/harness-everything/skills/tdd/scripts/quality-gate.js +475 -0
- package/plugins/harness-everything/skills/to-spec/SKILL.md +40 -0
- package/plugins/harness-everything/skills/to-spec/references/process.md +77 -0
- package/plugins/harness-everything/skills/to-spec/scripts/check-project-docs.js +239 -0
- package/plugins/harness-everything/skills/to-spec/scripts/project-docs-resolver.js +111 -0
- package/plugins/harness-everything/skills/to-spec/templates/cli-reference.md +31 -0
- package/plugins/harness-everything/skills/to-spec/templates/dev-doc.md +27 -0
- package/plugins/harness-everything/skills/to-spec/templates/feature-spec.md +59 -0
- package/plugins/harness-everything/skills/to-spec/templates/schema-doc.md +31 -0
- package/plugins/harness-everything/skills/to-tickets/SKILL.md +42 -0
- package/plugins/harness-everything/skills/to-tickets/references/publishing-and-templates.md +97 -0
- package/plugins/harness-everything/skills/todo-driven-workflow/SKILL.md +42 -0
- package/plugins/harness-everything/skills/todo-driven-workflow/references/execution-guide.md +27 -0
- package/plugins/harness-everything/skills/using-git-worktrees/SKILL.md +49 -0
- package/plugins/harness-everything/skills/using-git-worktrees/references/workflow-details.md +108 -0
- package/plugins/harness-everything/skills/verification-loop/SKILL.md +46 -0
- package/plugins/harness-everything/skills/verification-loop/references/verification-phases.md +116 -0
- package/plugins/harness-everything/skills/verification-loop/templates/verification-report.template.md +15 -0
- package/plugins/harness-everything/skills/verify-before-claim/SKILL.md +43 -0
- package/plugins/harness-everything/skills/verify-before-claim/references/verification-guide.md +73 -0
- package/plugins/harness-everything/skills/zoom-out/SKILL.md +42 -0
- package/plugins/harness-everything/skills/zoom-out/references/circuit-breaker.md +66 -0
- package/plugins/harness-everything/skills/zoom-out/templates/zoom-out-report.template.md +20 -0
- package/repo-docs/SKILL.md +3 -7
- package/rewrite-commits/SKILL.md +2 -2
- package/scripts/build-openai-submission.js +158 -0
- package/scripts/installer.js +86 -11
- package/scripts/lib/advisory-text.js +25 -18
- package/scripts/lib/execution-contract.js +70 -0
- package/scripts/lib/manifest.js +34 -38
- package/scripts/lib/path-boundary.js +90 -0
- package/scripts/lib/skills.js +124 -70
- package/scripts/lib/workspace.js +269 -7
- package/scripts/sync-openai-plugin.js +91 -0
- package/scripts/sync-release-version.js +212 -0
- package/security-review/SKILL.md +4 -7
- package/security-review/scripts/audit-secrets.js +9 -8
- package/self-evolve/SKILL.md +9 -11
- package/self-evolve/references/memory-resolution.md +4 -1
- package/self-evolve/scripts/persist-memory.js +221 -88
- package/self-evolve/scripts/register-dynamic-skill.js +12 -10
- package/self-evolve/scripts/self-regression.js +23 -8
- package/skill-creator/SKILL.md +12 -14
- package/skill-creator/references/quality-checklist.md +1 -0
- package/skill-style/SKILL.md +4 -4
- package/skill-style/references/style-guide.md +29 -3
- package/submission/openai/README.md +67 -0
- package/submission/openai/listing.json +31 -0
- package/submission/openai/test-cases.json +61 -0
- package/tdd/SKILL.md +20 -26
- package/tdd/fixtures/pass-evidence.json +100 -0
- package/tdd/fixtures/quality-scenarios.json +20 -0
- package/tdd/references/core-discipline.md +2 -0
- package/tdd/references/integration-testing.md +13 -0
- package/tdd/references/quality-model.md +68 -0
- package/tdd/references/unit-testing.md +15 -0
- package/tdd/schemas/quality-evidence.schema.json +211 -0
- package/tdd/schemas/quality-report.schema.json +110 -0
- package/tdd/scripts/quality-gate.js +475 -0
- package/to-spec/SKILL.md +3 -5
- package/to-spec/scripts/check-project-docs.js +42 -62
- package/to-spec/scripts/project-docs-resolver.js +111 -0
- package/to-tickets/SKILL.md +4 -4
- package/todo-driven-workflow/SKILL.md +6 -4
- package/using-git-worktrees/SKILL.md +3 -3
- package/verification-loop/SKILL.md +4 -4
- package/verify-before-claim/SKILL.md +2 -2
- package/zoom-out/SKILL.md +4 -4
- package/ab-test-deep-analysis-2026-08-27.md +0 -383
- package/ab-test-results-2026-08-27.md +0 -152
- package/ab-test-statistical-analysis-31cases.md +0 -193
- package/opencode-plugin/hooks/circuit-breaker.js +0 -121
- package/opencode-plugin/hooks/compliance.js +0 -128
- package/opencode-plugin/hooks/post-edit.js +0 -62
- package/opencode-plugin/hooks/pre-complete.js +0 -65
- package/opencode-plugin/test-hook-firing.js +0 -198
package/scripts/lib/workspace.js
CHANGED
|
@@ -1,14 +1,246 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
|
+
const crypto = require('crypto');
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
const WORKSPACE_CONTEXT_KEYS = [
|
|
7
|
+
'workspace_root', 'workspaceRoot', 'project_root', 'projectRoot',
|
|
8
|
+
'working_directory', 'workingDirectory', 'cwd', 'root'
|
|
9
|
+
];
|
|
10
|
+
const SESSION_CONTEXT_KEYS = ['session_id', 'sessionId'];
|
|
11
|
+
const HOST_CONTEXT_KEYS = [
|
|
12
|
+
'host_id', 'hostId', 'agent_id', 'agentId', 'client_id', 'clientId',
|
|
13
|
+
'machine_id', 'machineId', 'runtime_id', 'runtimeId'
|
|
14
|
+
];
|
|
15
|
+
const SESSION_REGISTRY_DIR = 'session-workspaces';
|
|
16
|
+
const SESSION_REGISTRY_VERSION = 1;
|
|
17
|
+
const UNBOUND_WORKSPACE_KEY = 'unbound-workspace';
|
|
18
|
+
|
|
19
|
+
function findWorkspaceRoot(startPath, allowNonGit = false) {
|
|
20
|
+
if (typeof startPath !== 'string' || !startPath.trim()) return null;
|
|
21
|
+
let dir = path.resolve(startPath);
|
|
22
|
+
while (true) {
|
|
23
|
+
if (fs.existsSync(path.join(dir, '.git'))) return canonicalPath(dir);
|
|
24
|
+
const parent = path.dirname(dir);
|
|
25
|
+
if (parent === dir) break;
|
|
26
|
+
dir = parent;
|
|
27
|
+
}
|
|
28
|
+
return allowNonGit ? canonicalPath(startPath) : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function canonicalPath(value) {
|
|
32
|
+
if (typeof value !== 'string' || !value.trim()) return null;
|
|
33
|
+
const resolved = path.resolve(value);
|
|
34
|
+
try { return fs.realpathSync(resolved); } catch (err) { return resolved; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getSessionId(context) {
|
|
38
|
+
if (!context || typeof context !== 'object') return null;
|
|
39
|
+
for (const key of SESSION_CONTEXT_KEYS) {
|
|
40
|
+
if (typeof context[key] === 'string' && context[key].trim()) {
|
|
41
|
+
return context[key].trim();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const key of ['session', 'metadata', 'context']) {
|
|
45
|
+
if (context[key] && typeof context[key] === 'object') {
|
|
46
|
+
const nested = getSessionId(context[key]);
|
|
47
|
+
if (nested) return nested;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getHostId(context) {
|
|
54
|
+
if (!context || typeof context !== 'object') return null;
|
|
55
|
+
for (const key of HOST_CONTEXT_KEYS) {
|
|
56
|
+
if (typeof context[key] === 'string' && context[key].trim()) return context[key].trim();
|
|
57
|
+
}
|
|
58
|
+
if (typeof context.host === 'string' && context.host.trim()) return context.host.trim();
|
|
59
|
+
if (context.host && typeof context.host === 'object') return getHostId(context.host);
|
|
60
|
+
for (const key of ['agent', 'client', 'machine', 'runtime', 'metadata', 'context']) {
|
|
61
|
+
if (context[key] && typeof context[key] === 'object') {
|
|
62
|
+
const nested = getHostId(context[key]);
|
|
63
|
+
if (nested) return nested;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function contextPath(context, depth = 0) {
|
|
70
|
+
if (typeof context === 'string' && context.trim()) return context.trim();
|
|
71
|
+
if (!context || typeof context !== 'object' || depth > 2) return null;
|
|
72
|
+
|
|
73
|
+
for (const key of WORKSPACE_CONTEXT_KEYS) {
|
|
74
|
+
const value = context[key];
|
|
75
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
76
|
+
if (value && typeof value === 'object') {
|
|
77
|
+
const nested = contextPath(value, depth + 1);
|
|
78
|
+
if (nested) return nested;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const key of ['workspace', 'project', 'host', 'context']) {
|
|
83
|
+
const nested = contextPath(context[key], depth + 1);
|
|
84
|
+
if (nested) return nested;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function sessionRegistryPath(sessionId, hostId) {
|
|
90
|
+
const namespace = hostId ? `${hostId}\0${sessionId}` : String(sessionId);
|
|
91
|
+
const hash = crypto.createHash('sha256').update(namespace).digest('hex');
|
|
92
|
+
return path.join(getStateHome(), SESSION_REGISTRY_DIR, `${hash}.json`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function readRegistryRecord(filePath) {
|
|
96
|
+
try {
|
|
97
|
+
const record = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
98
|
+
if (record.version !== SESSION_REGISTRY_VERSION || typeof record.workspaceRoot !== 'string') return null;
|
|
99
|
+
return record;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function findSessionRecords(sessionId) {
|
|
106
|
+
const registryDir = path.join(getStateHome(), SESSION_REGISTRY_DIR);
|
|
107
|
+
try {
|
|
108
|
+
return fs.readdirSync(registryDir, { withFileTypes: true })
|
|
109
|
+
.filter(entry => entry.isFile() && entry.name.endsWith('.json'))
|
|
110
|
+
.map(entry => readRegistryRecord(path.join(registryDir, entry.name)))
|
|
111
|
+
.filter(record => record && record.sessionId === sessionId);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function readWorkspaceSession(sessionId, context) {
|
|
118
|
+
if (!sessionId) return null;
|
|
119
|
+
const hostId = getHostId(context);
|
|
120
|
+
const records = [];
|
|
121
|
+
const preferred = readRegistryRecord(sessionRegistryPath(sessionId, hostId));
|
|
122
|
+
if (preferred && (!hostId || !preferred.hostId || preferred.hostId === hostId)) records.push(preferred);
|
|
123
|
+
if (hostId) {
|
|
124
|
+
// A session may have been first observed by a caller that did not expose
|
|
125
|
+
// host metadata. Reuse that unscoped binding rather than forking state.
|
|
126
|
+
const unscoped = readRegistryRecord(sessionRegistryPath(sessionId, null));
|
|
127
|
+
if (unscoped && !unscoped.hostId) records.push(unscoped);
|
|
128
|
+
} else {
|
|
129
|
+
records.push(...findSessionRecords(sessionId));
|
|
10
130
|
}
|
|
11
|
-
|
|
131
|
+
|
|
132
|
+
const unique = [...new Map(records.map(record => [
|
|
133
|
+
`${record.hostId || ''}\0${record.workspaceRoot}`,
|
|
134
|
+
record
|
|
135
|
+
])).values()];
|
|
136
|
+
if (unique.length === 1) return unique[0];
|
|
137
|
+
if (unique.length > 1) {
|
|
138
|
+
// Hostless payloads are safe only when all known host bindings agree on
|
|
139
|
+
// the same workspace. Otherwise select nothing and require context.
|
|
140
|
+
const roots = new Set(unique.map(record => canonicalPath(record.workspaceRoot)));
|
|
141
|
+
if (roots.size === 1) return unique[0];
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function bindWorkspaceSession(sessionId, workspaceRoot, context, force = false) {
|
|
147
|
+
if (!sessionId) return null;
|
|
148
|
+
const root = findWorkspaceRoot(workspaceRoot, true);
|
|
149
|
+
if (!root) return null;
|
|
150
|
+
const hostId = getHostId(context);
|
|
151
|
+
const target = sessionRegistryPath(sessionId, hostId);
|
|
152
|
+
const record = {
|
|
153
|
+
version: SESSION_REGISTRY_VERSION,
|
|
154
|
+
sessionId,
|
|
155
|
+
hostId: hostId || undefined,
|
|
156
|
+
workspaceRoot: root,
|
|
157
|
+
workspaceKey: getWorkspaceKey(root),
|
|
158
|
+
updatedAt: new Date().toISOString()
|
|
159
|
+
};
|
|
160
|
+
const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
161
|
+
try {
|
|
162
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
163
|
+
fs.writeFileSync(temporary, JSON.stringify(record, null, 2), 'utf8');
|
|
164
|
+
// Keep the first successful binding when two hooks initialize together.
|
|
165
|
+
if (fs.existsSync(target)) {
|
|
166
|
+
if (readRegistryRecord(target) && !force) fs.unlinkSync(temporary);
|
|
167
|
+
else {
|
|
168
|
+
fs.unlinkSync(target);
|
|
169
|
+
fs.renameSync(temporary, target);
|
|
170
|
+
}
|
|
171
|
+
} else fs.renameSync(temporary, target);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
// Identity persistence is best effort. The caller still receives the
|
|
174
|
+
// resolved root and the next invocation can retry the binding.
|
|
175
|
+
try { if (fs.existsSync(temporary)) fs.unlinkSync(temporary); } catch (cleanupErr) { /* ignore */ }
|
|
176
|
+
}
|
|
177
|
+
return root;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function isIdentityReset(context) {
|
|
181
|
+
if (!context || typeof context !== 'object') return false;
|
|
182
|
+
return context.workspace_identity_reset === true ||
|
|
183
|
+
context.workspaceIdentityReset === true ||
|
|
184
|
+
context.new_session === true ||
|
|
185
|
+
context.newSession === true;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function getWorkspaceRoot(context) {
|
|
189
|
+
const sessionId = getSessionId(context);
|
|
190
|
+
const existing = readWorkspaceSession(sessionId, context);
|
|
191
|
+
const explicit = contextPath(context);
|
|
192
|
+
|
|
193
|
+
// SessionStart is the host's authoritative opportunity to establish (or
|
|
194
|
+
// re-establish) the project identity. All later hook invocations keep that
|
|
195
|
+
// binding even if the agent changes cwd or reports a nested repository.
|
|
196
|
+
if (existing && !isIdentityReset(context)) return existing.workspaceRoot;
|
|
197
|
+
|
|
198
|
+
const hostRoot = explicit || process.env.HARNESS_WORKSPACE_ROOT ||
|
|
199
|
+
process.env.CLAUDE_PROJECT_DIR || process.env.CODEX_PROJECT_DIR;
|
|
200
|
+
const root = findWorkspaceRoot(hostRoot || process.cwd(), Boolean(hostRoot));
|
|
201
|
+
if (sessionId) bindWorkspaceSession(sessionId, root, context, isIdentityReset(context));
|
|
202
|
+
return root;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Single global root for ALL Harness runtime state (session counters,
|
|
206
|
+
// circuit-breaker trips, audit logs, handoff timestamps, ...). Never
|
|
207
|
+
// cwd-derived, so a script invoked from a fixture dir, worktree, submodule,
|
|
208
|
+
// or any other nested non-git directory can never scatter files there
|
|
209
|
+
// (issue #42). `$HARNESS_STATE_HOME` matches the env var the opencode
|
|
210
|
+
// plugin already used for its own (differently-rooted) global state.
|
|
211
|
+
function getStateHome() {
|
|
212
|
+
return process.env.HARNESS_STATE_HOME || path.join(os.homedir(), '.agents', 'harness-everything');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Keys runtime state per real workspace instead of per invocation cwd, so
|
|
216
|
+
// two different repos (or a repo and a stray subdirectory someone `cd`ed
|
|
217
|
+
// into) never collide or fork the same state stream. Uses the resolved real
|
|
218
|
+
// path (symlinks collapsed) hashed short, prefixed with a readable slug
|
|
219
|
+
// purely so `~/.agents/harness-everything/workspaces/` stays eyeballable.
|
|
220
|
+
function getWorkspaceKey(root) {
|
|
221
|
+
const workspaceRoot = typeof root === 'string' && root.trim() ? root : getWorkspaceRoot(root);
|
|
222
|
+
if (!workspaceRoot) return UNBOUND_WORKSPACE_KEY;
|
|
223
|
+
const resolved = path.resolve(workspaceRoot);
|
|
224
|
+
let real = resolved;
|
|
225
|
+
try { real = fs.realpathSync(resolved); } catch (err) { /* path may not exist yet (tests) */ }
|
|
226
|
+
const slug = path.basename(real).toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'workspace';
|
|
227
|
+
const hashInput = process.platform === 'win32' ? real.toLowerCase() : real;
|
|
228
|
+
const hash = crypto.createHash('sha1').update(hashInput).digest('hex').slice(0, 12);
|
|
229
|
+
return `${slug}-${hash}`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function getWorkspaceStateDir(root) {
|
|
233
|
+
return path.join(getStateHome(), 'workspaces', getWorkspaceKey(root));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function resolveWorkspaceIdentity(context) {
|
|
237
|
+
const workspaceRoot = getWorkspaceRoot(context);
|
|
238
|
+
return {
|
|
239
|
+
workspaceRoot,
|
|
240
|
+
workspaceKey: getWorkspaceKey(workspaceRoot),
|
|
241
|
+
stateDir: getWorkspaceStateDir(workspaceRoot),
|
|
242
|
+
sessionId: getSessionId(context)
|
|
243
|
+
};
|
|
12
244
|
}
|
|
13
245
|
|
|
14
246
|
function getUserPromptsDir() {
|
|
@@ -25,4 +257,34 @@ function getUserPromptsDir() {
|
|
|
25
257
|
}
|
|
26
258
|
}
|
|
27
259
|
|
|
28
|
-
|
|
260
|
+
// The harness repo must never be "repaired" into carrying its own generated
|
|
261
|
+
// advisory files. Identify it by repo identity, not by comparing the caller's
|
|
262
|
+
// __dirname: when harness runs from an npx/global install against its own
|
|
263
|
+
// source checkout, the installed path and the workspace path differ and a
|
|
264
|
+
// path-based guard silently fails open (issue #40).
|
|
265
|
+
const HARNESS_PACKAGE_NAME = 'harness-everything';
|
|
266
|
+
|
|
267
|
+
function isHarnessRepo(workspaceRoot) {
|
|
268
|
+
try {
|
|
269
|
+
const pkgPath = path.join(workspaceRoot, 'package.json');
|
|
270
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
271
|
+
return pkg.name === HARNESS_PACKAGE_NAME;
|
|
272
|
+
} catch (err) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
module.exports = {
|
|
278
|
+
getWorkspaceRoot,
|
|
279
|
+
resolveWorkspaceIdentity,
|
|
280
|
+
bindWorkspaceSession,
|
|
281
|
+
readWorkspaceSession,
|
|
282
|
+
getSessionId,
|
|
283
|
+
getHostId,
|
|
284
|
+
getStateHome,
|
|
285
|
+
getWorkspaceKey,
|
|
286
|
+
getWorkspaceStateDir,
|
|
287
|
+
getUserPromptsDir,
|
|
288
|
+
isHarnessRepo,
|
|
289
|
+
HARNESS_PACKAGE_NAME,
|
|
290
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
8
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(ROOT, '.claude-plugin', 'plugin.json'), 'utf8'));
|
|
9
|
+
const pluginSkillsRoot = path.join(ROOT, 'plugins', 'harness-everything', 'skills');
|
|
10
|
+
const pluginRoot = path.join(ROOT, 'plugins', 'harness-everything');
|
|
11
|
+
const pluginHooksRoot = path.join(pluginRoot, 'hooks');
|
|
12
|
+
const sourceHooksScriptsRoot = path.join(ROOT, 'hooks', 'scripts');
|
|
13
|
+
|
|
14
|
+
function normalizeText(file) {
|
|
15
|
+
const bytes = fs.readFileSync(file);
|
|
16
|
+
// Skills and hooks are text, but keeping binary assets byte-preserving makes
|
|
17
|
+
// this copier safe if a referenced asset is added later.
|
|
18
|
+
if (bytes.includes(0)) return;
|
|
19
|
+
const normalized = bytes.toString('utf8').replace(/\r\n/g, '\n');
|
|
20
|
+
fs.writeFileSync(file, normalized, 'utf8');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function normalizeTree(root) {
|
|
24
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
25
|
+
const full = path.join(root, entry.name);
|
|
26
|
+
if (entry.isDirectory()) normalizeTree(full);
|
|
27
|
+
else if (entry.isFile()) normalizeText(full);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function syncTree(source, destination) {
|
|
32
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
33
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
34
|
+
if (fs.statSync(source).isDirectory()) {
|
|
35
|
+
fs.cpSync(source, destination, { recursive: true });
|
|
36
|
+
normalizeTree(destination);
|
|
37
|
+
} else {
|
|
38
|
+
fs.copyFileSync(source, destination);
|
|
39
|
+
normalizeText(destination);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fs.mkdirSync(pluginSkillsRoot, { recursive: true });
|
|
44
|
+
|
|
45
|
+
const expected = new Set();
|
|
46
|
+
for (const entry of manifest.skills || []) {
|
|
47
|
+
const source = path.resolve(ROOT, entry);
|
|
48
|
+
const name = path.basename(source);
|
|
49
|
+
const destination = path.join(pluginSkillsRoot, name);
|
|
50
|
+
expected.add(name);
|
|
51
|
+
syncTree(source, destination);
|
|
52
|
+
console.log(`synced ${name}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const name of fs.readdirSync(pluginSkillsRoot)) {
|
|
56
|
+
if (!expected.has(name)) fs.rmSync(path.join(pluginSkillsRoot, name), { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The Claude hook definitions are host-specific, but their enforcement
|
|
60
|
+
// scripts are shared. Package the scripts plus the small runtime libraries
|
|
61
|
+
// they require so every OpenAI hook is self-contained under PLUGIN_ROOT.
|
|
62
|
+
const packageHookScriptsRoot = path.join(pluginHooksRoot, 'scripts');
|
|
63
|
+
syncTree(sourceHooksScriptsRoot, packageHookScriptsRoot);
|
|
64
|
+
|
|
65
|
+
const packageWorkspace = path.join(packageHookScriptsRoot, 'lib', 'workspace.js');
|
|
66
|
+
syncTree(path.join(ROOT, 'scripts', 'lib', 'workspace.js'), packageWorkspace);
|
|
67
|
+
|
|
68
|
+
const packageAdvisoryText = path.join(pluginRoot, 'scripts', 'lib', 'advisory-text.js');
|
|
69
|
+
syncTree(path.join(ROOT, 'scripts', 'lib', 'advisory-text.js'), packageAdvisoryText);
|
|
70
|
+
|
|
71
|
+
// The source hook state module is shared with Claude and uses the repository
|
|
72
|
+
// layout. Rewrite only that import in the packaged copy; all other relative
|
|
73
|
+
// imports remain inside the package tree.
|
|
74
|
+
const packageState = path.join(packageHookScriptsRoot, 'lib', 'harness-state.js');
|
|
75
|
+
const stateSource = fs.readFileSync(packageState, 'utf8');
|
|
76
|
+
fs.writeFileSync(
|
|
77
|
+
packageState,
|
|
78
|
+
stateSource.replace("require('../../../scripts/lib/workspace')", "require('./workspace')"),
|
|
79
|
+
'utf8'
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const packageSessionStart = path.join(pluginHooksRoot, 'session-start.js');
|
|
83
|
+
syncTree(path.join(sourceHooksScriptsRoot, 'openai-session-start.js'), packageSessionStart);
|
|
84
|
+
const sessionSource = fs.readFileSync(packageSessionStart, 'utf8');
|
|
85
|
+
fs.writeFileSync(
|
|
86
|
+
packageSessionStart,
|
|
87
|
+
sessionSource.replace("require('./lib/harness-state')", "require('./scripts/lib/harness-state')"),
|
|
88
|
+
'utf8'
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
console.log(`OpenAI plugin skills and runtime synchronized: ${expected.size} skills`);
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execFileSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
9
|
+
const STABLE_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
10
|
+
|
|
11
|
+
const VERSION_TARGETS = [
|
|
12
|
+
{ file: 'package.json', get: data => data.version, set: (data, v) => { data.version = v; } },
|
|
13
|
+
{
|
|
14
|
+
file: 'package-lock.json',
|
|
15
|
+
get: data => [data.version, data.packages && data.packages[''] && data.packages[''].version],
|
|
16
|
+
set: (data, v) => {
|
|
17
|
+
if (!data.packages || !data.packages['']) throw new Error('package-lock.json missing packages[""]');
|
|
18
|
+
data.version = v;
|
|
19
|
+
data.packages[''].version = v;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{ file: '.claude-plugin/plugin.json', get: data => data.version, set: (data, v) => { data.version = v; } },
|
|
23
|
+
{
|
|
24
|
+
file: '.claude-plugin/marketplace.json',
|
|
25
|
+
get: data => {
|
|
26
|
+
const entry = Array.isArray(data.plugins) ? data.plugins.find(p => p && p.name === 'harness-everything') : null;
|
|
27
|
+
return entry && entry.version;
|
|
28
|
+
},
|
|
29
|
+
set: (data, v) => {
|
|
30
|
+
const entry = Array.isArray(data.plugins) ? data.plugins.find(p => p && p.name === 'harness-everything') : null;
|
|
31
|
+
if (!entry) throw new Error('.claude-plugin/marketplace.json missing harness-everything plugin entry');
|
|
32
|
+
entry.version = v;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{ file: 'plugins/harness-everything/plugin.json', get: data => data.version, set: (data, v) => { data.version = v; } },
|
|
36
|
+
{ file: 'plugins/harness-everything/.codex-plugin/plugin.json', get: data => data.version, set: (data, v) => { data.version = v; } },
|
|
37
|
+
{ file: 'opencode-plugin/plugin.json', get: data => data.version, set: (data, v) => { data.version = v; } }
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
function isStableSemVer(version) {
|
|
41
|
+
return STABLE_SEMVER.test(String(version || ''));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readJsonTargets(root) {
|
|
45
|
+
return VERSION_TARGETS.map(target => {
|
|
46
|
+
const filePath = path.join(root, target.file);
|
|
47
|
+
if (!fs.existsSync(filePath)) throw new Error(`Missing release version target: ${target.file}`);
|
|
48
|
+
let data;
|
|
49
|
+
try {
|
|
50
|
+
data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throw new Error(`Invalid JSON in ${target.file}: ${error.message}`);
|
|
53
|
+
}
|
|
54
|
+
const current = target.get(data);
|
|
55
|
+
if (Array.isArray(current)) {
|
|
56
|
+
if (current.some(value => typeof value !== 'string' || !value)) {
|
|
57
|
+
throw new Error(`${target.file} is missing one or more owned version fields`);
|
|
58
|
+
}
|
|
59
|
+
} else if (typeof current !== 'string' || !current) {
|
|
60
|
+
throw new Error(`${target.file} is missing its owned version field`);
|
|
61
|
+
}
|
|
62
|
+
return { kind: 'json', target, filePath, data, current };
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function canonicalSkillDirs(root) {
|
|
67
|
+
const manifestPath = path.join(root, '.claude-plugin', 'plugin.json');
|
|
68
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
69
|
+
if (!Array.isArray(manifest.skills)) throw new Error('.claude-plugin/plugin.json missing skills array');
|
|
70
|
+
return manifest.skills.map(entry => {
|
|
71
|
+
const dir = path.resolve(root, entry);
|
|
72
|
+
const boundary = `${root}${path.sep}`;
|
|
73
|
+
if (dir !== root && !dir.startsWith(boundary)) throw new Error(`Skill path escapes repository: ${entry}`);
|
|
74
|
+
if (!fs.existsSync(path.join(dir, 'SKILL.md'))) throw new Error(`Skill path missing SKILL.md: ${entry}`);
|
|
75
|
+
return { entry, dir, rel: path.relative(root, dir).replace(/\\/g, '/') };
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function changedSkillDirs(root, base) {
|
|
80
|
+
if (!base) return [];
|
|
81
|
+
const skills = canonicalSkillDirs(root);
|
|
82
|
+
let changed;
|
|
83
|
+
try {
|
|
84
|
+
changed = execFileSync(
|
|
85
|
+
'git',
|
|
86
|
+
['diff', '--name-only', `${base}...HEAD`, '--', ...skills.map(skill => skill.rel)],
|
|
87
|
+
{ cwd: root, encoding: 'utf8' }
|
|
88
|
+
).split(/\r?\n/).filter(Boolean);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw new Error(`Unable to diff changed skills from ${base}: ${String(error.stderr || error.message).trim()}`);
|
|
91
|
+
}
|
|
92
|
+
return skills.filter(skill => changed.some(file => file === skill.rel || file.startsWith(`${skill.rel}/`)));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function walkSkillDocs(root, dir, out = []) {
|
|
96
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
97
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;
|
|
98
|
+
const full = path.join(dir, entry.name);
|
|
99
|
+
if (entry.isDirectory()) walkSkillDocs(root, full, out);
|
|
100
|
+
else if (entry.isFile() && entry.name === 'SKILL.md') out.push(full);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function readSkillTargets(root, base) {
|
|
106
|
+
const records = [];
|
|
107
|
+
for (const skill of changedSkillDirs(root, base)) {
|
|
108
|
+
for (const filePath of walkSkillDocs(root, skill.dir)) {
|
|
109
|
+
const text = fs.readFileSync(filePath, 'utf8');
|
|
110
|
+
const match = text.match(/^(\s{2}version:\s*)(\S+)(\s*)$/m);
|
|
111
|
+
const rel = path.relative(root, filePath).replace(/\\/g, '/');
|
|
112
|
+
if (!match) throw new Error(`${rel} is missing metadata.version`);
|
|
113
|
+
records.push({
|
|
114
|
+
kind: 'skill',
|
|
115
|
+
file: rel,
|
|
116
|
+
filePath,
|
|
117
|
+
text,
|
|
118
|
+
current: match[2],
|
|
119
|
+
prefix: match[1],
|
|
120
|
+
suffix: match[3]
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return records;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function syncReleaseVersion(root, version, options = {}) {
|
|
128
|
+
if (!isStableSemVer(version)) {
|
|
129
|
+
throw new Error(`Release version must be stable SemVer x.y.z (no prerelease/build suffix): ${version}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Read and validate every target before the first write. This keeps a missing
|
|
133
|
+
// manifest or malformed skill version from leaving a half-updated release tree.
|
|
134
|
+
const jsonRecords = readJsonTargets(root);
|
|
135
|
+
const skillRecords = readSkillTargets(root, options.base);
|
|
136
|
+
const records = [...jsonRecords, ...skillRecords];
|
|
137
|
+
|
|
138
|
+
if (options.checkOnly) {
|
|
139
|
+
const mismatches = [];
|
|
140
|
+
for (const record of records) {
|
|
141
|
+
const values = Array.isArray(record.current) ? record.current : [record.current];
|
|
142
|
+
if (values.some(value => value !== version)) {
|
|
143
|
+
const name = record.kind === 'json' ? record.target.file : record.file;
|
|
144
|
+
mismatches.push(`${name}: ${values.join(', ')} != ${version}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (mismatches.length) throw new Error(`Release version drift:\n${mismatches.join('\n')}`);
|
|
148
|
+
return records.map(record => record.kind === 'json' ? record.target.file : record.file);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (const record of jsonRecords) record.target.set(record.data, version);
|
|
152
|
+
for (const record of jsonRecords) {
|
|
153
|
+
fs.writeFileSync(record.filePath, `${JSON.stringify(record.data, null, 2)}\n`, 'utf8');
|
|
154
|
+
}
|
|
155
|
+
for (const record of skillRecords) {
|
|
156
|
+
const next = record.text.replace(
|
|
157
|
+
/^(\s{2}version:\s*)(\S+)(\s*)$/m,
|
|
158
|
+
`$1${version}$3`
|
|
159
|
+
);
|
|
160
|
+
fs.writeFileSync(record.filePath, next, 'utf8');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return records.map(record => record.kind === 'json' ? record.target.file : record.file);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function parseArgs(argv) {
|
|
167
|
+
let checkOnly = false;
|
|
168
|
+
let validateOnly = false;
|
|
169
|
+
let version = null;
|
|
170
|
+
let base = null;
|
|
171
|
+
for (let i = 0; i < argv.length; i++) {
|
|
172
|
+
const arg = argv[i];
|
|
173
|
+
if (arg === '--check') checkOnly = true;
|
|
174
|
+
else if (arg === '--validate') validateOnly = true;
|
|
175
|
+
else if (arg === '--base') {
|
|
176
|
+
base = argv[++i];
|
|
177
|
+
if (!base) throw new Error('--base requires a git ref');
|
|
178
|
+
} else if (!version) version = arg;
|
|
179
|
+
else throw new Error(`Unexpected argument: ${arg}`);
|
|
180
|
+
}
|
|
181
|
+
return { checkOnly, validateOnly, version, base };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function main(argv = process.argv.slice(2)) {
|
|
185
|
+
const { checkOnly, validateOnly, version, base } = parseArgs(argv);
|
|
186
|
+
if (!version) throw new Error('Usage: sync-release-version.js [--check|--validate] <x.y.z> [--base <git-ref>]');
|
|
187
|
+
if (validateOnly) {
|
|
188
|
+
if (!isStableSemVer(version)) throw new Error(`Release version must be stable SemVer x.y.z: ${version}`);
|
|
189
|
+
console.log(`Valid stable release version: ${version}`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const files = syncReleaseVersion(ROOT, version, { checkOnly, base });
|
|
193
|
+
const skillNote = base ? ` (changed skills since ${base} included)` : '';
|
|
194
|
+
console.log(`${checkOnly ? 'Verified' : 'Synchronized'} release version ${version} across ${files.length} files${skillNote}.`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (require.main === module) {
|
|
198
|
+
try {
|
|
199
|
+
main();
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error(error.message);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = {
|
|
207
|
+
VERSION_TARGETS,
|
|
208
|
+
isStableSemVer,
|
|
209
|
+
canonicalSkillDirs,
|
|
210
|
+
changedSkillDirs,
|
|
211
|
+
syncReleaseVersion
|
|
212
|
+
};
|
package/security-review/SKILL.md
CHANGED
|
@@ -4,13 +4,11 @@ description: Conducts STRIDE threat modeling, scans secrets via audit scripts, a
|
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: Miya Daniel
|
|
7
|
-
version: 0.
|
|
7
|
+
version: 0.4.0
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Security Review & Code Hardening
|
|
11
11
|
|
|
12
|
-
Hardens code against OWASP Top 10.
|
|
13
|
-
|
|
14
12
|
## USE FOR:
|
|
15
13
|
- AuthN or authZ implementation
|
|
16
14
|
- User input, uploads, new API endpoints
|
|
@@ -33,13 +31,12 @@ Hardens code against OWASP Top 10.
|
|
|
33
31
|
|
|
34
32
|
## Review Flow
|
|
35
33
|
|
|
36
|
-
1. Threat-model (`STRIDE`) per
|
|
34
|
+
1. Threat-model (`STRIDE`) per `<this-skill-dir>/guides/STRIDE-THREAT-MODEL.md`; misuse-case every endpoint/input.
|
|
37
35
|
2. Scan secrets and injection risks:
|
|
38
36
|
```bash
|
|
39
37
|
node "<this-skill-dir>/scripts/audit-secrets.js"
|
|
40
38
|
```
|
|
41
|
-
|
|
42
|
-
3. Fix findings per the boundary system; report to `docs/security-audit.md`, or `.github/harness-everything/security-audit.md` when docs/ is protected or absent.
|
|
39
|
+
3. Fix findings per the boundary system; report to `<workspace>/docs/security-audit.md`, or `<workspace>/.github/harness-everything/security-audit.md` when docs/ is protected or absent.
|
|
43
40
|
|
|
44
41
|
## Three-Tier Boundary System
|
|
45
42
|
|
|
@@ -47,4 +44,4 @@ Hardens code against OWASP Top 10.
|
|
|
47
44
|
- **Ask First**: CORS changes, auth/login flows, file uploads, rate limits.
|
|
48
45
|
- **Never Do**: commit hardcoded secrets (`sk-`), log passwords/tokens, use eval() or unescaped innerHTML.
|
|
49
46
|
|
|
50
|
-
Deep dive: references/security-checklist.md +
|
|
47
|
+
Deep dive: <this-skill-dir>/references/security-checklist.md + <this-skill-dir>/guides/OWASP-PATTERNS.md
|
|
@@ -7,14 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
dir = path.dirname(dir);
|
|
16
|
-
}
|
|
17
|
-
return process.cwd();
|
|
10
|
+
let getWorkspaceRoot;
|
|
11
|
+
try {
|
|
12
|
+
({ getWorkspaceRoot } = require('../../scripts/lib/workspace'));
|
|
13
|
+
} catch (err) {
|
|
14
|
+
getWorkspaceRoot = () => null;
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
const SECRET_PATTERNS = [
|
|
@@ -66,6 +63,10 @@ function scanFile(filePath, findings) {
|
|
|
66
63
|
}
|
|
67
64
|
|
|
68
65
|
const wsRoot = getWorkspaceRoot();
|
|
66
|
+
if (!wsRoot) {
|
|
67
|
+
console.error('[security-review] Cannot scan a workspace outside a git repository.');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
69
70
|
const findings = scanDir(wsRoot);
|
|
70
71
|
|
|
71
72
|
if (process.argv.includes('--json')) {
|