forge-workflow 0.0.9 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
- package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
- package/.cursor/rules/permissions-guidance.mdc +2 -2
- package/.forge/hooks/check-tdd.js +3 -0
- package/.forge/hooks/forge-native-hook.js +245 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +151 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -106
- package/QUICKSTART.md +171 -0
- package/README.md +271 -363
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +466 -489
- package/docs/INDEX.md +93 -0
- package/docs/PROJECT_DESIGN.md +685 -0
- package/docs/architecture/index.md +66 -0
- package/docs/architecture/notes/README.md +35 -0
- package/docs/architecture/subsystems/README.md +46 -0
- package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
- package/docs/forge/VALIDATION.md +82 -0
- package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
- package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
- package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
- package/docs/guides/GREPTILE_SETUP.md +46 -0
- package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
- package/docs/guides/MIGRATION.md +56 -0
- package/docs/guides/SETUP.md +118 -0
- package/docs/guides/SUPPORT.md +185 -0
- package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
- package/docs/guides/memory-backends.md +183 -0
- package/docs/reference/ADAPTERS.md +128 -0
- package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
- package/docs/reference/COMMANDS.md +205 -0
- package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
- package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
- package/docs/reference/HERMES_INTEGRATION.md +118 -0
- package/docs/reference/INSIGHTS_RECAP.md +63 -0
- package/docs/reference/INSTALL.md +164 -0
- package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
- package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
- package/docs/reference/RELEASE.md +68 -0
- package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
- package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
- package/docs/reference/SKILLS.md +35 -0
- package/docs/reference/STATUS_BOARD.md +80 -0
- package/docs/reference/TEMPLATES.md +106 -0
- package/docs/reference/TOOLCHAIN.md +658 -0
- package/docs/reference/VALIDATION.md +82 -0
- package/docs/reference/agent-permissions.md +169 -0
- package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
- package/docs/reference/control-plane-guarantees.md +125 -0
- package/docs/reference/dependency-chain.md +331 -0
- package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
- package/docs/reference/forge-kernel-schema.md +72 -0
- package/docs/reference/kernel-conflict-evaluators.md +27 -0
- package/docs/reference/patch-md-format.md +77 -0
- package/docs/reference/protected-state-surfaces.md +59 -0
- package/docs/reference/shepherd.md +115 -0
- package/docs/reference/superpowers-analysis.md +320 -0
- package/docs/reference/superpowers-integration-options.md +404 -0
- package/docs/reference/test-environment.md +519 -0
- package/docs/reference/upgrade-safety.md +59 -0
- package/lefthook.yml +18 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1042 -0
- package/lib/adapters/greptile-review-adapter.js +141 -0
- package/lib/adapters/kernel-issue-adapter.js +101 -0
- package/lib/adapters/pr-state-adapter.js +484 -0
- package/lib/adoption-profiles.js +126 -0
- package/lib/agents/README.md +2 -6
- package/lib/agents/claude.plugin.json +3 -8
- package/lib/agents/codex.plugin.json +9 -1
- package/lib/agents/cursor.plugin.json +2 -6
- package/lib/agents/hermes.plugin.json +22 -0
- package/lib/agents-config.js +39 -1236
- package/lib/audit-evidence.js +282 -0
- package/lib/beads-setup.js +225 -28
- package/lib/beads-sync-scaffold.js +36 -107
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +744 -70
- package/lib/commands/_manifest.js +91 -0
- package/lib/commands/_registry.js +85 -34
- package/lib/commands/_resolve-command-opts.js +261 -0
- package/lib/commands/_serve-security.js +270 -0
- package/lib/commands/adapter.js +12 -0
- package/lib/commands/add.js +118 -0
- package/lib/commands/audit.js +70 -0
- package/lib/commands/blocked.js +5 -0
- package/lib/commands/board.js +64 -0
- package/lib/commands/claim.js +21 -2
- package/lib/commands/claims.js +7 -0
- package/lib/commands/clean.js +485 -75
- package/lib/commands/close.js +2 -2
- package/lib/commands/comment.js +5 -0
- package/lib/commands/control.js +148 -0
- package/lib/commands/create.js +2 -2
- package/lib/commands/dev.js +185 -7
- package/lib/commands/doc-gate.js +336 -0
- package/lib/commands/doctor.js +156 -0
- package/lib/commands/explain.js +15 -0
- package/lib/commands/export.js +237 -0
- package/lib/commands/gate.js +192 -0
- package/lib/commands/hooks.js +242 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +598 -0
- package/lib/commands/insights.js +79 -0
- package/lib/commands/issue.js +12 -1
- package/lib/commands/issues.js +66 -0
- package/lib/commands/lint.js +5 -0
- package/lib/commands/list.js +2 -2
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +523 -0
- package/lib/commands/new.js +12 -0
- package/lib/commands/options.js +241 -0
- package/lib/commands/orient.js +13 -0
- package/lib/commands/orphans.js +5 -0
- package/lib/commands/patch.js +67 -0
- package/lib/commands/plan.js +436 -24
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +69 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +116 -0
- package/lib/commands/recap.js +61 -0
- package/lib/commands/recommend.js +22 -2
- package/lib/commands/release.js +91 -0
- package/lib/commands/remember.js +74 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +851 -979
- package/lib/commands/shepherd.js +436 -0
- package/lib/commands/ship.js +23 -1
- package/lib/commands/show.js +2 -2
- package/lib/commands/stage.js +192 -0
- package/lib/commands/stale.js +5 -0
- package/lib/commands/status.js +329 -11
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +15 -2
- package/lib/commands/test.js +58 -7
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +56 -25
- package/lib/commands/worktree.js +308 -128
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +946 -0
- package/lib/dep-guard/keyword-ripple.js +184 -0
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +42 -17
- package/lib/doc-gate/declaration.js +177 -0
- package/lib/doc-gate/detect.js +289 -0
- package/lib/doc-gate/gate.js +375 -0
- package/lib/doc-gate/okf-config.js +128 -0
- package/lib/doc-gate/okf.js +429 -0
- package/lib/docs-command.js +1161 -6
- package/lib/forge-issues.js +697 -0
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +193 -0
- package/lib/global-flags.js +74 -0
- package/lib/greptile-match.js +7 -63
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +451 -0
- package/lib/inbox.js +391 -0
- package/lib/insights.js +397 -0
- package/lib/issue-adapter.js +156 -0
- package/lib/issue-backend.js +145 -0
- package/lib/issue-render.js +220 -0
- package/lib/issue-sync/authority.js +100 -0
- package/lib/issue-sync/github-pull.js +184 -0
- package/lib/issue-sync/import-primitives.js +98 -0
- package/lib/issue-sync/legacy-link-bridge.js +436 -0
- package/lib/issue-sync/link-store.js +292 -0
- package/lib/issue-sync/project-github.js +123 -0
- package/lib/issue-sync/reconcile.js +195 -0
- package/lib/issue-sync/schema.js +126 -0
- package/lib/kernel/backing-issue.js +305 -0
- package/lib/kernel/broker.js +1218 -0
- package/lib/kernel/cli-broker-factory.js +130 -0
- package/lib/kernel/conflict-signal.js +82 -0
- package/lib/kernel/evaluators.js +195 -0
- package/lib/kernel/fs-class.js +495 -0
- package/lib/kernel/issue-command-contract.js +559 -0
- package/lib/kernel/issue-id-resolver.js +186 -0
- package/lib/kernel/lease-enforcer.js +158 -0
- package/lib/kernel/migrations.js +333 -0
- package/lib/kernel/planning-buckets-schema.js +109 -0
- package/lib/kernel/projection-jsonl-writer.js +450 -0
- package/lib/kernel/readiness-model.js +329 -0
- package/lib/kernel/schema.js +356 -0
- package/lib/kernel/sqlite-driver.js +2504 -0
- package/lib/kernel/taxonomy-validator.js +394 -0
- package/lib/lefthook-check.js +8 -4
- package/lib/lefthook-wiring.js +413 -0
- package/lib/mcp-config-renderer.js +288 -0
- package/lib/memory/graphiti-mcp.js +106 -0
- package/lib/memory/router.js +387 -0
- package/lib/memory/typed-api.js +102 -0
- package/lib/memory-digest.js +195 -0
- package/lib/merge-rules.js +395 -0
- package/lib/migrate-dry-run.js +466 -0
- package/lib/orientation.js +863 -0
- package/lib/package-manager-remediation.js +103 -0
- package/lib/package-root.js +381 -0
- package/lib/patch-intent.js +890 -0
- package/lib/plugin-catalog.js +3 -4
- package/lib/plugin-manager.js +0 -5
- package/lib/pr-bundle.js +186 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/gather.js +124 -0
- package/lib/pr-monitor/journal.js +299 -0
- package/lib/pr-monitor/monitor.js +146 -0
- package/lib/pr-monitor/render-sticky.js +157 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1273 -0
- package/lib/pr-shepherd.js +494 -0
- package/lib/pr-state-validator.js +59 -0
- package/lib/preflight/gates.js +237 -0
- package/lib/preflight/runner.js +116 -0
- package/lib/project-discovery.js +0 -53
- package/lib/project-memory.js +166 -0
- package/lib/protected-path-manifest.js +281 -0
- package/lib/protected-state-surfaces.js +387 -0
- package/lib/release-readiness.js +2089 -0
- package/lib/reset.js +59 -45
- package/lib/review-adapter.js +68 -0
- package/lib/rules-sync.js +260 -0
- package/lib/runtime-health.js +332 -23
- package/lib/safety-config-renderer.js +268 -0
- package/lib/setup-action-log.js +1 -7
- package/lib/setup.js +27 -65
- package/lib/shell-utils.js +76 -6
- package/lib/skills-sync.js +330 -0
- package/lib/smart-status/conflicts.js +205 -0
- package/lib/smart-status/scoring.js +191 -0
- package/lib/status/beads-snapshot.js +145 -0
- package/lib/status/presenter.js +216 -0
- package/lib/status/snapshot.js +186 -0
- package/lib/sync-backend.js +202 -0
- package/lib/untrusted-content.js +52 -0
- package/lib/upgrade-safety.js +199 -0
- package/lib/workflow/enforce-stage.js +298 -47
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +159 -14
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +46 -36
- package/rules/documentation.md +19 -0
- package/rules/kernel-tracking.md +26 -0
- package/rules/security.md +22 -0
- package/rules/tdd.md +20 -0
- package/rules/workflow.md +27 -0
- package/scripts/auto-backing-issue.js +47 -0
- package/scripts/beads-context.sh +165 -22
- package/scripts/beads-migrate-to-dolt.sh +7 -0
- package/scripts/beads-upgrade-smoke.sh +284 -0
- package/scripts/behavioral-judge.sh +115 -11
- package/scripts/benchmark.js +349 -63
- package/scripts/bootstrap-windows-tools.sh +78 -0
- package/scripts/branch-protection.js +2 -3
- package/scripts/check-agents.js +34 -137
- package/scripts/commitlint.js +3 -1
- package/scripts/conflict-detect.sh +3 -0
- package/scripts/dep-guard-analyze.js +52 -17
- package/scripts/dep-guard-keyword-ripple.js +29 -0
- package/scripts/dep-guard-render-review.js +86 -0
- package/scripts/dep-guard.sh +64 -232
- package/scripts/file-index.sh +3 -0
- package/scripts/forge-team/lib/claim.sh +34 -18
- package/scripts/forge-team/lib/dashboard.sh +61 -86
- package/scripts/forge-team/lib/epic.sh +99 -263
- package/scripts/forge-team/lib/hooks.sh +26 -28
- package/scripts/forge-team/lib/identity.sh +4 -4
- package/scripts/forge-team/lib/sync-github.sh +144 -47
- package/scripts/forge-team/lib/verify.sh +93 -83
- package/scripts/forge-team/lib/workload.sh +41 -65
- package/scripts/forge-team/tests/claim.test.sh +25 -19
- package/scripts/forge-team/tests/dashboard.test.sh +31 -46
- package/scripts/forge-team/tests/epic.test.sh +52 -71
- package/scripts/forge-team/tests/hooks.test.sh +38 -50
- package/scripts/forge-team/tests/identity.test.sh +3 -3
- package/scripts/forge-team/tests/integration.test.sh +44 -66
- package/scripts/forge-team/tests/sync-github.test.sh +183 -79
- package/scripts/forge-team/tests/verify.test.sh +37 -46
- package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
- package/scripts/forge-team/tests/workload.test.sh +32 -66
- package/scripts/gen-command-manifest.js +153 -0
- package/scripts/gen-embedded-assets.mjs +129 -0
- package/scripts/install.ps1 +139 -0
- package/scripts/install.sh +268 -0
- package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
- package/scripts/lib/release-asset.mjs +84 -0
- package/scripts/parity-check.mjs +145 -0
- package/scripts/parity-check.test.mjs +58 -0
- package/scripts/pin-agentic-workflow-images.js +112 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +108 -0
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status-score.js +31 -0
- package/scripts/smart-status-sessions.js +51 -0
- package/scripts/smart-status.sh +117 -369
- package/scripts/spikes/config-race-bench.js +111 -0
- package/scripts/spikes/harness-capability-matrix.js +13 -0
- package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
- package/scripts/spikes/protected-path-manifest.js +20 -0
- package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
- package/scripts/sync-agent-skills.js +62 -0
- package/scripts/sync-agentic-workflow.js +48 -0
- package/scripts/sync-utils.sh +3 -0
- package/scripts/test-ci-shard.js +251 -0
- package/scripts/test-dashboard.js +188 -52
- package/scripts/test-full-suite.js +186 -0
- package/scripts/test-profile.js +278 -0
- package/scripts/test.js +302 -28
- package/scripts/validate.js +143 -0
- package/scripts/validate.sh +18 -1
- package/skills/claim-safety/SKILL.md +102 -0
- package/skills/claim-safety/evals/evals.json +46 -0
- package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
- package/skills/dev/evals/evals.json +50 -0
- package/skills/hermes-forge/SKILL.md +185 -0
- package/skills/hermes-forge/evals/evals.json +46 -0
- package/skills/issue-basics/SKILL.md +111 -0
- package/skills/issue-basics/evals/evals.json +46 -0
- package/skills/kernel/SKILL.md +166 -0
- package/skills/kernel/evals/evals.json +50 -0
- package/skills/memory/SKILL.md +102 -0
- package/skills/parallel-deep-research/SKILL.md +14 -11
- package/skills/parallel-deep-research/evals/evals.json +11 -27
- package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
- package/skills/plan/evals/evals.json +42 -0
- package/skills/research/SKILL.md +195 -0
- package/skills/research/evals/evals.json +42 -0
- package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
- package/skills/review/evals/evals.json +42 -0
- package/skills/rollback/SKILL.md +110 -0
- package/skills/rollback/evals/evals.json +46 -0
- package/skills/rollback/references/methods.md +204 -0
- package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
- package/skills/shepherd/SKILL.md +66 -0
- package/skills/shepherd/evals/evals.json +42 -0
- package/skills/ship/SKILL.md +251 -0
- package/skills/ship/evals/evals.json +42 -0
- package/skills/smith/SKILL.md +142 -0
- package/skills/smith/evals/evals.json +46 -0
- package/skills/smith/references/autonomy-and-gates.md +94 -0
- package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
- package/skills/sonarcloud/evals/evals.json +46 -0
- package/skills/sonarcloud-analysis/SKILL.md +18 -13
- package/skills/sonarcloud-analysis/evals/evals.json +11 -15
- package/skills/status/SKILL.md +102 -0
- package/skills/status/evals/evals.json +50 -0
- package/skills/triage-ready/SKILL.md +121 -0
- package/skills/triage-ready/evals/evals.json +42 -0
- package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
- package/skills/validate/evals/evals.json +42 -0
- package/skills/verify/SKILL.md +299 -0
- package/skills/verify/evals/evals.json +50 -0
- package/.claude/commands/dev.md +0 -345
- package/.claude/commands/plan.md +0 -566
- package/.claude/commands/premerge.md +0 -186
- package/.claude/commands/research.md +0 -42
- package/.claude/commands/review.md +0 -451
- package/.claude/commands/rollback.md +0 -721
- package/.claude/commands/ship.md +0 -213
- package/.claude/commands/sonarcloud.md +0 -152
- package/.claude/commands/status.md +0 -90
- package/.claude/commands/validate.md +0 -288
- package/.claude/commands/verify.md +0 -269
- package/.claude/rules/workflow.md +0 -121
- package/.cline/workflows/dev.md +0 -342
- package/.cline/workflows/plan.md +0 -563
- package/.cline/workflows/premerge.md +0 -183
- package/.cline/workflows/research.md +0 -39
- package/.cline/workflows/review.md +0 -448
- package/.cline/workflows/rollback.md +0 -718
- package/.cline/workflows/ship.md +0 -210
- package/.cline/workflows/sonarcloud.md +0 -146
- package/.cline/workflows/status.md +0 -87
- package/.cline/workflows/validate.md +0 -285
- package/.cline/workflows/verify.md +0 -266
- package/.codex/config.toml +0 -11
- package/.codex/skills/dev/SKILL.md +0 -345
- package/.codex/skills/plan/SKILL.md +0 -566
- package/.codex/skills/premerge/SKILL.md +0 -186
- package/.codex/skills/research/SKILL.md +0 -42
- package/.codex/skills/review/SKILL.md +0 -451
- package/.codex/skills/rollback/SKILL.md +0 -721
- package/.codex/skills/ship/SKILL.md +0 -213
- package/.codex/skills/sonarcloud/SKILL.md +0 -149
- package/.codex/skills/status/SKILL.md +0 -90
- package/.codex/skills/validate/SKILL.md +0 -288
- package/.codex/skills/verify/SKILL.md +0 -269
- package/.cursor/commands/dev.md +0 -342
- package/.cursor/commands/plan.md +0 -563
- package/.cursor/commands/premerge.md +0 -183
- package/.cursor/commands/research.md +0 -39
- package/.cursor/commands/review.md +0 -448
- package/.cursor/commands/ship.md +0 -210
- package/.cursor/commands/sonarcloud.md +0 -146
- package/.cursor/commands/status.md +0 -87
- package/.cursor/commands/validate.md +0 -285
- package/.cursor/commands/verify.md +0 -266
- package/.cursorrules +0 -149
- package/.github/prompts/premerge.prompt.md +0 -188
- package/.github/prompts/research.prompt.md +0 -44
- package/.github/prompts/rollback.prompt.md +0 -723
- package/.github/prompts/ship.prompt.md +0 -215
- package/.github/prompts/status.prompt.md +0 -92
- package/.github/prompts/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -56
- package/.github/workflows/github-to-beads.yml +0 -97
- package/.kilocode/workflows/dev.md +0 -346
- package/.kilocode/workflows/plan.md +0 -567
- package/.kilocode/workflows/premerge.md +0 -187
- package/.kilocode/workflows/research.md +0 -43
- package/.kilocode/workflows/review.md +0 -452
- package/.kilocode/workflows/rollback.md +0 -722
- package/.kilocode/workflows/ship.md +0 -214
- package/.kilocode/workflows/sonarcloud.md +0 -150
- package/.kilocode/workflows/status.md +0 -91
- package/.kilocode/workflows/validate.md +0 -289
- package/.kilocode/workflows/verify.md +0 -270
- package/.opencode/commands/dev.md +0 -345
- package/.opencode/commands/plan.md +0 -566
- package/.opencode/commands/premerge.md +0 -186
- package/.opencode/commands/research.md +0 -42
- package/.opencode/commands/review.md +0 -451
- package/.opencode/commands/rollback.md +0 -721
- package/.opencode/commands/ship.md +0 -213
- package/.opencode/commands/sonarcloud.md +0 -149
- package/.opencode/commands/status.md +0 -90
- package/.opencode/commands/validate.md +0 -288
- package/.opencode/commands/verify.md +0 -269
- package/.roo/commands/dev.md +0 -346
- package/.roo/commands/plan.md +0 -567
- package/.roo/commands/premerge.md +0 -187
- package/.roo/commands/research.md +0 -43
- package/.roo/commands/review.md +0 -452
- package/.roo/commands/rollback.md +0 -722
- package/.roo/commands/ship.md +0 -214
- package/.roo/commands/sonarcloud.md +0 -150
- package/.roo/commands/status.md +0 -91
- package/.roo/commands/validate.md +0 -289
- package/.roo/commands/verify.md +0 -270
- package/docs/BEADS_GITHUB_SYNC.md +0 -255
- package/docs/GREPTILE_SETUP.md +0 -400
- package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
- package/docs/SETUP.md +0 -663
- package/docs/VALIDATION.md +0 -363
- package/lib/agents/cline.plugin.json +0 -29
- package/lib/agents/copilot.plugin.json +0 -24
- package/lib/agents/kilocode.plugin.json +0 -22
- package/lib/agents/opencode.plugin.json +0 -23
- package/lib/agents/roo.plugin.json +0 -30
- package/lib/beads-health-check.js +0 -143
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -567
- package/scripts/github-beads-sync/comment.mjs +0 -64
- package/scripts/github-beads-sync/config.mjs +0 -148
- package/scripts/github-beads-sync/github-api.mjs +0 -131
- package/scripts/github-beads-sync/index.mjs +0 -332
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -78
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
- package/scripts/github-beads-sync/run-bd.mjs +0 -161
- package/scripts/github-beads-sync/sanitize.mjs +0 -121
- package/scripts/github-beads-sync.config.json +0 -26
- package/scripts/sync-commands.js +0 -600
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Byte-for-byte parity check between the two Forge distribution channels.
|
|
2
|
+
//
|
|
3
|
+
// Runs `forge setup` from BOTH the npm path (`node bin/forge.js`) and the compiled
|
|
4
|
+
// single-file binary into two throwaway git projects, then compares the produced
|
|
5
|
+
// trees by relative-path set + per-file SHA-256. Catches flattening, missing files,
|
|
6
|
+
// CRLF mangling, and corruption in one assertion.
|
|
7
|
+
//
|
|
8
|
+
// This is the "one real-compile leg" the reliability guidance asks for. It is
|
|
9
|
+
// runnable locally (`bun run parity:binary`) and by the step-3 CI matrix on each
|
|
10
|
+
// host target. Requires `bun` and `git` on PATH.
|
|
11
|
+
|
|
12
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import os from 'node:os';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import crypto from 'node:crypto';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
|
|
19
|
+
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
20
|
+
const BIN = process.platform === 'win32' ? 'forge-bin.exe' : 'forge-bin';
|
|
21
|
+
const BIN_PATH = path.join(REPO, BIN);
|
|
22
|
+
|
|
23
|
+
function run(cmd, args, opts = {}) {
|
|
24
|
+
const r = spawnSync(cmd, args, { encoding: 'utf8', ...opts });
|
|
25
|
+
return r;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function sha256(file) {
|
|
29
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Directories to skip entirely while walking a setup tree. `node_modules` is a
|
|
33
|
+
// package-manager install artifact, NOT a Forge-managed asset — `setup --quick`
|
|
34
|
+
// may run an npm/bun install whose output (dependency trees, registry metadata)
|
|
35
|
+
// is legitimately non-deterministic between runs, channels, and OSes. Comparing
|
|
36
|
+
// it produces false-positive drift, so it is excluded. `.git` is runtime VCS state.
|
|
37
|
+
const EXCLUDED_DIRS = new Set(['.git', 'node_modules']);
|
|
38
|
+
|
|
39
|
+
// Files to skip regardless of location. Lockfiles are install byproducts: their
|
|
40
|
+
// bytes vary with registry ordering/timestamps and are not Forge assets. Excluding
|
|
41
|
+
// them keeps parity strict for real assets (skills/rules/hooks/.forge/AGENTS.md)
|
|
42
|
+
// while ignoring package-manager noise. `.package-lock.json` normally lives under
|
|
43
|
+
// node_modules/ (already excluded) but is guarded here too for robustness.
|
|
44
|
+
const EXCLUDED_FILES = new Set(['package-lock.json', '.package-lock.json']);
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Map of relative-posix-path → sha256 for every Forge-managed file under dir.
|
|
48
|
+
* Excludes .git, node_modules/**, and package-manager lockfiles (see the
|
|
49
|
+
* EXCLUDED_* sets above) so parity compares only Forge assets, not install output.
|
|
50
|
+
*/
|
|
51
|
+
function hashTree(dir) {
|
|
52
|
+
const map = {};
|
|
53
|
+
const walk = (abs, rel) => {
|
|
54
|
+
for (const e of fs.readdirSync(abs)) {
|
|
55
|
+
if (EXCLUDED_DIRS.has(e)) continue;
|
|
56
|
+
const a = path.join(abs, e);
|
|
57
|
+
const r = rel ? `${rel}/${e}` : e;
|
|
58
|
+
const st = fs.lstatSync(a);
|
|
59
|
+
if (st.isDirectory()) walk(a, r);
|
|
60
|
+
else if (st.isFile() && !EXCLUDED_FILES.has(e)) map[r] = sha256(a);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
walk(dir, '');
|
|
64
|
+
return map;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function makeProject(label) {
|
|
68
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), `forge-parity-${label}-`));
|
|
69
|
+
execFileSync('git', ['init', '-q'], { cwd: dir });
|
|
70
|
+
execFileSync('git', ['config', 'user.email', 'parity@forge.test'], { cwd: dir });
|
|
71
|
+
execFileSync('git', ['config', 'user.name', 'parity'], { cwd: dir });
|
|
72
|
+
return dir;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function runSetup(label, cmd, args, dir) {
|
|
76
|
+
console.log(`\n[${label}] ${cmd} ${args.join(' ')} (cwd=${dir})`);
|
|
77
|
+
const r = run(cmd, args, { cwd: dir });
|
|
78
|
+
if (r.status !== 0) {
|
|
79
|
+
console.error(`[${label}] setup exited ${r.status}`);
|
|
80
|
+
if (r.stdout) console.error(r.stdout.slice(-2000));
|
|
81
|
+
if (r.stderr) console.error(r.stderr.slice(-2000));
|
|
82
|
+
throw new Error(`[${label}] setup failed`);
|
|
83
|
+
}
|
|
84
|
+
return r;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function main() {
|
|
88
|
+
// 1. Build the binary (regenerates the embed manifest first).
|
|
89
|
+
console.log('Building single-file binary (bun run build:binary)…');
|
|
90
|
+
const build = run('bun', ['run', 'build:binary'], { cwd: REPO });
|
|
91
|
+
if (build.status !== 0) {
|
|
92
|
+
console.error(build.stdout || '');
|
|
93
|
+
console.error(build.stderr || '');
|
|
94
|
+
throw new Error('build:binary failed');
|
|
95
|
+
}
|
|
96
|
+
if (!fs.existsSync(BIN_PATH)) throw new Error(`binary not found at ${BIN_PATH}`);
|
|
97
|
+
|
|
98
|
+
// 2. Run setup from both channels into fresh git projects.
|
|
99
|
+
const setupArgs = ['setup', '--quick', '--yes'];
|
|
100
|
+
const npmDir = makeProject('npm');
|
|
101
|
+
const binDir = makeProject('bin');
|
|
102
|
+
|
|
103
|
+
// try/finally so the throwaway temp git projects are removed even when setup
|
|
104
|
+
// or the comparison throws (no leaked dirs on any failure path).
|
|
105
|
+
try {
|
|
106
|
+
runSetup('npm', 'node', [path.join(REPO, 'bin', 'forge.js'), ...setupArgs], npmDir);
|
|
107
|
+
runSetup('bin', BIN_PATH, setupArgs, binDir);
|
|
108
|
+
|
|
109
|
+
// 3. Compare trees.
|
|
110
|
+
const a = hashTree(npmDir);
|
|
111
|
+
const b = hashTree(binDir);
|
|
112
|
+
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
113
|
+
const missingInBin = [];
|
|
114
|
+
const missingInNpm = [];
|
|
115
|
+
const differing = [];
|
|
116
|
+
for (const k of keys) {
|
|
117
|
+
if (!(k in b)) missingInBin.push(k);
|
|
118
|
+
else if (!(k in a)) missingInNpm.push(k);
|
|
119
|
+
else if (a[k] !== b[k]) differing.push(k);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const problems = missingInBin.length + missingInNpm.length + differing.length;
|
|
123
|
+
console.log(`\nParity: ${Object.keys(a).length} npm files vs ${Object.keys(b).length} binary files.`);
|
|
124
|
+
if (missingInBin.length) console.log(` Missing in binary: ${missingInBin.join(', ')}`);
|
|
125
|
+
if (missingInNpm.length) console.log(` Missing in npm: ${missingInNpm.join(', ')}`);
|
|
126
|
+
if (differing.length) console.log(` Differing bytes: ${differing.join(', ')}`);
|
|
127
|
+
|
|
128
|
+
if (problems > 0) {
|
|
129
|
+
console.error(`\nPARITY FAILED (${problems} discrepancies).`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
console.log('\nPARITY OK — npm and compiled binary produced byte-identical setup trees.');
|
|
133
|
+
} finally {
|
|
134
|
+
fs.rmSync(npmDir, { recursive: true, force: true });
|
|
135
|
+
fs.rmSync(binDir, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Exported for unit testing the asset-vs-install-artifact filtering. `hashTree`
|
|
140
|
+
// and the exclusion sets are the load-bearing parity logic; the rest of the file
|
|
141
|
+
// drives real builds and only runs when executed directly (guarded below).
|
|
142
|
+
export { hashTree, EXCLUDED_DIRS, EXCLUDED_FILES };
|
|
143
|
+
|
|
144
|
+
// Only build + compare when run as a script, not when imported by a test.
|
|
145
|
+
if (import.meta.main) main();
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Unit test for the parity-check asset filtering. The parity check compares the
|
|
2
|
+
// npm-vs-binary `setup --quick` trees; it MUST ignore package-manager install
|
|
3
|
+
// byproducts (node_modules/**, lockfiles) that are legitimately non-deterministic
|
|
4
|
+
// between runs/channels/OSes, while staying strict for real Forge assets. This
|
|
5
|
+
// test pins that exclusion so a genuine skills/rules/hook drift still fails but
|
|
6
|
+
// install junk never produces a false-positive parity failure.
|
|
7
|
+
|
|
8
|
+
import { test, expect } from 'bun:test';
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { hashTree, EXCLUDED_DIRS, EXCLUDED_FILES } from './parity-check.mjs';
|
|
13
|
+
|
|
14
|
+
function mkTree() {
|
|
15
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'parity-hashtree-'));
|
|
16
|
+
// Real Forge-managed assets — these MUST be compared.
|
|
17
|
+
fs.mkdirSync(path.join(dir, 'skills'), { recursive: true });
|
|
18
|
+
fs.writeFileSync(path.join(dir, 'skills', 'SKILL.md'), 'real asset\n');
|
|
19
|
+
fs.writeFileSync(path.join(dir, 'AGENTS.md'), 'real asset\n');
|
|
20
|
+
// Install byproducts — these MUST be excluded.
|
|
21
|
+
fs.mkdirSync(path.join(dir, 'node_modules', 'left-pad'), { recursive: true });
|
|
22
|
+
fs.writeFileSync(path.join(dir, 'node_modules', 'left-pad', 'index.js'), 'junk\n');
|
|
23
|
+
fs.writeFileSync(path.join(dir, 'node_modules', '.package-lock.json'), '{}\n');
|
|
24
|
+
fs.writeFileSync(path.join(dir, 'package-lock.json'), '{"v":1}\n');
|
|
25
|
+
return dir;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
test('hashTree excludes node_modules and lockfiles but keeps real assets', () => {
|
|
29
|
+
const dir = mkTree();
|
|
30
|
+
try {
|
|
31
|
+
const keys = Object.keys(hashTree(dir)).sort();
|
|
32
|
+
expect(keys).toEqual(['AGENTS.md', 'skills/SKILL.md']);
|
|
33
|
+
// No install artifact leaked into the comparison set.
|
|
34
|
+
expect(keys.some(k => k.includes('node_modules'))).toBe(false);
|
|
35
|
+
expect(keys.some(k => k.endsWith('package-lock.json'))).toBe(false);
|
|
36
|
+
} finally {
|
|
37
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('a real asset byte change is still detected (parity stays strict)', () => {
|
|
42
|
+
const dir = mkTree();
|
|
43
|
+
try {
|
|
44
|
+
const before = hashTree(dir);
|
|
45
|
+
fs.writeFileSync(path.join(dir, 'skills', 'SKILL.md'), 'DRIFTED asset\n');
|
|
46
|
+
const after = hashTree(dir);
|
|
47
|
+
expect(after['skills/SKILL.md']).not.toBe(before['skills/SKILL.md']);
|
|
48
|
+
} finally {
|
|
49
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('exclusion sets document the intended artifacts', () => {
|
|
54
|
+
expect(EXCLUDED_DIRS.has('node_modules')).toBe(true);
|
|
55
|
+
expect(EXCLUDED_DIRS.has('.git')).toBe(true);
|
|
56
|
+
expect(EXCLUDED_FILES.has('package-lock.json')).toBe(true);
|
|
57
|
+
expect(EXCLUDED_FILES.has('.package-lock.json')).toBe(true);
|
|
58
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_LOCK_FILES = [
|
|
7
|
+
'.github/agentic-workflows/behavioral-test.lock.yml',
|
|
8
|
+
'.github/workflows/behavioral-test.lock.yml',
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
const IMAGE_PINS = [
|
|
12
|
+
{
|
|
13
|
+
image: 'ghcr.io/github/gh-aw-mcpg:v0.3.0',
|
|
14
|
+
digest: 'sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d',
|
|
15
|
+
pinnedImage: 'ghcr.io/github/gh-aw-mcpg@sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
image: 'ghcr.io/github/github-mcp-server:v1.0.2',
|
|
19
|
+
digest: 'sha256:26db03408086a99cf1916348dcc4f9614206658f9082a8060dc7c81ad787f4ba',
|
|
20
|
+
pinnedImage: 'ghcr.io/github/github-mcp-server@sha256:26db03408086a99cf1916348dcc4f9614206658f9082a8060dc7c81ad787f4ba',
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
function updateManifestLine(line) {
|
|
25
|
+
const prefix = '# gh-aw-manifest: ';
|
|
26
|
+
if (!line.startsWith(prefix)) {
|
|
27
|
+
return line;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const manifest = JSON.parse(line.slice(prefix.length));
|
|
31
|
+
if (!Array.isArray(manifest.containers)) {
|
|
32
|
+
return line;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
manifest.containers = manifest.containers.map((container) => {
|
|
36
|
+
const pin = IMAGE_PINS.find(candidate => candidate.image === container.image);
|
|
37
|
+
if (!pin) {
|
|
38
|
+
return container;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
...container,
|
|
43
|
+
digest: pin.digest,
|
|
44
|
+
pinned_image: pin.pinnedImage,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return `${prefix}${JSON.stringify(manifest)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function pinLockContent(content) {
|
|
52
|
+
const lines = content.split(/\r?\n/);
|
|
53
|
+
if (lines.length > 1) {
|
|
54
|
+
lines[1] = updateManifestLine(lines[1]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let pinned = lines.join('\n');
|
|
58
|
+
const manifestPrefix = /^# gh-aw-manifest: .*$/m;
|
|
59
|
+
const manifestMatch = pinned.match(manifestPrefix);
|
|
60
|
+
const manifestLine = manifestMatch ? manifestMatch[0] : null;
|
|
61
|
+
pinned = pinned.replace(manifestPrefix, '__GH_AW_MANIFEST_PLACEHOLDER__');
|
|
62
|
+
|
|
63
|
+
for (const pin of IMAGE_PINS) {
|
|
64
|
+
pinned = pinned.split(pin.image).join(pin.pinnedImage);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (manifestLine) {
|
|
68
|
+
pinned = pinned.replace('__GH_AW_MANIFEST_PLACEHOLDER__', updateManifestLine(manifestLine));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return pinned;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function pinLockFile(filePath) {
|
|
75
|
+
const original = fs.readFileSync(filePath, 'utf8');
|
|
76
|
+
const pinned = pinLockContent(original);
|
|
77
|
+
if (pinned !== original) {
|
|
78
|
+
fs.writeFileSync(filePath, pinned, 'utf8');
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function main() {
|
|
85
|
+
const args = process.argv.slice(2);
|
|
86
|
+
const files = args.length > 0 ? args : DEFAULT_LOCK_FILES;
|
|
87
|
+
const changed = [];
|
|
88
|
+
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
const filePath = path.resolve(process.cwd(), file);
|
|
91
|
+
if (pinLockFile(filePath)) {
|
|
92
|
+
changed.push(file);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (changed.length === 0) {
|
|
97
|
+
console.log('Agentic workflow runtime images already pinned');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log(`Pinned agentic workflow runtime images: ${changed.join(', ')}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (require.main === module) {
|
|
105
|
+
main();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
IMAGE_PINS,
|
|
110
|
+
pinLockContent,
|
|
111
|
+
pinLockFile,
|
|
112
|
+
};
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
set -euo pipefail
|
|
16
16
|
|
|
17
17
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
if [[ -f "$SCRIPT_DIR/bootstrap-windows-tools.sh" ]]; then
|
|
19
|
+
source "$SCRIPT_DIR/bootstrap-windows-tools.sh"
|
|
20
|
+
fi
|
|
18
21
|
source "$SCRIPT_DIR/lib/sanitize.sh" || { echo "FATAL: failed to source sanitize.sh" >&2; exit 2; }
|
|
19
22
|
|
|
20
23
|
# Source these but don't fail if missing (tests may not have them)
|
|
@@ -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
|
+
];
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -u
|
|
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
|
+
|
|
11
|
+
exit_code=0
|
|
12
|
+
have_gh=0
|
|
13
|
+
|
|
14
|
+
ok() {
|
|
15
|
+
printf 'OK %s - %s\n' "$1" "$2"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fixed() {
|
|
19
|
+
printf 'FIXED %s - %s\n' "$1" "$2"
|
|
20
|
+
if [ "$exit_code" -lt 1 ]; then
|
|
21
|
+
exit_code=1
|
|
22
|
+
fi
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
action() {
|
|
26
|
+
printf 'ACTION %s - %s\n' "$1" "$2"
|
|
27
|
+
exit_code=2
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
is_windows_shell() {
|
|
31
|
+
case "$(uname -s 2>/dev/null || printf unknown)" in
|
|
32
|
+
MINGW*|MSYS*|CYGWIN*)
|
|
33
|
+
return 0
|
|
34
|
+
;;
|
|
35
|
+
*)
|
|
36
|
+
return 1
|
|
37
|
+
;;
|
|
38
|
+
esac
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
check_tool() {
|
|
42
|
+
tool="$1"
|
|
43
|
+
hint="$2"
|
|
44
|
+
|
|
45
|
+
if command -v "$tool" >/dev/null 2>&1; then
|
|
46
|
+
ok "tool $tool" "found $(command -v "$tool")"
|
|
47
|
+
return 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
action "tool $tool" "missing"
|
|
51
|
+
if is_windows_shell; then
|
|
52
|
+
printf ' Windows hint: %s\n' "$hint"
|
|
53
|
+
else
|
|
54
|
+
printf ' Install hint: %s\n' "$hint"
|
|
55
|
+
fi
|
|
56
|
+
return 1
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
check_tools() {
|
|
60
|
+
check_tool "node" "install Node.js (https://nodejs.org)" || true
|
|
61
|
+
check_tool "jq" "winget install jqlang.jq" || true
|
|
62
|
+
|
|
63
|
+
if check_tool "gh" "winget install GitHub.cli"; then
|
|
64
|
+
have_gh=1
|
|
65
|
+
fi
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
check_github_auth() {
|
|
69
|
+
if [ "$have_gh" -ne 1 ]; then
|
|
70
|
+
return 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
if gh auth status >/dev/null 2>&1; then
|
|
74
|
+
ok "github-auth" "gh auth status succeeded"
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
action "github-auth" "not logged in; run: gh auth login"
|
|
79
|
+
}
|
|
80
|
+
|
|
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"
|
|
89
|
+
else
|
|
90
|
+
action "kernel-init" "kernel issue store not initializable; inspect: node bin/forge.js doctor"
|
|
91
|
+
return 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
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"
|
|
96
|
+
else
|
|
97
|
+
action "kernel-doctor" "forge doctor reports an unhealthy filesystem; inspect the kernel database path"
|
|
98
|
+
fi
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
main() {
|
|
102
|
+
check_tools
|
|
103
|
+
check_github_auth
|
|
104
|
+
check_kernel
|
|
105
|
+
exit "$exit_code"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
main "$@"
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
|
|
6
|
+
const { scoreIssues } = require('../lib/smart-status/scoring.js');
|
|
7
|
+
|
|
8
|
+
function readPayload() {
|
|
9
|
+
const raw = fs.readFileSync(0, 'utf8');
|
|
10
|
+
if (!raw.trim()) {
|
|
11
|
+
return { issues: [], epicStats: {} };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const payload = JSON.parse(raw);
|
|
15
|
+
return {
|
|
16
|
+
issues: Array.isArray(payload.issues) ? payload.issues : [],
|
|
17
|
+
epicStats: payload.epicStats && typeof payload.epicStats === 'object' ? payload.epicStats : {},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function main() {
|
|
22
|
+
try {
|
|
23
|
+
const payload = readPayload();
|
|
24
|
+
process.stdout.write(JSON.stringify(scoreIssues(payload.issues, { epicStats: payload.epicStats })));
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error(`smart-status-score: ${error.message}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
applyMergeTreeConflicts,
|
|
8
|
+
computeFileConflicts,
|
|
9
|
+
matchInProgressIssues,
|
|
10
|
+
parseWorktreePorcelain,
|
|
11
|
+
} = require('../lib/smart-status/conflicts.js');
|
|
12
|
+
|
|
13
|
+
function readPayload() {
|
|
14
|
+
const raw = fs.readFileSync(0, 'utf8');
|
|
15
|
+
if (!raw.trim()) {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return JSON.parse(raw);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function buildSessions(payload) {
|
|
23
|
+
let sessions = Array.isArray(payload.sessions)
|
|
24
|
+
? payload.sessions
|
|
25
|
+
: matchInProgressIssues(
|
|
26
|
+
parseWorktreePorcelain(payload.worktreePorcelain || '', payload.baseBranch || 'master'),
|
|
27
|
+
Array.isArray(payload.inProgressIssues) ? payload.inProgressIssues : [],
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (payload.branchFiles) {
|
|
31
|
+
sessions = computeFileConflicts(sessions, payload.branchFiles);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (payload.mergeTreeResults) {
|
|
35
|
+
sessions = applyMergeTreeConflicts(sessions, payload.mergeTreeResults);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return sessions;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function main() {
|
|
42
|
+
try {
|
|
43
|
+
const payload = readPayload();
|
|
44
|
+
process.stdout.write(JSON.stringify(buildSessions(payload)));
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(`smart-status-sessions: ${error.message}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
main();
|