forge-workflow 0.0.9 → 0.1.0-beta.2
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/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
- package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
- package/.cursor/rules/permissions-guidance.mdc +2 -2
- package/.forge/hooks/check-tdd.js +3 -0
- package/.forge/hooks/forge-native-hook.js +245 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +151 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -106
- package/QUICKSTART.md +171 -0
- package/README.md +271 -363
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +466 -489
- package/docs/INDEX.md +93 -0
- package/docs/PROJECT_DESIGN.md +685 -0
- package/docs/architecture/index.md +66 -0
- package/docs/architecture/notes/README.md +35 -0
- package/docs/architecture/subsystems/README.md +46 -0
- package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
- package/docs/forge/VALIDATION.md +82 -0
- package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
- package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
- package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
- package/docs/guides/GREPTILE_SETUP.md +46 -0
- package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
- package/docs/guides/MIGRATION.md +56 -0
- package/docs/guides/SETUP.md +118 -0
- package/docs/guides/SUPPORT.md +185 -0
- package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
- package/docs/guides/memory-backends.md +183 -0
- package/docs/reference/ADAPTERS.md +128 -0
- package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
- package/docs/reference/COMMANDS.md +205 -0
- package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
- package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
- package/docs/reference/HERMES_INTEGRATION.md +118 -0
- package/docs/reference/INSIGHTS_RECAP.md +63 -0
- package/docs/reference/INSTALL.md +164 -0
- package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
- package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
- package/docs/reference/RELEASE.md +68 -0
- package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
- package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
- package/docs/reference/SKILLS.md +35 -0
- package/docs/reference/STATUS_BOARD.md +80 -0
- package/docs/reference/TEMPLATES.md +106 -0
- package/docs/reference/TOOLCHAIN.md +658 -0
- package/docs/reference/VALIDATION.md +82 -0
- package/docs/reference/agent-permissions.md +169 -0
- package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
- package/docs/reference/control-plane-guarantees.md +125 -0
- package/docs/reference/dependency-chain.md +331 -0
- package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
- package/docs/reference/forge-kernel-schema.md +72 -0
- package/docs/reference/kernel-conflict-evaluators.md +27 -0
- package/docs/reference/patch-md-format.md +77 -0
- package/docs/reference/protected-state-surfaces.md +59 -0
- package/docs/reference/shepherd.md +115 -0
- package/docs/reference/superpowers-analysis.md +320 -0
- package/docs/reference/superpowers-integration-options.md +404 -0
- package/docs/reference/test-environment.md +519 -0
- package/docs/reference/upgrade-safety.md +59 -0
- package/lefthook.yml +18 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1042 -0
- package/lib/adapters/greptile-review-adapter.js +141 -0
- package/lib/adapters/kernel-issue-adapter.js +101 -0
- package/lib/adapters/pr-state-adapter.js +484 -0
- package/lib/adoption-profiles.js +126 -0
- package/lib/agents/README.md +2 -6
- package/lib/agents/claude.plugin.json +3 -8
- package/lib/agents/codex.plugin.json +9 -1
- package/lib/agents/cursor.plugin.json +2 -6
- package/lib/agents/hermes.plugin.json +22 -0
- package/lib/agents-config.js +39 -1236
- package/lib/audit-evidence.js +282 -0
- package/lib/beads-setup.js +225 -28
- package/lib/beads-sync-scaffold.js +36 -107
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +744 -70
- package/lib/commands/_manifest.js +91 -0
- package/lib/commands/_registry.js +85 -34
- package/lib/commands/_resolve-command-opts.js +261 -0
- package/lib/commands/_serve-security.js +270 -0
- package/lib/commands/adapter.js +12 -0
- package/lib/commands/add.js +118 -0
- package/lib/commands/audit.js +70 -0
- package/lib/commands/blocked.js +5 -0
- package/lib/commands/board.js +64 -0
- package/lib/commands/claim.js +21 -2
- package/lib/commands/claims.js +7 -0
- package/lib/commands/clean.js +485 -75
- package/lib/commands/close.js +2 -2
- package/lib/commands/comment.js +5 -0
- package/lib/commands/control.js +148 -0
- package/lib/commands/create.js +2 -2
- package/lib/commands/dev.js +185 -7
- package/lib/commands/doc-gate.js +336 -0
- package/lib/commands/doctor.js +156 -0
- package/lib/commands/explain.js +15 -0
- package/lib/commands/export.js +237 -0
- package/lib/commands/gate.js +192 -0
- package/lib/commands/hooks.js +242 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +598 -0
- package/lib/commands/insights.js +79 -0
- package/lib/commands/issue.js +12 -1
- package/lib/commands/issues.js +66 -0
- package/lib/commands/lint.js +5 -0
- package/lib/commands/list.js +2 -2
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +523 -0
- package/lib/commands/new.js +12 -0
- package/lib/commands/options.js +241 -0
- package/lib/commands/orient.js +13 -0
- package/lib/commands/orphans.js +5 -0
- package/lib/commands/patch.js +67 -0
- package/lib/commands/plan.js +436 -24
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +69 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +116 -0
- package/lib/commands/recap.js +61 -0
- package/lib/commands/recommend.js +22 -2
- package/lib/commands/release.js +91 -0
- package/lib/commands/remember.js +74 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +851 -979
- package/lib/commands/shepherd.js +436 -0
- package/lib/commands/ship.js +23 -1
- package/lib/commands/show.js +2 -2
- package/lib/commands/stage.js +192 -0
- package/lib/commands/stale.js +5 -0
- package/lib/commands/status.js +329 -11
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +15 -2
- package/lib/commands/test.js +58 -7
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +56 -25
- package/lib/commands/worktree.js +308 -128
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +946 -0
- package/lib/dep-guard/keyword-ripple.js +184 -0
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +42 -17
- package/lib/doc-gate/declaration.js +177 -0
- package/lib/doc-gate/detect.js +289 -0
- package/lib/doc-gate/gate.js +375 -0
- package/lib/doc-gate/okf-config.js +128 -0
- package/lib/doc-gate/okf.js +429 -0
- package/lib/docs-command.js +1161 -6
- package/lib/forge-issues.js +697 -0
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +193 -0
- package/lib/global-flags.js +74 -0
- package/lib/greptile-match.js +7 -63
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +451 -0
- package/lib/inbox.js +391 -0
- package/lib/insights.js +397 -0
- package/lib/issue-adapter.js +156 -0
- package/lib/issue-backend.js +145 -0
- package/lib/issue-render.js +220 -0
- package/lib/issue-sync/authority.js +100 -0
- package/lib/issue-sync/github-pull.js +184 -0
- package/lib/issue-sync/import-primitives.js +98 -0
- package/lib/issue-sync/legacy-link-bridge.js +436 -0
- package/lib/issue-sync/link-store.js +292 -0
- package/lib/issue-sync/project-github.js +123 -0
- package/lib/issue-sync/reconcile.js +195 -0
- package/lib/issue-sync/schema.js +126 -0
- package/lib/kernel/backing-issue.js +305 -0
- package/lib/kernel/broker.js +1218 -0
- package/lib/kernel/cli-broker-factory.js +130 -0
- package/lib/kernel/conflict-signal.js +82 -0
- package/lib/kernel/evaluators.js +195 -0
- package/lib/kernel/fs-class.js +495 -0
- package/lib/kernel/issue-command-contract.js +559 -0
- package/lib/kernel/issue-id-resolver.js +186 -0
- package/lib/kernel/lease-enforcer.js +158 -0
- package/lib/kernel/migrations.js +333 -0
- package/lib/kernel/planning-buckets-schema.js +109 -0
- package/lib/kernel/projection-jsonl-writer.js +450 -0
- package/lib/kernel/readiness-model.js +329 -0
- package/lib/kernel/schema.js +356 -0
- package/lib/kernel/sqlite-driver.js +2504 -0
- package/lib/kernel/taxonomy-validator.js +394 -0
- package/lib/lefthook-check.js +8 -4
- package/lib/lefthook-wiring.js +413 -0
- package/lib/mcp-config-renderer.js +288 -0
- package/lib/memory/graphiti-mcp.js +106 -0
- package/lib/memory/router.js +387 -0
- package/lib/memory/typed-api.js +102 -0
- package/lib/memory-digest.js +195 -0
- package/lib/merge-rules.js +395 -0
- package/lib/migrate-dry-run.js +466 -0
- package/lib/orientation.js +863 -0
- package/lib/package-manager-remediation.js +103 -0
- package/lib/package-root.js +381 -0
- package/lib/patch-intent.js +890 -0
- package/lib/plugin-catalog.js +3 -4
- package/lib/plugin-manager.js +0 -5
- package/lib/pr-bundle.js +186 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/gather.js +124 -0
- package/lib/pr-monitor/journal.js +299 -0
- package/lib/pr-monitor/monitor.js +146 -0
- package/lib/pr-monitor/render-sticky.js +157 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1273 -0
- package/lib/pr-shepherd.js +494 -0
- package/lib/pr-state-validator.js +59 -0
- package/lib/preflight/gates.js +237 -0
- package/lib/preflight/runner.js +116 -0
- package/lib/project-discovery.js +0 -53
- package/lib/project-memory.js +166 -0
- package/lib/protected-path-manifest.js +281 -0
- package/lib/protected-state-surfaces.js +387 -0
- package/lib/release-readiness.js +2089 -0
- package/lib/reset.js +59 -45
- package/lib/review-adapter.js +68 -0
- package/lib/rules-sync.js +260 -0
- package/lib/runtime-health.js +332 -23
- package/lib/safety-config-renderer.js +268 -0
- package/lib/setup-action-log.js +1 -7
- package/lib/setup.js +27 -65
- package/lib/shell-utils.js +76 -6
- package/lib/skills-sync.js +330 -0
- package/lib/smart-status/conflicts.js +205 -0
- package/lib/smart-status/scoring.js +191 -0
- package/lib/status/beads-snapshot.js +145 -0
- package/lib/status/presenter.js +216 -0
- package/lib/status/snapshot.js +186 -0
- package/lib/sync-backend.js +202 -0
- package/lib/untrusted-content.js +52 -0
- package/lib/upgrade-safety.js +199 -0
- package/lib/workflow/enforce-stage.js +298 -47
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +159 -14
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +46 -36
- package/rules/documentation.md +19 -0
- package/rules/kernel-tracking.md +26 -0
- package/rules/security.md +22 -0
- package/rules/tdd.md +20 -0
- package/rules/workflow.md +27 -0
- package/scripts/auto-backing-issue.js +47 -0
- package/scripts/beads-context.sh +165 -22
- package/scripts/beads-migrate-to-dolt.sh +7 -0
- package/scripts/beads-upgrade-smoke.sh +284 -0
- package/scripts/behavioral-judge.sh +115 -11
- package/scripts/benchmark.js +349 -63
- package/scripts/bootstrap-windows-tools.sh +78 -0
- package/scripts/branch-protection.js +2 -3
- package/scripts/check-agents.js +34 -137
- package/scripts/commitlint.js +3 -1
- package/scripts/conflict-detect.sh +3 -0
- package/scripts/dep-guard-analyze.js +52 -17
- package/scripts/dep-guard-keyword-ripple.js +29 -0
- package/scripts/dep-guard-render-review.js +86 -0
- package/scripts/dep-guard.sh +64 -232
- package/scripts/file-index.sh +3 -0
- package/scripts/forge-team/lib/claim.sh +34 -18
- package/scripts/forge-team/lib/dashboard.sh +61 -86
- package/scripts/forge-team/lib/epic.sh +99 -263
- package/scripts/forge-team/lib/hooks.sh +26 -28
- package/scripts/forge-team/lib/identity.sh +4 -4
- package/scripts/forge-team/lib/sync-github.sh +144 -47
- package/scripts/forge-team/lib/verify.sh +93 -83
- package/scripts/forge-team/lib/workload.sh +41 -65
- package/scripts/forge-team/tests/claim.test.sh +25 -19
- package/scripts/forge-team/tests/dashboard.test.sh +31 -46
- package/scripts/forge-team/tests/epic.test.sh +52 -71
- package/scripts/forge-team/tests/hooks.test.sh +38 -50
- package/scripts/forge-team/tests/identity.test.sh +3 -3
- package/scripts/forge-team/tests/integration.test.sh +44 -66
- package/scripts/forge-team/tests/sync-github.test.sh +183 -79
- package/scripts/forge-team/tests/verify.test.sh +37 -46
- package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
- package/scripts/forge-team/tests/workload.test.sh +32 -66
- package/scripts/gen-command-manifest.js +153 -0
- package/scripts/gen-embedded-assets.mjs +129 -0
- package/scripts/install.ps1 +139 -0
- package/scripts/install.sh +268 -0
- package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
- package/scripts/lib/release-asset.mjs +84 -0
- package/scripts/parity-check.mjs +145 -0
- package/scripts/parity-check.test.mjs +58 -0
- package/scripts/pin-agentic-workflow-images.js +112 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +108 -0
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status-score.js +31 -0
- package/scripts/smart-status-sessions.js +51 -0
- package/scripts/smart-status.sh +117 -369
- package/scripts/spikes/config-race-bench.js +111 -0
- package/scripts/spikes/harness-capability-matrix.js +13 -0
- package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
- package/scripts/spikes/protected-path-manifest.js +20 -0
- package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
- package/scripts/sync-agent-skills.js +62 -0
- package/scripts/sync-agentic-workflow.js +48 -0
- package/scripts/sync-utils.sh +3 -0
- package/scripts/test-ci-shard.js +251 -0
- package/scripts/test-dashboard.js +188 -52
- package/scripts/test-full-suite.js +186 -0
- package/scripts/test-profile.js +278 -0
- package/scripts/test.js +302 -28
- package/scripts/validate.js +143 -0
- package/scripts/validate.sh +18 -1
- package/skills/claim-safety/SKILL.md +102 -0
- package/skills/claim-safety/evals/evals.json +46 -0
- package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
- package/skills/dev/evals/evals.json +50 -0
- package/skills/hermes-forge/SKILL.md +185 -0
- package/skills/hermes-forge/evals/evals.json +46 -0
- package/skills/issue-basics/SKILL.md +111 -0
- package/skills/issue-basics/evals/evals.json +46 -0
- package/skills/kernel/SKILL.md +166 -0
- package/skills/kernel/evals/evals.json +50 -0
- package/skills/memory/SKILL.md +102 -0
- package/skills/parallel-deep-research/SKILL.md +14 -11
- package/skills/parallel-deep-research/evals/evals.json +11 -27
- package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
- package/skills/plan/evals/evals.json +42 -0
- package/skills/research/SKILL.md +195 -0
- package/skills/research/evals/evals.json +42 -0
- package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
- package/skills/review/evals/evals.json +42 -0
- package/skills/rollback/SKILL.md +110 -0
- package/skills/rollback/evals/evals.json +46 -0
- package/skills/rollback/references/methods.md +204 -0
- package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
- package/skills/shepherd/SKILL.md +66 -0
- package/skills/shepherd/evals/evals.json +42 -0
- package/skills/ship/SKILL.md +251 -0
- package/skills/ship/evals/evals.json +42 -0
- package/skills/smith/SKILL.md +142 -0
- package/skills/smith/evals/evals.json +46 -0
- package/skills/smith/references/autonomy-and-gates.md +94 -0
- package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
- package/skills/sonarcloud/evals/evals.json +46 -0
- package/skills/sonarcloud-analysis/SKILL.md +18 -13
- package/skills/sonarcloud-analysis/evals/evals.json +11 -15
- package/skills/status/SKILL.md +102 -0
- package/skills/status/evals/evals.json +50 -0
- package/skills/triage-ready/SKILL.md +121 -0
- package/skills/triage-ready/evals/evals.json +42 -0
- package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
- package/skills/validate/evals/evals.json +42 -0
- package/skills/verify/SKILL.md +299 -0
- package/skills/verify/evals/evals.json +50 -0
- package/.claude/commands/dev.md +0 -345
- package/.claude/commands/plan.md +0 -566
- package/.claude/commands/premerge.md +0 -186
- package/.claude/commands/research.md +0 -42
- package/.claude/commands/review.md +0 -451
- package/.claude/commands/rollback.md +0 -721
- package/.claude/commands/ship.md +0 -213
- package/.claude/commands/sonarcloud.md +0 -152
- package/.claude/commands/status.md +0 -90
- package/.claude/commands/validate.md +0 -288
- package/.claude/commands/verify.md +0 -269
- package/.claude/rules/workflow.md +0 -121
- package/.cline/workflows/dev.md +0 -342
- package/.cline/workflows/plan.md +0 -563
- package/.cline/workflows/premerge.md +0 -183
- package/.cline/workflows/research.md +0 -39
- package/.cline/workflows/review.md +0 -448
- package/.cline/workflows/rollback.md +0 -718
- package/.cline/workflows/ship.md +0 -210
- package/.cline/workflows/sonarcloud.md +0 -146
- package/.cline/workflows/status.md +0 -87
- package/.cline/workflows/validate.md +0 -285
- package/.cline/workflows/verify.md +0 -266
- package/.codex/config.toml +0 -11
- package/.codex/skills/dev/SKILL.md +0 -345
- package/.codex/skills/plan/SKILL.md +0 -566
- package/.codex/skills/premerge/SKILL.md +0 -186
- package/.codex/skills/research/SKILL.md +0 -42
- package/.codex/skills/review/SKILL.md +0 -451
- package/.codex/skills/rollback/SKILL.md +0 -721
- package/.codex/skills/ship/SKILL.md +0 -213
- package/.codex/skills/sonarcloud/SKILL.md +0 -149
- package/.codex/skills/status/SKILL.md +0 -90
- package/.codex/skills/validate/SKILL.md +0 -288
- package/.codex/skills/verify/SKILL.md +0 -269
- package/.cursor/commands/dev.md +0 -342
- package/.cursor/commands/plan.md +0 -563
- package/.cursor/commands/premerge.md +0 -183
- package/.cursor/commands/research.md +0 -39
- package/.cursor/commands/review.md +0 -448
- package/.cursor/commands/ship.md +0 -210
- package/.cursor/commands/sonarcloud.md +0 -146
- package/.cursor/commands/status.md +0 -87
- package/.cursor/commands/validate.md +0 -285
- package/.cursor/commands/verify.md +0 -266
- package/.cursorrules +0 -149
- package/.github/prompts/premerge.prompt.md +0 -188
- package/.github/prompts/research.prompt.md +0 -44
- package/.github/prompts/rollback.prompt.md +0 -723
- package/.github/prompts/ship.prompt.md +0 -215
- package/.github/prompts/status.prompt.md +0 -92
- package/.github/prompts/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -56
- package/.github/workflows/github-to-beads.yml +0 -97
- package/.kilocode/workflows/dev.md +0 -346
- package/.kilocode/workflows/plan.md +0 -567
- package/.kilocode/workflows/premerge.md +0 -187
- package/.kilocode/workflows/research.md +0 -43
- package/.kilocode/workflows/review.md +0 -452
- package/.kilocode/workflows/rollback.md +0 -722
- package/.kilocode/workflows/ship.md +0 -214
- package/.kilocode/workflows/sonarcloud.md +0 -150
- package/.kilocode/workflows/status.md +0 -91
- package/.kilocode/workflows/validate.md +0 -289
- package/.kilocode/workflows/verify.md +0 -270
- package/.opencode/commands/dev.md +0 -345
- package/.opencode/commands/plan.md +0 -566
- package/.opencode/commands/premerge.md +0 -186
- package/.opencode/commands/research.md +0 -42
- package/.opencode/commands/review.md +0 -451
- package/.opencode/commands/rollback.md +0 -721
- package/.opencode/commands/ship.md +0 -213
- package/.opencode/commands/sonarcloud.md +0 -149
- package/.opencode/commands/status.md +0 -90
- package/.opencode/commands/validate.md +0 -288
- package/.opencode/commands/verify.md +0 -269
- package/.roo/commands/dev.md +0 -346
- package/.roo/commands/plan.md +0 -567
- package/.roo/commands/premerge.md +0 -187
- package/.roo/commands/research.md +0 -43
- package/.roo/commands/review.md +0 -452
- package/.roo/commands/rollback.md +0 -722
- package/.roo/commands/ship.md +0 -214
- package/.roo/commands/sonarcloud.md +0 -150
- package/.roo/commands/status.md +0 -91
- package/.roo/commands/validate.md +0 -289
- package/.roo/commands/verify.md +0 -270
- package/docs/BEADS_GITHUB_SYNC.md +0 -255
- package/docs/GREPTILE_SETUP.md +0 -400
- package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
- package/docs/SETUP.md +0 -663
- package/docs/VALIDATION.md +0 -363
- package/lib/agents/cline.plugin.json +0 -29
- package/lib/agents/copilot.plugin.json +0 -24
- package/lib/agents/kilocode.plugin.json +0 -22
- package/lib/agents/opencode.plugin.json +0 -23
- package/lib/agents/roo.plugin.json +0 -30
- package/lib/beads-health-check.js +0 -143
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -567
- package/scripts/github-beads-sync/comment.mjs +0 -64
- package/scripts/github-beads-sync/config.mjs +0 -148
- package/scripts/github-beads-sync/github-api.mjs +0 -131
- package/scripts/github-beads-sync/index.mjs +0 -332
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -78
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
- package/scripts/github-beads-sync/run-bd.mjs +0 -161
- package/scripts/github-beads-sync/sanitize.mjs +0 -121
- package/scripts/github-beads-sync.config.json +0 -26
- package/scripts/sync-commands.js +0 -600
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { performance } = require('node:perf_hooks');
|
|
5
|
+
|
|
6
|
+
function readNumberFlag(args, name, fallback) {
|
|
7
|
+
const index = args.indexOf(name);
|
|
8
|
+
if (index === -1) return fallback;
|
|
9
|
+
const value = Number(args[index + 1]);
|
|
10
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
11
|
+
throw new Error(`${name} must be a non-negative number`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function assertPositiveInteger(name, value) {
|
|
17
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
18
|
+
throw new Error(`${name} must be a positive integer`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function machinePatch(trial, machine) {
|
|
23
|
+
const sharedValue = trial % 10 === 0 ? `shared-${trial}` : undefined;
|
|
24
|
+
const conflictingValue = trial === 37 ? `${machine}-conflict-${trial}` : undefined;
|
|
25
|
+
return {
|
|
26
|
+
machine,
|
|
27
|
+
global: {
|
|
28
|
+
...(sharedValue ? { 'stage.review.timeout': sharedValue } : {}),
|
|
29
|
+
...(conflictingValue ? { 'stage.dev.parallelism': conflictingValue } : {}),
|
|
30
|
+
},
|
|
31
|
+
machineLocal: {
|
|
32
|
+
[`${machine}.shell`]: machine === 'machine-a' ? 'git-bash' : 'pwsh',
|
|
33
|
+
[`${machine}.workspace`]: `.forge/machines/${machine}`,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function mergeTrial(trial) {
|
|
39
|
+
const left = machinePatch(trial, 'machine-a');
|
|
40
|
+
const right = machinePatch(trial, 'machine-b');
|
|
41
|
+
const merged = { global: {}, machineLocal: {} };
|
|
42
|
+
const conflicts = [];
|
|
43
|
+
|
|
44
|
+
for (const patch of [left, right]) {
|
|
45
|
+
Object.assign(merged.machineLocal, patch.machineLocal);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const globalKeys = new Set([...Object.keys(left.global), ...Object.keys(right.global)]);
|
|
49
|
+
for (const key of globalKeys) {
|
|
50
|
+
const leftHas = Object.prototype.hasOwnProperty.call(left.global, key);
|
|
51
|
+
const rightHas = Object.prototype.hasOwnProperty.call(right.global, key);
|
|
52
|
+
if (leftHas && rightHas && left.global[key] !== right.global[key]) {
|
|
53
|
+
conflicts.push(key);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
merged.global[key] = leftHas ? left.global[key] : right.global[key];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
trial,
|
|
61
|
+
manualResolve: conflicts.length > 0,
|
|
62
|
+
conflicts,
|
|
63
|
+
mergedKeyCount: Object.keys(merged.global).length + Object.keys(merged.machineLocal).length,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function runBench(options = {}) {
|
|
68
|
+
const trials = options.trials ?? 50;
|
|
69
|
+
const threshold = options.threshold ?? 0.05;
|
|
70
|
+
assertPositiveInteger('trials', trials);
|
|
71
|
+
|
|
72
|
+
const start = performance.now();
|
|
73
|
+
const results = Array.from({ length: trials }, (_, index) => mergeTrial(index + 1));
|
|
74
|
+
const manualResolves = results.filter((result) => result.manualResolve);
|
|
75
|
+
const manualResolveRate = manualResolves.length / trials;
|
|
76
|
+
const elapsedMs = performance.now() - start;
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
spike: 'cross-machine-config-race',
|
|
80
|
+
trials,
|
|
81
|
+
manualResolves: manualResolves.length,
|
|
82
|
+
manualResolveRate,
|
|
83
|
+
threshold,
|
|
84
|
+
passed: manualResolveRate < threshold,
|
|
85
|
+
elapsedMs: Number(elapsedMs.toFixed(3)),
|
|
86
|
+
complexity: 'O(trials * changed keys); machine-local files avoid shared-file git conflicts',
|
|
87
|
+
conflictTrials: manualResolves.map((result) => ({
|
|
88
|
+
trial: result.trial,
|
|
89
|
+
conflicts: result.conflicts,
|
|
90
|
+
})),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function main() {
|
|
95
|
+
const args = process.argv.slice(2);
|
|
96
|
+
const result = runBench({
|
|
97
|
+
trials: readNumberFlag(args, '--trials', 50),
|
|
98
|
+
threshold: readNumberFlag(args, '--threshold', 0.05),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
console.log(JSON.stringify(result, null, args.includes('--json') ? 0 : 2));
|
|
102
|
+
if (!result.passed) {
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (require.main === module) {
|
|
108
|
+
main();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = { runBench, mergeTrial };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { buildHarnessCapabilityEvidence } = require('../../lib/harness-capability-matrix');
|
|
5
|
+
|
|
6
|
+
function main() {
|
|
7
|
+
const evidence = buildHarnessCapabilityEvidence();
|
|
8
|
+
console.log(JSON.stringify(evidence, null, 2));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (require.main === module) {
|
|
12
|
+
main();
|
|
13
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { performance } = require('node:perf_hooks');
|
|
5
|
+
|
|
6
|
+
function readNumberFlag(args, name, fallback) {
|
|
7
|
+
const index = args.indexOf(name);
|
|
8
|
+
if (index === -1) return fallback;
|
|
9
|
+
const value = Number(args[index + 1]);
|
|
10
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
11
|
+
throw new Error(`${name} must be a non-negative number`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function assertPositiveInteger(name, value) {
|
|
17
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
18
|
+
throw new Error(`${name} must be a positive integer`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function assertNonNegativeInteger(name, value) {
|
|
23
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
24
|
+
throw new Error(`${name} must be a non-negative integer`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function runBench(options = {}) {
|
|
29
|
+
const anchorCount = options.anchorCount ?? 500;
|
|
30
|
+
const patchCount = options.patchCount ?? 50;
|
|
31
|
+
const renameCount = options.renameCount ?? patchCount;
|
|
32
|
+
const unmappedCount = options.unmappedCount ?? 2;
|
|
33
|
+
const threshold = options.threshold ?? 0.10;
|
|
34
|
+
|
|
35
|
+
assertPositiveInteger('anchorCount', anchorCount);
|
|
36
|
+
assertPositiveInteger('patchCount', patchCount);
|
|
37
|
+
assertNonNegativeInteger('renameCount', renameCount);
|
|
38
|
+
assertNonNegativeInteger('unmappedCount', unmappedCount);
|
|
39
|
+
if (patchCount > anchorCount) {
|
|
40
|
+
throw new Error('patchCount must be <= anchorCount');
|
|
41
|
+
}
|
|
42
|
+
if (renameCount > patchCount) {
|
|
43
|
+
throw new Error('renameCount must be <= patchCount');
|
|
44
|
+
}
|
|
45
|
+
if (unmappedCount > renameCount) {
|
|
46
|
+
throw new Error('unmappedCount must be <= renameCount');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const start = performance.now();
|
|
50
|
+
const oldAnchors = Array.from({ length: anchorCount }, (_, i) => `stage.${i}.body`);
|
|
51
|
+
const patches = oldAnchors.slice(0, patchCount).map((anchor, i) => ({
|
|
52
|
+
id: `patch-${String(i + 1).padStart(3, '0')}`,
|
|
53
|
+
anchor,
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
const currentAnchors = new Set(oldAnchors);
|
|
57
|
+
const aliases = new Map();
|
|
58
|
+
for (let i = 0; i < renameCount; i += 1) {
|
|
59
|
+
const oldAnchor = oldAnchors[i];
|
|
60
|
+
const newAnchor = `${oldAnchor}.v2`;
|
|
61
|
+
currentAnchors.delete(oldAnchor);
|
|
62
|
+
currentAnchors.add(newAnchor);
|
|
63
|
+
if (i >= unmappedCount) {
|
|
64
|
+
aliases.set(oldAnchor, newAnchor);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let resolved = 0;
|
|
69
|
+
let orphaned = 0;
|
|
70
|
+
const orphanIds = [];
|
|
71
|
+
for (const patch of patches) {
|
|
72
|
+
if (currentAnchors.has(patch.anchor)) {
|
|
73
|
+
resolved += 1;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const alias = aliases.get(patch.anchor);
|
|
77
|
+
if (alias && currentAnchors.has(alias)) {
|
|
78
|
+
resolved += 1;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
orphaned += 1;
|
|
82
|
+
orphanIds.push(patch.id);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const elapsedMs = performance.now() - start;
|
|
86
|
+
const orphanRate = orphaned / patchCount;
|
|
87
|
+
return {
|
|
88
|
+
spike: 'patch-anchor-stability',
|
|
89
|
+
patchCount,
|
|
90
|
+
anchorCount,
|
|
91
|
+
renamedAnchors: renameCount,
|
|
92
|
+
aliasEntries: aliases.size,
|
|
93
|
+
orphaned,
|
|
94
|
+
resolved,
|
|
95
|
+
orphanRate,
|
|
96
|
+
threshold,
|
|
97
|
+
passed: orphanRate < threshold,
|
|
98
|
+
elapsedMs: Number(elapsedMs.toFixed(3)),
|
|
99
|
+
complexity: 'O(patches + anchors + aliases) with Map/Set lookups',
|
|
100
|
+
orphanIds,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function main() {
|
|
105
|
+
const args = process.argv.slice(2);
|
|
106
|
+
const patchCount = readNumberFlag(args, '--patches', 50);
|
|
107
|
+
const result = runBench({
|
|
108
|
+
anchorCount: readNumberFlag(args, '--anchors', 500),
|
|
109
|
+
patchCount,
|
|
110
|
+
renameCount: readNumberFlag(args, '--renames', patchCount),
|
|
111
|
+
unmappedCount: readNumberFlag(args, '--unmapped', 2),
|
|
112
|
+
threshold: readNumberFlag(args, '--threshold', 0.10),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
console.log(JSON.stringify(result, null, args.includes('--json') ? 0 : 2));
|
|
116
|
+
if (!result.passed) {
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (require.main === module) {
|
|
122
|
+
main();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = { runBench };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const {
|
|
6
|
+
buildProtectedPathManifestEvidence,
|
|
7
|
+
loadProtectedPathManifest,
|
|
8
|
+
} = require('../../lib/protected-path-manifest');
|
|
9
|
+
|
|
10
|
+
const root = path.resolve(__dirname, '..', '..');
|
|
11
|
+
const manifestPath = path.join(root, '.forge', 'protected-paths.yaml');
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const manifest = loadProtectedPathManifest(manifestPath);
|
|
15
|
+
process.stdout.write(`${JSON.stringify(buildProtectedPathManifestEvidence(manifest), null, 2)}\n`);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18
|
+
process.stderr.write(`Failed to build protected path manifest evidence from ${manifestPath}: ${message}\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const os = require('node:os');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const YAML = require('yaml');
|
|
8
|
+
|
|
9
|
+
const CANONICAL_SKILL = {
|
|
10
|
+
name: 'guard-rails-audit',
|
|
11
|
+
description: 'Use when asked to inspect Forge L1 rail enforcement, audit events, or protected path policy.',
|
|
12
|
+
body: [
|
|
13
|
+
'# Guard Rails Audit',
|
|
14
|
+
'',
|
|
15
|
+
'Check Forge L1 rails, audit-event coverage, and protected path policy before recommending changes.',
|
|
16
|
+
].join('\n'),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const MATCHING_PROMPT = 'Inspect Forge L1 rail enforcement and audit events for protected path policy.';
|
|
20
|
+
const NON_MATCHING_PROMPT = 'Draft release notes for a minor README copy edit.';
|
|
21
|
+
const FIXTURE_FILE = 'src/index.js';
|
|
22
|
+
|
|
23
|
+
const SOURCES = [
|
|
24
|
+
{
|
|
25
|
+
label: 'S1',
|
|
26
|
+
harness: 'claude-code',
|
|
27
|
+
url: 'https://code.claude.com/docs/en/skills',
|
|
28
|
+
summary: 'Claude Code project skills live under .claude/skills/<skill>/SKILL.md and use description metadata for automatic invocation.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: 'S2',
|
|
32
|
+
harness: 'cursor',
|
|
33
|
+
url: 'https://docs.cursor.com/en/context',
|
|
34
|
+
summary: 'Cursor Project Rules live under .cursor/rules as .mdc files; Agent Requested rules are description-driven.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: 'S3',
|
|
38
|
+
harness: 'codex-cli',
|
|
39
|
+
url: 'https://github.com/openai/codex/blob/main/codex-rs/skills/src/assets/samples/skill-creator/SKILL.md',
|
|
40
|
+
summary: 'Codex skills use SKILL.md frontmatter name and description to determine when the skill gets used.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: 'S4',
|
|
44
|
+
harness: 'forge',
|
|
45
|
+
url: 'lib/codex-skills.js',
|
|
46
|
+
summary: 'Forge packages repository Codex skills from .codex/skills for installation into $CODEX_HOME/skills.',
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const HARNESS_TARGETS = [
|
|
51
|
+
{
|
|
52
|
+
harness: 'claude-code',
|
|
53
|
+
target: '.claude/skills/guard-rails-audit/SKILL.md',
|
|
54
|
+
explicitInvocation: '/guard-rails-audit',
|
|
55
|
+
sourceLabel: 'S1',
|
|
56
|
+
source: 'Claude Code skill directory target',
|
|
57
|
+
buildFrontmatter: () => ({
|
|
58
|
+
name: CANONICAL_SKILL.name,
|
|
59
|
+
description: CANONICAL_SKILL.description,
|
|
60
|
+
}),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
harness: 'cursor',
|
|
64
|
+
target: '.cursor/rules/guard-rails-audit.mdc',
|
|
65
|
+
explicitInvocation: '/guard-rails-audit',
|
|
66
|
+
sourceLabel: 'S2',
|
|
67
|
+
source: 'Cursor project rule with documented description/globs/alwaysApply metadata',
|
|
68
|
+
buildFrontmatter: () => ({
|
|
69
|
+
description: CANONICAL_SKILL.description,
|
|
70
|
+
globs: null,
|
|
71
|
+
alwaysApply: false,
|
|
72
|
+
}),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
harness: 'codex-cli',
|
|
76
|
+
target: '.codex/skills/guard-rails-audit/SKILL.md',
|
|
77
|
+
explicitInvocation: '/guard-rails-audit',
|
|
78
|
+
sourceLabel: 'S4',
|
|
79
|
+
source: 'Forge repository Codex skills package surface; custom slash prompt files are intentionally not used',
|
|
80
|
+
buildFrontmatter: () => ({
|
|
81
|
+
name: CANONICAL_SKILL.name,
|
|
82
|
+
description: CANONICAL_SKILL.description,
|
|
83
|
+
}),
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
function buildMarkdown(frontmatter, body) {
|
|
88
|
+
return `---\n${YAML.stringify(frontmatter, {
|
|
89
|
+
lineWidth: 0,
|
|
90
|
+
defaultKeyType: 'PLAIN',
|
|
91
|
+
defaultStringType: 'PLAIN',
|
|
92
|
+
}).trimEnd()}\n---\n\n${body}\n`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function parseFrontmatter(content) {
|
|
96
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
97
|
+
if (!match) return {};
|
|
98
|
+
const parsed = YAML.parse(match[1]);
|
|
99
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function writeFile(root, relativePath, content) {
|
|
103
|
+
const absolutePath = path.join(root, relativePath);
|
|
104
|
+
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
|
|
105
|
+
fs.writeFileSync(absolutePath, content);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isDirectoryEmpty(directory) {
|
|
109
|
+
return !fs.existsSync(directory) || fs.readdirSync(directory).length === 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function materializeFixture(root) {
|
|
113
|
+
if (!isDirectoryEmpty(root)) {
|
|
114
|
+
throw new Error(`fixture directory must be empty: ${root}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fs.mkdirSync(root, { recursive: true });
|
|
118
|
+
writeFile(root, FIXTURE_FILE, 'export function fixture() { return true; }\n');
|
|
119
|
+
|
|
120
|
+
for (const target of HARNESS_TARGETS) {
|
|
121
|
+
writeFile(
|
|
122
|
+
root,
|
|
123
|
+
target.target,
|
|
124
|
+
buildMarkdown(target.buildFrontmatter(), CANONICAL_SKILL.body),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { root, skill: CANONICAL_SKILL, targets: HARNESS_TARGETS };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function tokens(value) {
|
|
132
|
+
return String(value)
|
|
133
|
+
.toLowerCase()
|
|
134
|
+
.match(/[a-z0-9]+/g)
|
|
135
|
+
?.filter((token) => token.length >= 4) || [];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function descriptionMatches(description, prompt) {
|
|
139
|
+
const promptTokens = new Set(tokens(prompt));
|
|
140
|
+
const triggerTokens = tokens(description)
|
|
141
|
+
.filter((token) => !['when', 'asked', 'inspect'].includes(token));
|
|
142
|
+
|
|
143
|
+
return triggerTokens.some((token) => promptTokens.has(token));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function cursorGlobMatches(globs, filePath) {
|
|
147
|
+
void filePath;
|
|
148
|
+
if (!Array.isArray(globs) || globs.length === 0) return true;
|
|
149
|
+
return globs.includes('**/*') || globs.includes(filePath);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function validateHarness(root, target) {
|
|
153
|
+
const absolutePath = path.join(root, target.target);
|
|
154
|
+
const failures = [];
|
|
155
|
+
|
|
156
|
+
if (!fs.existsSync(absolutePath)) {
|
|
157
|
+
failures.push(`missing ${target.target}`);
|
|
158
|
+
return { ...target, passed: false, failures };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const frontmatter = parseFrontmatter(fs.readFileSync(absolutePath, 'utf8'));
|
|
162
|
+
const description = frontmatter.description;
|
|
163
|
+
const positiveMatch = descriptionMatches(description, MATCHING_PROMPT);
|
|
164
|
+
const negativeMatch = descriptionMatches(description, NON_MATCHING_PROMPT);
|
|
165
|
+
|
|
166
|
+
if (description !== CANONICAL_SKILL.description) {
|
|
167
|
+
failures.push('description does not match canonical skill description');
|
|
168
|
+
}
|
|
169
|
+
if (!positiveMatch) {
|
|
170
|
+
failures.push('matching prompt did not select the skill by description');
|
|
171
|
+
}
|
|
172
|
+
if (negativeMatch) {
|
|
173
|
+
failures.push('unrelated prompt selected the skill');
|
|
174
|
+
}
|
|
175
|
+
if (target.explicitInvocation !== `/${CANONICAL_SKILL.name}`) {
|
|
176
|
+
failures.push('explicit slash invocation does not match canonical skill name');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (target.harness === 'claude-code') {
|
|
180
|
+
if (frontmatter.name !== CANONICAL_SKILL.name) failures.push('Claude skill name is missing or incorrect');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (target.harness === 'cursor') {
|
|
184
|
+
if (frontmatter.alwaysApply !== false) failures.push('Cursor rule must keep alwaysApply: false for request-time selection');
|
|
185
|
+
if (!cursorGlobMatches(frontmatter.globs, FIXTURE_FILE)) failures.push(`Cursor globs do not include ${FIXTURE_FILE}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (target.harness === 'codex-cli') {
|
|
189
|
+
if (!target.target.startsWith('.codex/skills/') || target.target.includes('/prompts/')) {
|
|
190
|
+
failures.push('Codex target must use .codex/skills, not undocumented prompt/slash files');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
...target,
|
|
196
|
+
passed: failures.length === 0,
|
|
197
|
+
descriptionMatched: positiveMatch,
|
|
198
|
+
negativePromptIgnored: !negativeMatch,
|
|
199
|
+
frontmatter,
|
|
200
|
+
failures,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function runParity(options = {}) {
|
|
205
|
+
const root = options.fixtureDir || fs.mkdtempSync(path.join(os.tmpdir(), 'forge-w0-harness-parity-'));
|
|
206
|
+
const cleanup = options.fixtureDir ? false : options.cleanup !== false;
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
materializeFixture(root);
|
|
210
|
+
const harnesses = HARNESS_TARGETS.map((target) => validateHarness(root, target));
|
|
211
|
+
const passed = harnesses.every((result) => result.passed);
|
|
212
|
+
const feasibleCount = harnesses.filter((result) => result.passed).length;
|
|
213
|
+
const knownIssues = passed ? [] : [{
|
|
214
|
+
issue: 'Cross-harness skill render parity did not pass for all three target harnesses.',
|
|
215
|
+
d38: 'D38 kills v3 if two of Claude/Cursor/Codex cannot render a single skill correctly by the W3 checkpoint.',
|
|
216
|
+
feasibleCount,
|
|
217
|
+
}];
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
fixture: 'w0-skill-auto-invoke-parity',
|
|
221
|
+
fixtureRoot: root,
|
|
222
|
+
matchingPrompt: MATCHING_PROMPT,
|
|
223
|
+
nonMatchingPrompt: NON_MATCHING_PROMPT,
|
|
224
|
+
canonicalSkill: CANONICAL_SKILL,
|
|
225
|
+
harnesses,
|
|
226
|
+
sources: SOURCES,
|
|
227
|
+
proofBoundary: {
|
|
228
|
+
level: 'metadata-surface',
|
|
229
|
+
liveAgentInvocation: 'not-run',
|
|
230
|
+
codexRuntimeDiscovery: 'not-run',
|
|
231
|
+
reason: 'closed-source harness model invocation is outside this deterministic fixture; .codex/skills is the Forge repository packaging surface, not proof of direct Codex runtime discovery',
|
|
232
|
+
},
|
|
233
|
+
passed,
|
|
234
|
+
knownIssues,
|
|
235
|
+
};
|
|
236
|
+
} finally {
|
|
237
|
+
if (cleanup) fs.rmSync(root, { recursive: true, force: true });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function printText(result) {
|
|
242
|
+
console.log(`W0 skill auto-invoke parity: ${result.passed ? 'PASS' : 'FAIL'}`);
|
|
243
|
+
for (const harness of result.harnesses) {
|
|
244
|
+
const status = harness.passed ? 'PASS' : 'FAIL';
|
|
245
|
+
console.log(`${status} ${harness.harness} ${harness.target}`);
|
|
246
|
+
for (const failure of harness.failures) {
|
|
247
|
+
console.log(` - ${failure}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (result.knownIssues.length > 0) {
|
|
251
|
+
for (const issue of result.knownIssues) {
|
|
252
|
+
console.log(`Known issue: ${issue.issue} ${issue.d38}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function readStringFlag(args, name) {
|
|
258
|
+
const index = args.indexOf(name);
|
|
259
|
+
if (index === -1) return undefined;
|
|
260
|
+
const value = args[index + 1];
|
|
261
|
+
if (!value || value.startsWith('-')) return undefined;
|
|
262
|
+
return value;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function main() {
|
|
266
|
+
const args = process.argv.slice(2);
|
|
267
|
+
const result = runParity({ fixtureDir: readStringFlag(args, '--fixture-dir') });
|
|
268
|
+
|
|
269
|
+
if (args.includes('--json')) {
|
|
270
|
+
console.log(JSON.stringify(result));
|
|
271
|
+
} else {
|
|
272
|
+
printText(result);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (!result.passed) process.exitCode = 1;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (require.main === module) {
|
|
279
|
+
main();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
module.exports = {
|
|
283
|
+
CANONICAL_SKILL,
|
|
284
|
+
HARNESS_TARGETS,
|
|
285
|
+
MATCHING_PROMPT,
|
|
286
|
+
NON_MATCHING_PROMPT,
|
|
287
|
+
SOURCES,
|
|
288
|
+
descriptionMatches,
|
|
289
|
+
materializeFixture,
|
|
290
|
+
parseFrontmatter,
|
|
291
|
+
runParity,
|
|
292
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Pre-commit auto-sync for the committed `.agents/skills` mirror.
|
|
6
|
+
*
|
|
7
|
+
* `.agents/skills` is Codex's repo-local discovery path and the ONE skill mirror
|
|
8
|
+
* committed to the repo (so a teammate who clones WITHOUT running `forge setup`
|
|
9
|
+
* still gets Forge skills/stages auto-discovered). It must stay byte-identical to
|
|
10
|
+
* the canonical `skills/` source, which the structural drift gate enforces.
|
|
11
|
+
*
|
|
12
|
+
* This hook removes the regen friction: whenever `skills/**` is staged, it
|
|
13
|
+
* regenerates `.agents/skills` from `skills/` (reusing the same
|
|
14
|
+
* `populateCodexRepoSkills` / `populateAgentSkills` sync the drift gate checks
|
|
15
|
+
* against) and re-stages it, so the committed mirror never drifts from canonical.
|
|
16
|
+
*
|
|
17
|
+
* It is intentionally scoped to `skills/**` via lefthook's `glob`, so it only runs
|
|
18
|
+
* when the canonical source actually changes.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const path = require('node:path');
|
|
22
|
+
const { execFileSync } = require('node:child_process');
|
|
23
|
+
const { populateCodexRepoSkills, resolveCodexRepoSkillsDir } = require('../lib/codex-skills');
|
|
24
|
+
|
|
25
|
+
function repoRoot() {
|
|
26
|
+
try {
|
|
27
|
+
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
28
|
+
encoding: 'utf8',
|
|
29
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
30
|
+
}).trim();
|
|
31
|
+
} catch {
|
|
32
|
+
return path.resolve(__dirname, '..');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function main() {
|
|
37
|
+
const root = repoRoot();
|
|
38
|
+
let written = [];
|
|
39
|
+
try {
|
|
40
|
+
// clean: true so skills removed from canonical are dropped from the mirror too.
|
|
41
|
+
({ written } = populateCodexRepoSkills({ sourceRoot: root, projectRoot: root, clean: true }));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error(`sync-agent-skills: failed to regenerate .agents/skills — ${error.message}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const mirror = resolveCodexRepoSkillsDir(root);
|
|
48
|
+
try {
|
|
49
|
+
// Stage the regenerated mirror (and any deletions within it).
|
|
50
|
+
execFileSync('git', ['add', '--all', '--', mirror], {
|
|
51
|
+
cwd: root,
|
|
52
|
+
stdio: ['ignore', 'ignore', 'inherit'],
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(`sync-agent-skills: failed to stage .agents/skills — ${error.message}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log(`sync-agent-skills: .agents/skills in sync with skills/ (${written.length} skills)`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
main();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const rootDir = path.join(__dirname, '..');
|
|
7
|
+
const sourceDir = path.join(rootDir, '.github', 'agentic-workflows');
|
|
8
|
+
const targetDir = path.join(rootDir, '.github', 'workflows');
|
|
9
|
+
|
|
10
|
+
function syncFile(fileName, checkOnly) {
|
|
11
|
+
const sourcePath = path.join(sourceDir, fileName);
|
|
12
|
+
const targetPath = path.join(targetDir, fileName);
|
|
13
|
+
const source = fs.readFileSync(sourcePath, 'utf8');
|
|
14
|
+
const target = fs.existsSync(targetPath) ? fs.readFileSync(targetPath, 'utf8') : null;
|
|
15
|
+
|
|
16
|
+
if (source === target) {
|
|
17
|
+
return { changed: false, fileName };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (checkOnly) {
|
|
21
|
+
return { changed: true, fileName };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
25
|
+
fs.writeFileSync(targetPath, source);
|
|
26
|
+
return { changed: true, fileName };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function main() {
|
|
30
|
+
const checkOnly = process.argv.includes('--check');
|
|
31
|
+
const fileNames = fs.readdirSync(sourceDir).filter((file) => file === 'behavioral-test.md' || file === 'behavioral-test.lock.yml');
|
|
32
|
+
const results = fileNames.map((fileName) => syncFile(fileName, checkOnly));
|
|
33
|
+
const changed = results.filter((result) => result.changed);
|
|
34
|
+
|
|
35
|
+
if (checkOnly && changed.length > 0) {
|
|
36
|
+
console.error(`Agentic workflow files out of sync: ${changed.map((result) => result.fileName).join(', ')}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (changed.length === 0) {
|
|
41
|
+
console.log('Agentic workflow files already in sync');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(`Synced agentic workflow files: ${changed.map((result) => result.fileName).join(', ')}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
main();
|
package/scripts/sync-utils.sh
CHANGED
|
@@ -13,6 +13,9 @@ fi
|
|
|
13
13
|
|
|
14
14
|
# Source shared sanitize library
|
|
15
15
|
_SU_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
if [[ -f "$_SU_SCRIPT_DIR/bootstrap-windows-tools.sh" ]]; then
|
|
17
|
+
source "$_SU_SCRIPT_DIR/bootstrap-windows-tools.sh"
|
|
18
|
+
fi
|
|
16
19
|
source "$_SU_SCRIPT_DIR/lib/sanitize.sh"
|
|
17
20
|
|
|
18
21
|
# ── Session Identity ───────────────────────────────────────────────────
|