forge-workflow 0.0.10 → 0.1.0-beta.3
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 +82 -5
- package/.forge/hooks/forge-native-hook.js +431 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +151 -61
- package/CHANGELOG.md +709 -0
- package/CLAUDE.md +9 -118
- package/QUICKSTART.md +175 -0
- package/README.md +275 -365
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +532 -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/forge/TOOLCHAIN.md +670 -0
- 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 +121 -0
- package/docs/guides/SUPPORT.md +190 -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 +214 -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/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
- 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 +155 -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/activation/ensure-forge-home.js +135 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1109 -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 +139 -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-detect.js +60 -0
- package/lib/beads-nudge.js +91 -0
- package/lib/beads-setup.js +121 -0
- package/lib/beads-sync-scaffold.js +25 -101
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_aliases.js +248 -0
- package/lib/commands/_issue.js +780 -77
- package/lib/commands/_manifest.js +93 -0
- package/lib/commands/_registry.js +99 -34
- package/lib/commands/_resolve-command-opts.js +230 -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 +209 -0
- package/lib/commands/hooks.js +377 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +604 -0
- package/lib/commands/insights.js +79 -0
- package/lib/commands/issue.js +12 -1
- package/lib/commands/issues.js +17 -0
- package/lib/commands/lint.js +5 -0
- package/lib/commands/list.js +2 -2
- package/lib/commands/memory.js +81 -0
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +362 -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 +481 -29
- package/lib/commands/pr.js +88 -0
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +135 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +171 -0
- package/lib/commands/recap.js +75 -0
- package/lib/commands/recommend.js +0 -1
- package/lib/commands/release.js +104 -0
- package/lib/commands/remember.js +140 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +900 -971
- package/lib/commands/shepherd.js +501 -0
- package/lib/commands/ship.js +59 -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 +158 -21
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +4 -1
- package/lib/commands/test.js +43 -27
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +43 -18
- package/lib/commands/worktree.js +362 -99
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +977 -0
- package/lib/dep-guard/keyword-ripple.js +2 -2
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +35 -9
- 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 +382 -11
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +192 -0
- package/lib/global-flags.js +104 -0
- package/lib/greptile-match.js +7 -63
- package/lib/grounding/context-events.js +230 -0
- package/lib/grounding/read-first.js +112 -0
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +541 -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/kernel/backing-issue.js +311 -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/owned-kernel.js +43 -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 +2540 -0
- package/lib/kernel/taxonomy-validator.js +394 -0
- package/lib/lefthook-check.js +3 -2
- 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/auto-actions.js +175 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/digest.js +206 -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 +192 -0
- package/lib/pr-monitor/upsert-sticky.js +169 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1314 -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 +99 -497
- package/lib/protected-path-manifest.js +281 -0
- package/lib/protected-state-surfaces.js +387 -0
- package/lib/release-readiness.js +2105 -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 +241 -20
- 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/scoring.js +17 -3
- package/lib/status/beads-snapshot.js +45 -2
- package/lib/status/presenter.js +169 -18
- 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 +251 -0
- package/lib/workflow/enforce-stage.js +351 -45
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +11 -22
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +37 -35
- 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 +81 -57
- package/scripts/beads-upgrade-smoke.sh +24 -3
- 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.sh +22 -3
- 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 +49 -84
- 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 +50 -83
- 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/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-auto-actions.js +93 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/pr-verdict-label.js +50 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +21 -94
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status.sh +60 -57
- 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-utils.sh +3 -0
- package/scripts/test-ci-shard.js +13 -6
- package/scripts/test.js +95 -12
- 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} +44 -50
- 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} +132 -157
- 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/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
- 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/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
- 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/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -89
- package/.github/workflows/github-to-beads.yml +0 -100
- 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 -281
- 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-bootstrap.js +0 -225
- package/lib/beads-health-check.js +0 -188
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -584
- 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 -356
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -132
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
- 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
package/scripts/smart-status.sh
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# smart-status.sh  Workflow intelligence scoring engine
|
|
3
3
|
#
|
|
4
|
-
# Reads issues via `
|
|
5
|
-
# for each issue, and outputs them sorted by score descending.
|
|
4
|
+
# Reads issues via `forge issue list --json` (Forge Kernel), computes a composite
|
|
5
|
+
# score for each issue, and outputs them sorted by score descending.
|
|
6
6
|
#
|
|
7
7
|
# Composite score:
|
|
8
8
|
# priority_weight * unblock_chain * type_weight * status_boost * epic_proximity * staleness_boost
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# smart-status.sh [--json]
|
|
12
12
|
#
|
|
13
13
|
# Environment:
|
|
14
|
-
#
|
|
14
|
+
# FORGE_CMD override the forge command (for testing with mocks)
|
|
15
15
|
# GIT_CMD  override the git command (for testing with mocks)
|
|
16
16
|
# DEFAULT_BRANCH  override the default branch name (default: auto-detect)
|
|
17
17
|
#
|
|
@@ -25,7 +25,28 @@ set -euo pipefail
|
|
|
25
25
|
# sync-utils.sh: get_session_identity for current developer identity
|
|
26
26
|
|
|
27
27
|
_SMART_STATUS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
28
|
+
if [ -f "$_SMART_STATUS_DIR/bootstrap-windows-tools.sh" ]; then
|
|
29
|
+
source "$_SMART_STATUS_DIR/bootstrap-windows-tools.sh"
|
|
30
|
+
fi
|
|
28
31
|
NODE_CMD="${NODE_CMD:-node}"
|
|
32
|
+
JQ_CMD="${JQ_CMD:-jq}"
|
|
33
|
+
|
|
34
|
+
node_script_path() {
|
|
35
|
+
case "$1" in
|
|
36
|
+
/[A-Za-z]/*)
|
|
37
|
+
_drive="$(printf '%s' "${1:1:1}" | tr '[:lower:]' '[:upper:]')"
|
|
38
|
+
printf '%s:/%s\n' "$_drive" "${1:3}"
|
|
39
|
+
;;
|
|
40
|
+
*)
|
|
41
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
42
|
+
cygpath -m "$1"
|
|
43
|
+
else
|
|
44
|
+
printf '%s\n' "$1"
|
|
45
|
+
fi
|
|
46
|
+
;;
|
|
47
|
+
esac
|
|
48
|
+
}
|
|
49
|
+
|
|
29
50
|
if [ -f "$_SMART_STATUS_DIR/lib/sanitize.sh" ]; then
|
|
30
51
|
source "$_SMART_STATUS_DIR/lib/sanitize.sh"
|
|
31
52
|
fi
|
|
@@ -59,7 +80,7 @@ fi
|
|
|
59
80
|
|
|
60
81
|
# ââ€â‚¬Ã¢â€â‚¬ Dependency check ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
61
82
|
|
|
62
|
-
if ! command -v
|
|
83
|
+
if ! command -v "$JQ_CMD" &>/dev/null; then
|
|
63
84
|
echo "Error: jq is required but not found." >&2
|
|
64
85
|
echo "Install jq:" >&2
|
|
65
86
|
echo " macOS: brew install jq" >&2
|
|
@@ -71,7 +92,7 @@ fi
|
|
|
71
92
|
# Warn if jq < 1.6 (fromdateiso8601 and sub/2 require 1.6+)
|
|
72
93
|
# Strip CR here as well because Windows jq.exe (or a CRLF-emitting wrapper in
|
|
73
94
|
# tests) can affect the version probe before the jq() shim is defined below.
|
|
74
|
-
_jq_version="$(command
|
|
95
|
+
_jq_version="$(command "$JQ_CMD" --version 2>/dev/null | tr -d '\r' | sed 's/jq-//')" || true
|
|
75
96
|
if [ -n "$_jq_version" ]; then
|
|
76
97
|
_jq_major="${_jq_version%%.*}"
|
|
77
98
|
_jq_minor="${_jq_version#*.}"; _jq_minor="${_jq_minor%%.*}"
|
|
@@ -84,12 +105,12 @@ fi
|
|
|
84
105
|
# numeric comparisons like `[ "$count" -gt 0 ]`. Strip trailing CRs from
|
|
85
106
|
# all jq output after the dependency/version checks above.
|
|
86
107
|
jq() {
|
|
87
|
-
command
|
|
108
|
+
command "$JQ_CMD" "$@" | tr -d '\r'
|
|
88
109
|
}
|
|
89
110
|
|
|
90
111
|
# ââ€â‚¬Ã¢â€â‚¬ Configuration ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
91
112
|
|
|
92
|
-
|
|
113
|
+
FORGE="${FORGE_CMD:-forge}"
|
|
93
114
|
GIT="${GIT_CMD:-git}"
|
|
94
115
|
JSON_MODE=0
|
|
95
116
|
|
|
@@ -127,42 +148,16 @@ done
|
|
|
127
148
|
|
|
128
149
|
# ââ€â‚¬Ã¢â€â‚¬ Fetch issues ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
129
150
|
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
_bd_list_err="$("$BD" list --json --limit 0 2>&1 >/dev/null || true)"
|
|
141
|
-
if printf '%s' "$_bd_list_err" | grep -qi "database.*not found"; then
|
|
142
|
-
_repo_root="$("$GIT" rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
143
|
-
_metadata_path="$_repo_root/.beads/metadata.json"
|
|
144
|
-
_bd_prefix=""
|
|
145
|
-
if [ -f "$_metadata_path" ]; then
|
|
146
|
-
if _metadata_prefix="$(jq -r '(.dolt_database // "") | strings' "$_metadata_path" 2>/dev/null)"; then
|
|
147
|
-
_bd_prefix="$(printf '%s' "$_metadata_prefix" | tr -d '\r')"
|
|
148
|
-
fi
|
|
149
|
-
fi
|
|
150
|
-
if [ -z "$_bd_prefix" ]; then
|
|
151
|
-
_bd_prefix="$(basename "$_repo_root" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]-' '-' | sed 's/^-//;s/-$//')"
|
|
152
|
-
fi
|
|
153
|
-
echo "Beads database not found - attempting auto-recovery..." >&2
|
|
154
|
-
if "$BD" init --force --prefix "$_bd_prefix" >/dev/null 2>&1; then
|
|
155
|
-
if [ -d "$_repo_root/.beads/backup" ] && ls "$_repo_root/.beads/backup"/*.jsonl >/dev/null 2>&1; then
|
|
156
|
-
"$BD" backup restore >/dev/null 2>&1 && echo "Beads: restored from backup." >&2 || echo "Beads: backup restore failed." >&2
|
|
157
|
-
else
|
|
158
|
-
echo "Beads: initialized fresh (no backup found)." >&2
|
|
159
|
-
fi
|
|
160
|
-
else
|
|
161
|
-
echo "Beads: auto-recovery failed - run 'bd doctor' manually." >&2
|
|
162
|
-
fi
|
|
163
|
-
fi
|
|
164
|
-
|
|
165
|
-
ISSUES_JSON="$("$BD" list --json --limit 0 2>/dev/null || echo '[]')"
|
|
151
|
+
# Fetch issues from the Forge Kernel and normalize the envelope into the bare
|
|
152
|
+
# array the scorer + grouping expect: extract .data.issues and drop terminal
|
|
153
|
+
# (done/cancelled) issues so completed work never pollutes the work queue. The
|
|
154
|
+
# kernel already supplies each issue's `dependents` (reverse-graph) and
|
|
155
|
+
# `dependencies` arrays, so no per-issue reshaping is needed. The kernel
|
|
156
|
+
# auto-migrates its single-machine SQLite store on read — nothing to recover.
|
|
157
|
+
ISSUES_JSON="$("$FORGE" issue list --json 2>/dev/null | jq '[
|
|
158
|
+
.data.issues[]
|
|
159
|
+
| select(.status != "done" and .status != "cancelled")
|
|
160
|
+
]' 2>/dev/null || echo '[]')"
|
|
166
161
|
|
|
167
162
|
# Bail early on empty
|
|
168
163
|
if [ "$(printf '%s' "$ISSUES_JSON" | jq 'length')" = "0" ]; then
|
|
@@ -185,9 +180,14 @@ if [ -n "$EPIC_IDS" ]; then
|
|
|
185
180
|
# Process each epic (bash 3.2 compatible  read line by line)
|
|
186
181
|
while IFS= read -r epic_id; do
|
|
187
182
|
[ -z "$epic_id" ] && continue
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
183
|
+
# Kernel `forge issue children` returns the rollup envelope; read total + the done
|
|
184
|
+
# count straight from the kernel-computed rollup (the kernel `done` status replaces
|
|
185
|
+
# the beads `closed` this math used to count). EPIC_STATS keeps its { total, closed }
|
|
186
|
+
# shape, so smart-status-score.js / scoring.js:getEpicProximity stay untouched.
|
|
187
|
+
CHILDREN_JSON="$("$FORGE" issue children "$epic_id" --json 2>/dev/null || echo '{}')"
|
|
188
|
+
[ -z "$CHILDREN_JSON" ] && CHILDREN_JSON='{}'
|
|
189
|
+
TOTAL="$(printf '%s' "$CHILDREN_JSON" | jq -r '(.data.rollup.total // 0)')"
|
|
190
|
+
CLOSED="$(printf '%s' "$CHILDREN_JSON" | jq -r '(.data.rollup.done // 0)')"
|
|
191
191
|
EPIC_STATS="$(printf '%s' "$EPIC_STATS" | jq --arg id "$epic_id" --argjson total "$TOTAL" --argjson closed "$CLOSED" '. + {($id): {total: $total, closed: $closed}}')"
|
|
192
192
|
done <<EOF
|
|
193
193
|
$EPIC_IDS
|
|
@@ -201,7 +201,7 @@ if ! command -v "$NODE_CMD" >/dev/null 2>&1; then
|
|
|
201
201
|
exit 1
|
|
202
202
|
fi
|
|
203
203
|
|
|
204
|
-
SCORED_JSON="$(printf '{"issues":%s,"epicStats":%s}' "$ISSUES_JSON" "$EPIC_STATS" | "$NODE_CMD" "$_SMART_STATUS_DIR/smart-status-score.js")"
|
|
204
|
+
SCORED_JSON="$(printf '{"issues":%s,"epicStats":%s}' "$ISSUES_JSON" "$EPIC_STATS" | "$NODE_CMD" "$(node_script_path "$_SMART_STATUS_DIR/smart-status-score.js")")"
|
|
205
205
|
|
|
206
206
|
# ââ€â‚¬Ã¢â€â‚¬ Session detection ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
207
207
|
|
|
@@ -218,16 +218,15 @@ WORKTREE_PORCELAIN="$("$GIT" worktree list --porcelain 2>/dev/null || echo '')"
|
|
|
218
218
|
# only performs command execution and rendering.
|
|
219
219
|
IN_PROGRESS_JSON="[]"
|
|
220
220
|
if [ -n "$WORKTREE_PORCELAIN" ]; then
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
fi
|
|
221
|
+
# In-progress issues come straight from the already-fetched kernel list (it
|
|
222
|
+
# includes in_progress; only done/cancelled were dropped during the fetch).
|
|
223
|
+
IN_PROGRESS_JSON="$(printf '%s' "$ISSUES_JSON" | jq '[.[] | select(.status == "in_progress")]')"
|
|
225
224
|
fi
|
|
226
225
|
|
|
227
226
|
SESSIONS_JSON="$(printf '{"baseBranch":%s,"worktreePorcelain":%s,"inProgressIssues":%s}' \
|
|
228
227
|
"$(printf '%s' "$BASE_BRANCH" | jq -R '.')" \
|
|
229
228
|
"$(printf '%s' "$WORKTREE_PORCELAIN" | jq -Rs '.')" \
|
|
230
|
-
"$IN_PROGRESS_JSON" | "$NODE_CMD" "$_SMART_STATUS_DIR/smart-status-sessions.js")"
|
|
229
|
+
"$IN_PROGRESS_JSON" | "$NODE_CMD" "$(node_script_path "$_SMART_STATUS_DIR/smart-status-sessions.js")")"
|
|
231
230
|
SESSION_COUNT="$(printf '%s' "$SESSIONS_JSON" | jq 'length')"
|
|
232
231
|
|
|
233
232
|
if [ "$SESSION_COUNT" -ge 2 ]; then
|
|
@@ -246,7 +245,7 @@ if [ "$SESSION_COUNT" -ge 2 ]; then
|
|
|
246
245
|
$_session_branches
|
|
247
246
|
BRANCHEOF
|
|
248
247
|
|
|
249
|
-
SESSIONS_JSON="$(printf '{"sessions":%s,"branchFiles":%s}' "$SESSIONS_JSON" "$BRANCH_FILES_JSON" | "$NODE_CMD" "$_SMART_STATUS_DIR/smart-status-sessions.js")"
|
|
248
|
+
SESSIONS_JSON="$(printf '{"sessions":%s,"branchFiles":%s}' "$SESSIONS_JSON" "$BRANCH_FILES_JSON" | "$NODE_CMD" "$(node_script_path "$_SMART_STATUS_DIR/smart-status-sessions.js")")"
|
|
250
249
|
fi
|
|
251
250
|
|
|
252
251
|
TIER2_ENABLED=0
|
|
@@ -294,7 +293,7 @@ if [ "$TIER2_ENABLED" = "1" ]; then
|
|
|
294
293
|
$_conflict_pairs
|
|
295
294
|
PAIRSEOF
|
|
296
295
|
|
|
297
|
-
SESSIONS_JSON="$(printf '{"sessions":%s,"mergeTreeResults":%s}' "$SESSIONS_JSON" "$MERGE_TREE_RESULTS_JSON" | "$NODE_CMD" "$_SMART_STATUS_DIR/smart-status-sessions.js")"
|
|
296
|
+
SESSIONS_JSON="$(printf '{"sessions":%s,"mergeTreeResults":%s}' "$SESSIONS_JSON" "$MERGE_TREE_RESULTS_JSON" | "$NODE_CMD" "$(node_script_path "$_SMART_STATUS_DIR/smart-status-sessions.js")")"
|
|
298
297
|
fi
|
|
299
298
|
fi
|
|
300
299
|
|
|
@@ -478,14 +477,18 @@ else
|
|
|
478
477
|
# Group assignment (priority order: RESUME > UNBLOCK_CHAINS > BLOCKED > BACKLOG > READY_WORK)
|
|
479
478
|
# Note: in_progress issues with active blockers still show in RESUME (you started
|
|
480
479
|
# the work, you need to see it) but get a "BLOCKED" annotation in the output.
|
|
480
|
+
# Counts come from the scorer output: dependents is the reverse-graph
|
|
481
|
+
# array it computes, dependencies is the normalized { depends_on_id }
|
|
482
|
+
# list. done/cancelled were dropped at fetch; the status guards below stay
|
|
483
|
+
# as defense-in-depth so a terminal issue can never land in BLOCKED.
|
|
481
484
|
(if .status == "in_progress" then "RESUME"
|
|
482
|
-
elif (.
|
|
483
|
-
elif (.
|
|
485
|
+
elif ((.dependents // []) | length) >= 2 then "UNBLOCK_CHAINS"
|
|
486
|
+
elif ((.dependencies // []) | length) > 0 and .status != "done" and .status != "cancelled" then "BLOCKED"
|
|
484
487
|
elif (.priority == "P4" or .priority == 4) then "BACKLOG"
|
|
485
488
|
else "READY_WORK"
|
|
486
489
|
end) as $group |
|
|
487
490
|
# Flag in_progress issues that have active blockers
|
|
488
|
-
(if .status == "in_progress" and (.
|
|
491
|
+
(if .status == "in_progress" and ((.dependencies // []) | length) > 0 then true else false end) as $blocked_resume |
|
|
489
492
|
$item + { group: $group, days_ago: $days_ago, blocked_resume: $blocked_resume }
|
|
490
493
|
] |
|
|
491
494
|
|
|
@@ -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
|
+
}
|