forge-workflow 0.0.10 → 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 +150 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -118
- 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 +461 -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 +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/{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 +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 +121 -0
- package/lib/beads-sync-scaffold.js +25 -101
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +741 -77
- 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 +17 -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 +0 -1
- 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 +838 -972
- 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 +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 +307 -100
- 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 +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 +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/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 +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/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 +99 -497
- 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 +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 +199 -0
- package/lib/workflow/enforce-stage.js +296 -47
- 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-coordinator.sh +3 -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
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Isolated ESLint flat config for `forge preflight`'s SonarCloud-parity gate.
|
|
2
|
+
//
|
|
3
|
+
// Run ONLY via `eslint --no-config-lookup --config scripts/preflight-sonar.eslint.config.mjs`
|
|
4
|
+
// on changed files, so these Sonar rules never merge with the repo's main
|
|
5
|
+
// eslint.config.js. The point is deterministic parity with the SonarCloud
|
|
6
|
+
// quality gate WITHOUT the full (slow) Sonar scanner, on the change blast radius.
|
|
7
|
+
//
|
|
8
|
+
// cognitive-complexity is pinned to 15 — the same threshold SonarCloud's default
|
|
9
|
+
// "Cognitive Complexity" (S3776) gate uses, and the most common gate failure.
|
|
10
|
+
//
|
|
11
|
+
// Rule name ↔ Sonar rule ID (eslint-plugin-sonarjs v4):
|
|
12
|
+
// sonarjs/cognitive-complexity S3776 (pinned to 15)
|
|
13
|
+
// sonarjs/void-use S3735 (void should not be used)
|
|
14
|
+
// sonarjs/regex-complexity S5843 (regex too complex)
|
|
15
|
+
//
|
|
16
|
+
// prefer-optional-chain (S6582) is intentionally omitted: sonarjs exposes it
|
|
17
|
+
// only through the TypeScript type-checker path, which this fast JS-only gate
|
|
18
|
+
// does not wire (it would require @typescript-eslint/parser + a project graph
|
|
19
|
+
// and defeats the speed goal). CI's full Sonar scan still enforces it.
|
|
20
|
+
import sonarjs from 'eslint-plugin-sonarjs';
|
|
21
|
+
|
|
22
|
+
const sonarRules = {
|
|
23
|
+
'sonarjs/cognitive-complexity': ['error', 15],
|
|
24
|
+
'sonarjs/void-use': 'error',
|
|
25
|
+
'sonarjs/regex-complexity': 'error',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default [
|
|
29
|
+
{
|
|
30
|
+
ignores: ['node_modules/', 'coverage/', 'dist/', 'build/', '**/*.min.js'],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
files: ['**/*.mjs'],
|
|
34
|
+
languageOptions: { ecmaVersion: 2023, sourceType: 'module' },
|
|
35
|
+
plugins: { sonarjs },
|
|
36
|
+
rules: sonarRules,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
files: ['**/*.js', '**/*.cjs'],
|
|
40
|
+
languageOptions: { ecmaVersion: 2023, sourceType: 'commonjs' },
|
|
41
|
+
plugins: { sonarjs },
|
|
42
|
+
rules: sonarRules,
|
|
43
|
+
},
|
|
44
|
+
];
|
package/scripts/preflight.sh
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -u
|
|
3
3
|
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
if [ -f "$SCRIPT_DIR/bootstrap-windows-tools.sh" ]; then
|
|
6
|
+
source "$SCRIPT_DIR/bootstrap-windows-tools.sh"
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
FORGE_CLI="$SCRIPT_DIR/../bin/forge.js"
|
|
10
|
+
|
|
4
11
|
exit_code=0
|
|
5
|
-
have_bd=0
|
|
6
12
|
have_gh=0
|
|
7
13
|
|
|
8
14
|
ok() {
|
|
@@ -21,62 +27,6 @@ action() {
|
|
|
21
27
|
exit_code=2
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
snapshot_hooks_dir() {
|
|
25
|
-
hooks_dir="$1"
|
|
26
|
-
hooks_snapshot_dir="$(mktemp -d 2>/dev/null || mktemp -d -t forge-preflight-hooks)"
|
|
27
|
-
|
|
28
|
-
if [ -d "$hooks_dir" ]; then
|
|
29
|
-
cp -pR "$hooks_dir"/. "$hooks_snapshot_dir"/
|
|
30
|
-
fi
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
restore_hooks_dir() {
|
|
34
|
-
hooks_dir="$1"
|
|
35
|
-
mkdir -p "$hooks_dir"
|
|
36
|
-
|
|
37
|
-
find "$hooks_dir" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
|
|
38
|
-
|
|
39
|
-
if [ -d "$hooks_snapshot_dir" ]; then
|
|
40
|
-
cp -pR "$hooks_snapshot_dir"/. "$hooks_dir"/
|
|
41
|
-
rm -rf "$hooks_snapshot_dir"
|
|
42
|
-
fi
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
write_beads_config() {
|
|
46
|
-
mkdir -p .beads
|
|
47
|
-
{
|
|
48
|
-
printf '%s\n' 'issue-prefix: forge'
|
|
49
|
-
printf '\n'
|
|
50
|
-
printf '%s\n' 'database:'
|
|
51
|
-
printf '%s\n' ' backend: dolt'
|
|
52
|
-
printf '\n'
|
|
53
|
-
} > .beads/config.yaml
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
write_beads_gitignore() {
|
|
57
|
-
mkdir -p .beads
|
|
58
|
-
{
|
|
59
|
-
printf '%s\n' '# Dolt database files (binary, not suitable for git)'
|
|
60
|
-
printf '%s\n' 'dolt/'
|
|
61
|
-
printf '%s\n' '*.db'
|
|
62
|
-
printf '%s\n' '*.lock'
|
|
63
|
-
printf '\n'
|
|
64
|
-
} > .beads/.gitignore
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
safe_bd_init() {
|
|
68
|
-
hooks_dir="$(git rev-parse --git-path hooks 2>/dev/null || printf '.git/hooks')"
|
|
69
|
-
write_beads_config
|
|
70
|
-
write_beads_gitignore
|
|
71
|
-
snapshot_hooks_dir "$hooks_dir"
|
|
72
|
-
|
|
73
|
-
bd init --database forge --prefix forge >/dev/null 2>&1
|
|
74
|
-
init_status=$?
|
|
75
|
-
|
|
76
|
-
restore_hooks_dir "$hooks_dir"
|
|
77
|
-
return "$init_status"
|
|
78
|
-
}
|
|
79
|
-
|
|
80
30
|
is_windows_shell() {
|
|
81
31
|
case "$(uname -s 2>/dev/null || printf unknown)" in
|
|
82
32
|
MINGW*|MSYS*|CYGWIN*)
|
|
@@ -107,10 +57,7 @@ check_tool() {
|
|
|
107
57
|
}
|
|
108
58
|
|
|
109
59
|
check_tools() {
|
|
110
|
-
|
|
111
|
-
have_bd=1
|
|
112
|
-
fi
|
|
113
|
-
|
|
60
|
+
check_tool "node" "install Node.js (https://nodejs.org)" || true
|
|
114
61
|
check_tool "jq" "winget install jqlang.jq" || true
|
|
115
62
|
|
|
116
63
|
if check_tool "gh" "winget install GitHub.cli"; then
|
|
@@ -131,50 +78,30 @@ check_github_auth() {
|
|
|
131
78
|
action "github-auth" "not logged in; run: gh auth login"
|
|
132
79
|
}
|
|
133
80
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if
|
|
141
|
-
ok "
|
|
142
|
-
init_ok=1
|
|
81
|
+
# Validate the Forge Kernel issue store. The kernel DB is a single-machine
|
|
82
|
+
# SQLite store in the git common dir; any kernel issue command auto-migrates it
|
|
83
|
+
# via broker.initialize(), so the ensure step is just a kernel read. `forge
|
|
84
|
+
# doctor` then reports the filesystem class of that DB path (D19) — it only
|
|
85
|
+
# reports, it does not init, which is why the ensure step runs first.
|
|
86
|
+
check_kernel() {
|
|
87
|
+
if node "$FORGE_CLI" issue list --json >/dev/null 2>&1; then
|
|
88
|
+
ok "kernel-init" "Kernel issue store is readable"
|
|
143
89
|
else
|
|
144
|
-
|
|
145
|
-
fixed "beads-init" "ran hook-preserving bd init --database forge --prefix forge"
|
|
146
|
-
init_ok=1
|
|
147
|
-
else
|
|
148
|
-
action "beads-init" "bd init failed; inspect Beads setup manually"
|
|
149
|
-
fi
|
|
150
|
-
fi
|
|
151
|
-
|
|
152
|
-
if [ "$init_ok" -ne 1 ]; then
|
|
90
|
+
action "kernel-init" "kernel issue store not initializable; inspect: node bin/forge.js doctor"
|
|
153
91
|
return 0
|
|
154
92
|
fi
|
|
155
93
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if [ "$doctor_status" -eq 0 ]; then
|
|
159
|
-
doctor_output_lower="$(printf '%s' "$doctor_output" | tr '[:upper:]' '[:lower:]')"
|
|
160
|
-
if [ "$exit_code" -eq 1 ] ||
|
|
161
|
-
[[ "$doctor_output_lower" == *fix* ]] ||
|
|
162
|
-
[[ "$doctor_output_lower" == *repair* ]] ||
|
|
163
|
-
[[ "$doctor_output_lower" == *migrat* ]] ||
|
|
164
|
-
[[ "$doctor_output_lower" == *updat* ]]; then
|
|
165
|
-
fixed "beads-doctor" "ran bd doctor --fix --yes after Beads repair"
|
|
166
|
-
else
|
|
167
|
-
ok "beads-doctor" "bd doctor --fix --yes succeeded"
|
|
168
|
-
fi
|
|
94
|
+
if node "$FORGE_CLI" doctor >/dev/null 2>&1; then
|
|
95
|
+
ok "kernel-doctor" "forge doctor reports a healthy filesystem for the kernel database"
|
|
169
96
|
else
|
|
170
|
-
action "
|
|
97
|
+
action "kernel-doctor" "forge doctor reports an unhealthy filesystem; inspect the kernel database path"
|
|
171
98
|
fi
|
|
172
99
|
}
|
|
173
100
|
|
|
174
101
|
main() {
|
|
175
102
|
check_tools
|
|
176
103
|
check_github_auth
|
|
177
|
-
|
|
104
|
+
check_kernel
|
|
178
105
|
exit "$exit_code"
|
|
179
106
|
}
|
|
180
107
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execFileSync } = require('node:child_process');
|
|
4
|
+
const {
|
|
5
|
+
assertProtectedWriteAllowed,
|
|
6
|
+
recordProtectedStateAuditEvent,
|
|
7
|
+
} = require('../lib/protected-state-surfaces');
|
|
8
|
+
|
|
9
|
+
function parseNameStatus(output) {
|
|
10
|
+
const files = [];
|
|
11
|
+
for (const line of output.split(/\r?\n/)) {
|
|
12
|
+
const trimmed = line.trim();
|
|
13
|
+
if (!trimmed) continue;
|
|
14
|
+
const parts = trimmed.split('\t').filter(Boolean);
|
|
15
|
+
const status = parts[0] || '';
|
|
16
|
+
if (/^[RC]/.test(status)) {
|
|
17
|
+
files.push(...parts.slice(1, 3));
|
|
18
|
+
} else {
|
|
19
|
+
files.push(parts[1]);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return [...new Set(files.filter(Boolean))];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getStagedFiles() {
|
|
26
|
+
if (process.env.FORGE_PROTECTED_STATE_STAGED_NAME_STATUS !== undefined) {
|
|
27
|
+
return parseNameStatus(process.env.FORGE_PROTECTED_STATE_STAGED_NAME_STATUS);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (process.env.FORGE_PROTECTED_STATE_STAGED_FILES !== undefined) {
|
|
31
|
+
return process.env.FORGE_PROTECTED_STATE_STAGED_FILES
|
|
32
|
+
.split(/\r?\n/)
|
|
33
|
+
.map(line => line.trim())
|
|
34
|
+
.filter(Boolean);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const output = execFileSync('git', ['diff', '--cached', '--name-status', '--diff-filter=ACMRDT'], {
|
|
38
|
+
encoding: 'utf8',
|
|
39
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
40
|
+
});
|
|
41
|
+
return parseNameStatus(output);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getAllowedSurfaces() {
|
|
45
|
+
return new Set(
|
|
46
|
+
String(process.env.FORGE_PROTECTED_STATE_ALLOWED_SURFACES || '')
|
|
47
|
+
.split(',')
|
|
48
|
+
.map(surface => surface.trim())
|
|
49
|
+
.filter(Boolean),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function main() {
|
|
54
|
+
const actor =
|
|
55
|
+
process.env.FORGE_PROTECTED_STATE_ACTOR ||
|
|
56
|
+
process.env.FORGE_ACTOR ||
|
|
57
|
+
process.env.USER ||
|
|
58
|
+
process.env.USERNAME ||
|
|
59
|
+
'unknown';
|
|
60
|
+
const allowedSurfaces = getAllowedSurfaces();
|
|
61
|
+
const decisions = getStagedFiles()
|
|
62
|
+
.map(file => {
|
|
63
|
+
const probe = assertProtectedWriteAllowed(file, { actor, operation: 'staged_edit' });
|
|
64
|
+
if (probe.requiredSurface && allowedSurfaces.has(probe.requiredSurface)) {
|
|
65
|
+
return assertProtectedWriteAllowed(file, {
|
|
66
|
+
actor,
|
|
67
|
+
operation: 'staged_edit',
|
|
68
|
+
viaForgeApi: true,
|
|
69
|
+
surface: probe.requiredSurface,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return probe;
|
|
73
|
+
})
|
|
74
|
+
.filter(decision => !decision.allowed);
|
|
75
|
+
|
|
76
|
+
for (const decision of decisions) {
|
|
77
|
+
const audit = recordProtectedStateAuditEvent(decision, { cwd: process.cwd() });
|
|
78
|
+
if (!audit.success) {
|
|
79
|
+
console.error(`WARN: Failed to record protected-state audit for ${decision.path}: ${audit.error}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (decisions.length === 0) {
|
|
84
|
+
console.log('OK: No protected state edits detected.');
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.error('ERROR: Protected state edit detected. Direct edits to these paths are blocked:');
|
|
89
|
+
for (const decision of decisions) {
|
|
90
|
+
console.error(` - ${decision.path} [${decision.requiredSurface}]`);
|
|
91
|
+
console.error(` Decision: ${decision.decision}`);
|
|
92
|
+
console.error(` Repair: ${decision.repairHint}`);
|
|
93
|
+
}
|
|
94
|
+
console.error('');
|
|
95
|
+
console.error('Use the owning Forge or Beads API surface, then stage the generated result if that command explicitly owns it.');
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
main();
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error(`Protected state check failed: ${error.message}`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
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
|
+
}
|