forge-workflow 0.0.10 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
- package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
- package/.cursor/rules/permissions-guidance.mdc +2 -2
- package/.forge/hooks/check-tdd.js +82 -5
- package/.forge/hooks/forge-native-hook.js +431 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +151 -61
- package/CHANGELOG.md +709 -0
- package/CLAUDE.md +9 -118
- package/QUICKSTART.md +175 -0
- package/README.md +275 -365
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +532 -489
- package/docs/INDEX.md +93 -0
- package/docs/PROJECT_DESIGN.md +685 -0
- package/docs/architecture/index.md +66 -0
- package/docs/architecture/notes/README.md +35 -0
- package/docs/architecture/subsystems/README.md +46 -0
- package/docs/forge/TOOLCHAIN.md +670 -0
- package/docs/forge/VALIDATION.md +82 -0
- package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
- package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
- package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
- package/docs/guides/GREPTILE_SETUP.md +46 -0
- package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
- package/docs/guides/MIGRATION.md +56 -0
- package/docs/guides/SETUP.md +121 -0
- package/docs/guides/SUPPORT.md +190 -0
- package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
- package/docs/guides/memory-backends.md +183 -0
- package/docs/reference/ADAPTERS.md +128 -0
- package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
- package/docs/reference/COMMANDS.md +214 -0
- package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
- package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
- package/docs/reference/HERMES_INTEGRATION.md +118 -0
- package/docs/reference/INSIGHTS_RECAP.md +63 -0
- package/docs/reference/INSTALL.md +164 -0
- package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
- package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
- package/docs/reference/RELEASE.md +68 -0
- package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
- package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
- package/docs/reference/SKILLS.md +35 -0
- package/docs/reference/STATUS_BOARD.md +80 -0
- package/docs/reference/TEMPLATES.md +106 -0
- package/docs/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
- package/docs/reference/VALIDATION.md +82 -0
- package/docs/reference/agent-permissions.md +169 -0
- package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
- package/docs/reference/control-plane-guarantees.md +125 -0
- package/docs/reference/dependency-chain.md +331 -0
- package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
- package/docs/reference/forge-kernel-schema.md +72 -0
- package/docs/reference/kernel-conflict-evaluators.md +27 -0
- package/docs/reference/patch-md-format.md +77 -0
- package/docs/reference/protected-state-surfaces.md +59 -0
- package/docs/reference/shepherd.md +155 -0
- package/docs/reference/superpowers-analysis.md +320 -0
- package/docs/reference/superpowers-integration-options.md +404 -0
- package/docs/reference/test-environment.md +519 -0
- package/docs/reference/upgrade-safety.md +59 -0
- package/lefthook.yml +18 -0
- package/lib/activation/ensure-forge-home.js +135 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1109 -0
- package/lib/adapters/greptile-review-adapter.js +141 -0
- package/lib/adapters/kernel-issue-adapter.js +101 -0
- package/lib/adapters/pr-state-adapter.js +484 -0
- package/lib/adoption-profiles.js +139 -0
- package/lib/agents/README.md +2 -6
- package/lib/agents/claude.plugin.json +3 -8
- package/lib/agents/codex.plugin.json +9 -1
- package/lib/agents/cursor.plugin.json +2 -6
- package/lib/agents/hermes.plugin.json +22 -0
- package/lib/agents-config.js +39 -1236
- package/lib/audit-evidence.js +282 -0
- package/lib/beads-detect.js +60 -0
- package/lib/beads-nudge.js +91 -0
- package/lib/beads-setup.js +121 -0
- package/lib/beads-sync-scaffold.js +25 -101
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_aliases.js +248 -0
- package/lib/commands/_issue.js +780 -77
- package/lib/commands/_manifest.js +93 -0
- package/lib/commands/_registry.js +99 -34
- package/lib/commands/_resolve-command-opts.js +230 -0
- package/lib/commands/_serve-security.js +270 -0
- package/lib/commands/adapter.js +12 -0
- package/lib/commands/add.js +118 -0
- package/lib/commands/audit.js +70 -0
- package/lib/commands/blocked.js +5 -0
- package/lib/commands/board.js +64 -0
- package/lib/commands/claim.js +21 -2
- package/lib/commands/claims.js +7 -0
- package/lib/commands/clean.js +485 -75
- package/lib/commands/close.js +2 -2
- package/lib/commands/comment.js +5 -0
- package/lib/commands/control.js +148 -0
- package/lib/commands/create.js +2 -2
- package/lib/commands/dev.js +185 -7
- package/lib/commands/doc-gate.js +336 -0
- package/lib/commands/doctor.js +156 -0
- package/lib/commands/explain.js +15 -0
- package/lib/commands/export.js +237 -0
- package/lib/commands/gate.js +209 -0
- package/lib/commands/hooks.js +377 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +604 -0
- package/lib/commands/insights.js +79 -0
- package/lib/commands/issue.js +12 -1
- package/lib/commands/issues.js +17 -0
- package/lib/commands/lint.js +5 -0
- package/lib/commands/list.js +2 -2
- package/lib/commands/memory.js +81 -0
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +362 -0
- package/lib/commands/new.js +12 -0
- package/lib/commands/options.js +241 -0
- package/lib/commands/orient.js +13 -0
- package/lib/commands/orphans.js +5 -0
- package/lib/commands/patch.js +67 -0
- package/lib/commands/plan.js +481 -29
- package/lib/commands/pr.js +88 -0
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +135 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +171 -0
- package/lib/commands/recap.js +75 -0
- package/lib/commands/recommend.js +0 -1
- package/lib/commands/release.js +104 -0
- package/lib/commands/remember.js +140 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +900 -971
- package/lib/commands/shepherd.js +501 -0
- package/lib/commands/ship.js +59 -1
- package/lib/commands/show.js +2 -2
- package/lib/commands/stage.js +192 -0
- package/lib/commands/stale.js +5 -0
- package/lib/commands/status.js +158 -21
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +4 -1
- package/lib/commands/test.js +43 -27
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +43 -18
- package/lib/commands/worktree.js +362 -99
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +977 -0
- package/lib/dep-guard/keyword-ripple.js +2 -2
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +35 -9
- package/lib/doc-gate/declaration.js +177 -0
- package/lib/doc-gate/detect.js +289 -0
- package/lib/doc-gate/gate.js +375 -0
- package/lib/doc-gate/okf-config.js +128 -0
- package/lib/doc-gate/okf.js +429 -0
- package/lib/docs-command.js +1161 -6
- package/lib/forge-issues.js +382 -11
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +192 -0
- package/lib/global-flags.js +104 -0
- package/lib/greptile-match.js +7 -63
- package/lib/grounding/context-events.js +230 -0
- package/lib/grounding/read-first.js +112 -0
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +541 -0
- package/lib/inbox.js +391 -0
- package/lib/insights.js +397 -0
- package/lib/issue-adapter.js +156 -0
- package/lib/issue-backend.js +145 -0
- package/lib/issue-render.js +220 -0
- package/lib/kernel/backing-issue.js +311 -0
- package/lib/kernel/broker.js +1218 -0
- package/lib/kernel/cli-broker-factory.js +130 -0
- package/lib/kernel/conflict-signal.js +82 -0
- package/lib/kernel/evaluators.js +195 -0
- package/lib/kernel/fs-class.js +495 -0
- package/lib/kernel/issue-command-contract.js +559 -0
- package/lib/kernel/issue-id-resolver.js +186 -0
- package/lib/kernel/lease-enforcer.js +158 -0
- package/lib/kernel/migrations.js +333 -0
- package/lib/kernel/owned-kernel.js +43 -0
- package/lib/kernel/planning-buckets-schema.js +109 -0
- package/lib/kernel/projection-jsonl-writer.js +450 -0
- package/lib/kernel/readiness-model.js +329 -0
- package/lib/kernel/schema.js +356 -0
- package/lib/kernel/sqlite-driver.js +2540 -0
- package/lib/kernel/taxonomy-validator.js +394 -0
- package/lib/lefthook-check.js +3 -2
- package/lib/lefthook-wiring.js +413 -0
- package/lib/mcp-config-renderer.js +288 -0
- package/lib/memory/graphiti-mcp.js +106 -0
- package/lib/memory/router.js +387 -0
- package/lib/memory/typed-api.js +102 -0
- package/lib/memory-digest.js +195 -0
- package/lib/merge-rules.js +395 -0
- package/lib/migrate-dry-run.js +466 -0
- package/lib/orientation.js +863 -0
- package/lib/package-manager-remediation.js +103 -0
- package/lib/package-root.js +381 -0
- package/lib/patch-intent.js +890 -0
- package/lib/plugin-catalog.js +3 -4
- package/lib/plugin-manager.js +0 -5
- package/lib/pr-bundle.js +186 -0
- package/lib/pr-monitor/auto-actions.js +175 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/digest.js +206 -0
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/gather.js +124 -0
- package/lib/pr-monitor/journal.js +299 -0
- package/lib/pr-monitor/monitor.js +146 -0
- package/lib/pr-monitor/render-sticky.js +192 -0
- package/lib/pr-monitor/upsert-sticky.js +169 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1314 -0
- package/lib/pr-shepherd.js +494 -0
- package/lib/pr-state-validator.js +59 -0
- package/lib/preflight/gates.js +237 -0
- package/lib/preflight/runner.js +116 -0
- package/lib/project-discovery.js +0 -53
- package/lib/project-memory.js +99 -497
- package/lib/protected-path-manifest.js +281 -0
- package/lib/protected-state-surfaces.js +387 -0
- package/lib/release-readiness.js +2105 -0
- package/lib/reset.js +59 -45
- package/lib/review-adapter.js +68 -0
- package/lib/rules-sync.js +260 -0
- package/lib/runtime-health.js +241 -20
- package/lib/safety-config-renderer.js +268 -0
- package/lib/setup-action-log.js +1 -7
- package/lib/setup.js +27 -65
- package/lib/shell-utils.js +76 -6
- package/lib/skills-sync.js +330 -0
- package/lib/smart-status/scoring.js +17 -3
- package/lib/status/beads-snapshot.js +45 -2
- package/lib/status/presenter.js +169 -18
- package/lib/status/snapshot.js +186 -0
- package/lib/sync-backend.js +202 -0
- package/lib/untrusted-content.js +52 -0
- package/lib/upgrade-safety.js +251 -0
- package/lib/workflow/enforce-stage.js +351 -45
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +11 -22
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +37 -35
- package/rules/documentation.md +19 -0
- package/rules/kernel-tracking.md +26 -0
- package/rules/security.md +22 -0
- package/rules/tdd.md +20 -0
- package/rules/workflow.md +27 -0
- package/scripts/auto-backing-issue.js +47 -0
- package/scripts/beads-context.sh +81 -57
- package/scripts/beads-upgrade-smoke.sh +24 -3
- package/scripts/bootstrap-windows-tools.sh +78 -0
- package/scripts/branch-protection.js +2 -3
- package/scripts/check-agents.js +34 -137
- package/scripts/commitlint.js +3 -1
- package/scripts/conflict-detect.sh +3 -0
- package/scripts/dep-guard.sh +22 -3
- package/scripts/file-index.sh +3 -0
- package/scripts/forge-team/lib/claim.sh +34 -18
- package/scripts/forge-team/lib/dashboard.sh +61 -86
- package/scripts/forge-team/lib/epic.sh +99 -263
- package/scripts/forge-team/lib/hooks.sh +26 -28
- package/scripts/forge-team/lib/identity.sh +4 -4
- package/scripts/forge-team/lib/sync-github.sh +49 -84
- package/scripts/forge-team/lib/verify.sh +93 -83
- package/scripts/forge-team/lib/workload.sh +41 -65
- package/scripts/forge-team/tests/claim.test.sh +25 -19
- package/scripts/forge-team/tests/dashboard.test.sh +31 -46
- package/scripts/forge-team/tests/epic.test.sh +52 -71
- package/scripts/forge-team/tests/hooks.test.sh +38 -50
- package/scripts/forge-team/tests/identity.test.sh +3 -3
- package/scripts/forge-team/tests/integration.test.sh +44 -66
- package/scripts/forge-team/tests/sync-github.test.sh +50 -83
- package/scripts/forge-team/tests/verify.test.sh +37 -46
- package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
- package/scripts/forge-team/tests/workload.test.sh +32 -66
- package/scripts/gen-command-manifest.js +153 -0
- package/scripts/gen-embedded-assets.mjs +129 -0
- package/scripts/install.ps1 +139 -0
- package/scripts/install.sh +268 -0
- package/scripts/lib/release-asset.mjs +84 -0
- package/scripts/parity-check.mjs +145 -0
- package/scripts/parity-check.test.mjs +58 -0
- package/scripts/pin-agentic-workflow-images.js +112 -0
- package/scripts/pr-auto-actions.js +93 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/pr-verdict-label.js +50 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +21 -94
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status.sh +60 -57
- package/scripts/spikes/config-race-bench.js +111 -0
- package/scripts/spikes/harness-capability-matrix.js +13 -0
- package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
- package/scripts/spikes/protected-path-manifest.js +20 -0
- package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
- package/scripts/sync-agent-skills.js +62 -0
- package/scripts/sync-utils.sh +3 -0
- package/scripts/test-ci-shard.js +13 -6
- package/scripts/test.js +95 -12
- package/skills/claim-safety/SKILL.md +102 -0
- package/skills/claim-safety/evals/evals.json +46 -0
- package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
- package/skills/dev/evals/evals.json +50 -0
- package/skills/hermes-forge/SKILL.md +185 -0
- package/skills/hermes-forge/evals/evals.json +46 -0
- package/skills/issue-basics/SKILL.md +111 -0
- package/skills/issue-basics/evals/evals.json +46 -0
- package/skills/kernel/SKILL.md +166 -0
- package/skills/kernel/evals/evals.json +50 -0
- package/skills/memory/SKILL.md +102 -0
- package/skills/parallel-deep-research/SKILL.md +14 -11
- package/skills/parallel-deep-research/evals/evals.json +11 -27
- package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
- package/skills/plan/evals/evals.json +42 -0
- package/skills/research/SKILL.md +195 -0
- package/skills/research/evals/evals.json +42 -0
- package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
- package/skills/review/evals/evals.json +42 -0
- package/skills/rollback/SKILL.md +110 -0
- package/skills/rollback/evals/evals.json +46 -0
- package/skills/rollback/references/methods.md +204 -0
- package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
- package/skills/shepherd/SKILL.md +66 -0
- package/skills/shepherd/evals/evals.json +42 -0
- package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
- package/skills/ship/evals/evals.json +42 -0
- package/skills/smith/SKILL.md +142 -0
- package/skills/smith/evals/evals.json +46 -0
- package/skills/smith/references/autonomy-and-gates.md +94 -0
- package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
- package/skills/sonarcloud/evals/evals.json +46 -0
- package/skills/sonarcloud-analysis/SKILL.md +18 -13
- package/skills/sonarcloud-analysis/evals/evals.json +11 -15
- package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
- package/skills/status/evals/evals.json +50 -0
- package/skills/triage-ready/SKILL.md +121 -0
- package/skills/triage-ready/evals/evals.json +42 -0
- package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
- package/skills/validate/evals/evals.json +42 -0
- package/skills/verify/SKILL.md +299 -0
- package/skills/verify/evals/evals.json +50 -0
- package/.claude/commands/dev.md +0 -345
- package/.claude/commands/plan.md +0 -566
- package/.claude/commands/premerge.md +0 -186
- package/.claude/commands/research.md +0 -42
- package/.claude/commands/review.md +0 -451
- package/.claude/commands/rollback.md +0 -721
- package/.claude/commands/ship.md +0 -213
- package/.claude/commands/sonarcloud.md +0 -152
- package/.claude/commands/status.md +0 -90
- package/.claude/commands/validate.md +0 -288
- package/.claude/commands/verify.md +0 -269
- package/.claude/rules/workflow.md +0 -121
- package/.cline/workflows/dev.md +0 -342
- package/.cline/workflows/plan.md +0 -563
- package/.cline/workflows/premerge.md +0 -183
- package/.cline/workflows/research.md +0 -39
- package/.cline/workflows/review.md +0 -448
- package/.cline/workflows/rollback.md +0 -718
- package/.cline/workflows/ship.md +0 -210
- package/.cline/workflows/sonarcloud.md +0 -146
- package/.cline/workflows/status.md +0 -87
- package/.cline/workflows/validate.md +0 -285
- package/.cline/workflows/verify.md +0 -266
- package/.codex/config.toml +0 -11
- package/.codex/skills/dev/SKILL.md +0 -345
- package/.codex/skills/plan/SKILL.md +0 -566
- package/.codex/skills/premerge/SKILL.md +0 -186
- package/.codex/skills/research/SKILL.md +0 -42
- package/.codex/skills/review/SKILL.md +0 -451
- package/.codex/skills/rollback/SKILL.md +0 -721
- package/.codex/skills/ship/SKILL.md +0 -213
- package/.codex/skills/sonarcloud/SKILL.md +0 -149
- package/.codex/skills/status/SKILL.md +0 -90
- package/.codex/skills/validate/SKILL.md +0 -288
- package/.codex/skills/verify/SKILL.md +0 -269
- package/.cursor/commands/dev.md +0 -342
- package/.cursor/commands/plan.md +0 -563
- package/.cursor/commands/premerge.md +0 -183
- package/.cursor/commands/research.md +0 -39
- package/.cursor/commands/review.md +0 -448
- package/.cursor/commands/ship.md +0 -210
- package/.cursor/commands/sonarcloud.md +0 -146
- package/.cursor/commands/status.md +0 -87
- package/.cursor/commands/validate.md +0 -285
- package/.cursor/commands/verify.md +0 -266
- package/.cursorrules +0 -149
- package/.github/prompts/premerge.prompt.md +0 -188
- package/.github/prompts/research.prompt.md +0 -44
- package/.github/prompts/rollback.prompt.md +0 -723
- package/.github/prompts/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -89
- package/.github/workflows/github-to-beads.yml +0 -100
- package/.kilocode/workflows/dev.md +0 -346
- package/.kilocode/workflows/plan.md +0 -567
- package/.kilocode/workflows/premerge.md +0 -187
- package/.kilocode/workflows/research.md +0 -43
- package/.kilocode/workflows/review.md +0 -452
- package/.kilocode/workflows/rollback.md +0 -722
- package/.kilocode/workflows/ship.md +0 -214
- package/.kilocode/workflows/sonarcloud.md +0 -150
- package/.kilocode/workflows/status.md +0 -91
- package/.kilocode/workflows/validate.md +0 -289
- package/.kilocode/workflows/verify.md +0 -270
- package/.opencode/commands/dev.md +0 -345
- package/.opencode/commands/plan.md +0 -566
- package/.opencode/commands/premerge.md +0 -186
- package/.opencode/commands/research.md +0 -42
- package/.opencode/commands/review.md +0 -451
- package/.opencode/commands/rollback.md +0 -721
- package/.opencode/commands/ship.md +0 -213
- package/.opencode/commands/sonarcloud.md +0 -149
- package/.opencode/commands/status.md +0 -90
- package/.opencode/commands/validate.md +0 -288
- package/.opencode/commands/verify.md +0 -269
- package/.roo/commands/dev.md +0 -346
- package/.roo/commands/plan.md +0 -567
- package/.roo/commands/premerge.md +0 -187
- package/.roo/commands/research.md +0 -43
- package/.roo/commands/review.md +0 -452
- package/.roo/commands/rollback.md +0 -722
- package/.roo/commands/ship.md +0 -214
- package/.roo/commands/sonarcloud.md +0 -150
- package/.roo/commands/status.md +0 -91
- package/.roo/commands/validate.md +0 -289
- package/.roo/commands/verify.md +0 -270
- package/docs/BEADS_GITHUB_SYNC.md +0 -281
- package/docs/GREPTILE_SETUP.md +0 -400
- package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
- package/docs/SETUP.md +0 -663
- package/docs/VALIDATION.md +0 -363
- package/lib/agents/cline.plugin.json +0 -29
- package/lib/agents/copilot.plugin.json +0 -24
- package/lib/agents/kilocode.plugin.json +0 -22
- package/lib/agents/opencode.plugin.json +0 -23
- package/lib/agents/roo.plugin.json +0 -30
- package/lib/beads-bootstrap.js +0 -225
- package/lib/beads-health-check.js +0 -188
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -584
- package/scripts/github-beads-sync/comment.mjs +0 -64
- package/scripts/github-beads-sync/config.mjs +0 -148
- package/scripts/github-beads-sync/github-api.mjs +0 -131
- package/scripts/github-beads-sync/index.mjs +0 -356
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -132
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
- package/scripts/github-beads-sync/run-bd.mjs +0 -161
- package/scripts/github-beads-sync/sanitize.mjs +0 -121
- package/scripts/github-beads-sync.config.json +0 -26
- package/scripts/sync-commands.js +0 -600
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lazy `.forge/` home creation (activation foundation).
|
|
5
|
+
*
|
|
6
|
+
* Discovery ≠ initialization. The global Forge plugin (SessionStart hook +
|
|
7
|
+
* `activation` skill) must create NOTHING in a user's repo. The FIRST verb that
|
|
8
|
+
* performs a real MUTATION (e.g. `forge claim`, `forge create`, `forge
|
|
9
|
+
* remember`) is what lazily materializes the bare-minimum `.forge/` skeleton.
|
|
10
|
+
* Read-only verbs (`ready`, `show`, `status`, `recap`, …) never call this, so a
|
|
11
|
+
* bare repo stays untouched until the user actually changes state.
|
|
12
|
+
*
|
|
13
|
+
* "Bare-minimum" here is strictly LESS than `forge init --minimal`: this writes
|
|
14
|
+
* ONLY `.forge/config.yaml` with every gate disabled (the `minimal` adoption
|
|
15
|
+
* profile). It installs NO git hooks, NO lefthook.yml, NO protected-paths
|
|
16
|
+
* manifest, NO `.mcp.json`, and NO scripts tree — those remain the opt-in
|
|
17
|
+
* payload of `forge setup`, never forced. Progressive growth: heavier slices
|
|
18
|
+
* initialize themselves on first use.
|
|
19
|
+
*
|
|
20
|
+
* @module activation/ensure-forge-home
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('node:fs');
|
|
24
|
+
const path = require('node:path');
|
|
25
|
+
|
|
26
|
+
const { renderAdoptionConfigYaml } = require('../adoption-profiles');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Core verbs that MUTATE project state and therefore need `.forge/` to exist.
|
|
30
|
+
*
|
|
31
|
+
* Deliberately excludes `init`/`setup` — they own `.forge/` creation with their
|
|
32
|
+
* own (richer) logic, and pre-creating a minimal config would trip their
|
|
33
|
+
* no-clobber guard. Also excludes every read-only verb, so those write nothing.
|
|
34
|
+
*
|
|
35
|
+
* Deliberately EXCLUDES `gate` and `stage`: each has read-only subcommands
|
|
36
|
+
* (`gate status`/`gate check`, `stage --list`/`--current`) that must not create
|
|
37
|
+
* `.forge/` — a verb-level trigger would violate the foundation's own
|
|
38
|
+
* "read-only writes nothing" invariant. Their genuinely-mutating forms
|
|
39
|
+
* self-manage without ensureForgeHome: `gate enable|disable` and `role` write
|
|
40
|
+
* via the config writer (which creates `.forge/config.yaml` if absent), while
|
|
41
|
+
* `gate approve|reject` and `stage --start|--complete` write kernel events/runs
|
|
42
|
+
* through the broker (which lazily creates its own store). Subcommand-level
|
|
43
|
+
* granularity is deferred; excluding the whole verb is the correct, safe default
|
|
44
|
+
* because the read-only forms are the common case. `role` is retained: it has NO
|
|
45
|
+
* read-only form (every valid invocation writes config), so it violates nothing.
|
|
46
|
+
*
|
|
47
|
+
* A command module may override membership by exporting `mutating: true|false`;
|
|
48
|
+
* this Set is the default classification for the foundation.
|
|
49
|
+
*/
|
|
50
|
+
const MUTATING_VERBS = new Set([
|
|
51
|
+
'claim',
|
|
52
|
+
'close',
|
|
53
|
+
'create',
|
|
54
|
+
'comment',
|
|
55
|
+
'update',
|
|
56
|
+
'add',
|
|
57
|
+
'new',
|
|
58
|
+
'remember',
|
|
59
|
+
'role',
|
|
60
|
+
'patch',
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Decide whether a verb should lazily ensure the `.forge/` home.
|
|
65
|
+
*
|
|
66
|
+
* A command module's explicit `mutating` boolean wins; otherwise fall back to
|
|
67
|
+
* the default {@link MUTATING_VERBS} classification.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} name - Command/verb name being dispatched.
|
|
70
|
+
* @param {object} [command] - The resolved command module (may declare `mutating`).
|
|
71
|
+
* @returns {boolean}
|
|
72
|
+
*/
|
|
73
|
+
function isMutatingVerb(name, command) {
|
|
74
|
+
if (command && typeof command === 'object' && typeof command.mutating === 'boolean') {
|
|
75
|
+
return command.mutating;
|
|
76
|
+
}
|
|
77
|
+
return MUTATING_VERBS.has(name);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Render the bare-minimum, gates-disabled config body.
|
|
82
|
+
*
|
|
83
|
+
* Reuses the canonical `minimal` adoption profile so the produced YAML is
|
|
84
|
+
* schema-valid and identical in spirit to `forge init --minimal` (minus the
|
|
85
|
+
* hooks/protected-paths side effects). Overridable via `deps.renderConfig` for
|
|
86
|
+
* tests that don't want to depend on the profile renderer.
|
|
87
|
+
*
|
|
88
|
+
* @param {object} [deps]
|
|
89
|
+
* @returns {string} config.yaml contents
|
|
90
|
+
*/
|
|
91
|
+
function renderMinimalConfig(deps = {}) {
|
|
92
|
+
const render = deps.renderConfig || (() => renderAdoptionConfigYaml('minimal'));
|
|
93
|
+
return render();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Idempotently create the bare-minimum `.forge/` skeleton for a mutating verb.
|
|
98
|
+
*
|
|
99
|
+
* No-clobber by construction: if `.forge/` already exists (inited repo, or a
|
|
100
|
+
* partially-created home), this is a NO-OP and never touches the user's files.
|
|
101
|
+
* The kernel/issue store (`.forge/kernel/…`) is created lazily by the broker on
|
|
102
|
+
* the mutating verb itself — this function only guarantees the config skeleton.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} [projectRoot=process.cwd()] - Repo root to initialize.
|
|
105
|
+
* @param {object} [deps] - Injectable seams: `fs`, `renderConfig`.
|
|
106
|
+
* @returns {{ created: boolean, reason?: string, configPath: string }}
|
|
107
|
+
*/
|
|
108
|
+
function ensureForgeHome(projectRoot = process.cwd(), deps = {}) {
|
|
109
|
+
const fsImpl = deps.fs || fs;
|
|
110
|
+
const forgeDir = path.join(projectRoot, '.forge');
|
|
111
|
+
const configPath = path.join(forgeDir, 'config.yaml');
|
|
112
|
+
|
|
113
|
+
// Idempotent + self-healing. The presence of `config.yaml` — NOT the `.forge/`
|
|
114
|
+
// directory alone — is what marks the home as initialized. Keying the
|
|
115
|
+
// no-clobber check on the config FILE means a half-init (dir created but config
|
|
116
|
+
// never written: disk full, permission error, or the process killed between
|
|
117
|
+
// mkdir and write) is COMPLETED on the next call instead of being permanently
|
|
118
|
+
// stuck behind a dir-exists early-return. We only ever ADD a missing
|
|
119
|
+
// config.yaml and never overwrite an existing one, so a real inited repo is
|
|
120
|
+
// still never clobbered. `mkdirSync({recursive})` is a no-op if the dir exists.
|
|
121
|
+
if (fsImpl.existsSync(configPath)) {
|
|
122
|
+
return { created: false, reason: 'config-exists', configPath };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
fsImpl.mkdirSync(forgeDir, { recursive: true });
|
|
126
|
+
fsImpl.writeFileSync(configPath, renderMinimalConfig(deps), 'utf8');
|
|
127
|
+
return { created: true, configPath };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = {
|
|
131
|
+
ensureForgeHome,
|
|
132
|
+
isMutatingVerb,
|
|
133
|
+
renderMinimalConfig,
|
|
134
|
+
MUTATING_VERBS,
|
|
135
|
+
};
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { GreptileReviewAdapter } = require('./adapters/greptile-review-adapter');
|
|
6
|
+
const { validateReviewAdapter } = require('./review-adapter');
|
|
7
|
+
|
|
8
|
+
const RESERVED_ADAPTER_NAMES = new Set(['greptile']);
|
|
9
|
+
const REQUIRED_NORMALIZED_THREAD_KEYS = ['id', 'commentId', 'file', 'line', 'body', 'author', 'isResolved', 'raw'];
|
|
10
|
+
|
|
11
|
+
function parseOption(args, name, fallback = undefined) {
|
|
12
|
+
const prefix = `--${name}=`;
|
|
13
|
+
const inline = args.find((arg) => arg.startsWith(prefix));
|
|
14
|
+
if (inline) {
|
|
15
|
+
return inline.slice(prefix.length);
|
|
16
|
+
}
|
|
17
|
+
const index = args.indexOf(`--${name}`);
|
|
18
|
+
if (index !== -1 && args[index + 1] && !args[index + 1].startsWith('--')) {
|
|
19
|
+
return args[index + 1];
|
|
20
|
+
}
|
|
21
|
+
if (index !== -1) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function validateAdapterName(name) {
|
|
28
|
+
if (!name || typeof name !== 'string') {
|
|
29
|
+
return 'Adapter name is required';
|
|
30
|
+
}
|
|
31
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
32
|
+
return 'Adapter name must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens';
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function assertValidAdapterName(name) {
|
|
38
|
+
const error = validateAdapterName(name);
|
|
39
|
+
if (error) {
|
|
40
|
+
throw new Error(error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isReservedAdapterName(name) {
|
|
45
|
+
return RESERVED_ADAPTER_NAMES.has(name);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function adapterNameToClassName(name) {
|
|
49
|
+
return `${name
|
|
50
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
51
|
+
.filter(Boolean)
|
|
52
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
53
|
+
.join('')}ReviewAdapter`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getReviewAdapterDir(projectRoot) {
|
|
57
|
+
return path.join(projectRoot, '.forge', 'adapters', 'review');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getAdapterPath(projectRoot, name, kind = 'review') {
|
|
61
|
+
assertValidAdapterName(name);
|
|
62
|
+
const adapterDir = path.resolve(projectRoot, '.forge', 'adapters', kind);
|
|
63
|
+
const adapterPath = path.resolve(adapterDir, `${name}.js`);
|
|
64
|
+
if (!adapterPath.startsWith(`${adapterDir}${path.sep}`)) {
|
|
65
|
+
throw new Error('Adapter path must stay under .forge/adapters');
|
|
66
|
+
}
|
|
67
|
+
return adapterPath;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function renderReviewAdapterScaffold(name, template) {
|
|
71
|
+
const className = adapterNameToClassName(name);
|
|
72
|
+
return `'use strict';
|
|
73
|
+
|
|
74
|
+
class ${className} {
|
|
75
|
+
constructor(options = {}) {
|
|
76
|
+
this.id = options.id || '${name}';
|
|
77
|
+
this.kind = 'review';
|
|
78
|
+
this.name = options.name || '${className}';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async fetchThreads(_context) {
|
|
82
|
+
throw new Error('${name}.fetchThreads must connect to the review provider');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
parse(_payload) {
|
|
86
|
+
// Start from the ${template} template and normalize provider payloads into:
|
|
87
|
+
// { id, commentId, file, line, body, author, isResolved, raw }
|
|
88
|
+
throw new Error('${name}.parse must normalize provider payloads before use');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async reply(_context) {
|
|
92
|
+
throw new Error('${name}.reply must post a review-thread reply');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async resolve(_context) {
|
|
96
|
+
throw new Error('${name}.resolve must mark a review thread resolved');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
score(threads) {
|
|
100
|
+
return threads.map((thread) => ({ ...thread, resolved: false, reason: 'not scored' }));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = { ${className} };
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function scaffoldAdapter(args, projectRoot) {
|
|
109
|
+
const subcommand = args[0];
|
|
110
|
+
const name = args[1];
|
|
111
|
+
const kind = parseOption(args, 'kind', 'review');
|
|
112
|
+
const template = parseOption(args, 'template', 'greptile');
|
|
113
|
+
|
|
114
|
+
if (subcommand !== 'adapter' || !name) {
|
|
115
|
+
return { success: false, error: 'Usage: forge new adapter <name> --kind=review --template=greptile' };
|
|
116
|
+
}
|
|
117
|
+
const nameError = validateAdapterName(name);
|
|
118
|
+
if (nameError) {
|
|
119
|
+
return { success: false, error: nameError };
|
|
120
|
+
}
|
|
121
|
+
if (isReservedAdapterName(name)) {
|
|
122
|
+
return { success: false, error: `Adapter name is reserved: ${name}` };
|
|
123
|
+
}
|
|
124
|
+
if (kind !== 'review') {
|
|
125
|
+
return { success: false, error: 'Only --kind=review is supported in this foundation PR' };
|
|
126
|
+
}
|
|
127
|
+
if (template !== 'greptile') {
|
|
128
|
+
return { success: false, error: 'Only --template=greptile is supported in this foundation PR' };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const adapterDir = getReviewAdapterDir(projectRoot);
|
|
132
|
+
const adapterPath = getAdapterPath(projectRoot, name, kind);
|
|
133
|
+
fs.mkdirSync(adapterDir, { recursive: true });
|
|
134
|
+
|
|
135
|
+
if (fs.existsSync(adapterPath)) {
|
|
136
|
+
return { success: false, error: `Adapter already exists: ${path.relative(projectRoot, adapterPath)}` };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
fs.writeFileSync(adapterPath, renderReviewAdapterScaffold(name, template));
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
success: true,
|
|
143
|
+
output: `Created review adapter scaffold: ${path.relative(projectRoot, adapterPath).split(path.sep).join('/')}`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function loadAdapter(name, projectRoot) {
|
|
148
|
+
if (name === 'greptile') {
|
|
149
|
+
return new GreptileReviewAdapter();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const adapterPath = getAdapterPath(projectRoot, name);
|
|
153
|
+
if (!fs.existsSync(adapterPath)) {
|
|
154
|
+
throw new Error(`Adapter not found: ${name}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const mod = require(adapterPath);
|
|
158
|
+
const exported = mod.default || mod[adapterNameToClassName(name)] || mod.adapter || mod;
|
|
159
|
+
return typeof exported === 'function' ? new exported() : exported;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function hasNormalizedReviewThreadShape(thread) {
|
|
163
|
+
return Boolean(thread)
|
|
164
|
+
&& REQUIRED_NORMALIZED_THREAD_KEYS.every((key) => Object.prototype.hasOwnProperty.call(thread, key));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function runFixtureReplay(args, projectRoot) {
|
|
168
|
+
const name = args[1];
|
|
169
|
+
const fixturePath = parseOption(args, 'fixture');
|
|
170
|
+
|
|
171
|
+
if (!name || !fixturePath) {
|
|
172
|
+
return { success: false, error: 'Usage: forge adapter test <name> --fixture=<path>' };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let adapter;
|
|
176
|
+
try {
|
|
177
|
+
adapter = loadAdapter(name, projectRoot);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
return { success: false, error: error.message };
|
|
180
|
+
}
|
|
181
|
+
const validation = validateReviewAdapter(adapter);
|
|
182
|
+
if (!validation.valid) {
|
|
183
|
+
return { success: false, error: validation.errors.join('; ') };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const absoluteFixturePath = path.isAbsolute(fixturePath)
|
|
187
|
+
? fixturePath
|
|
188
|
+
: path.join(projectRoot, fixturePath);
|
|
189
|
+
let fixture;
|
|
190
|
+
let parsed;
|
|
191
|
+
let scores;
|
|
192
|
+
try {
|
|
193
|
+
fixture = JSON.parse(fs.readFileSync(absoluteFixturePath, 'utf8'));
|
|
194
|
+
const fixtureInput = Object.prototype.hasOwnProperty.call(fixture, 'input')
|
|
195
|
+
? fixture.input
|
|
196
|
+
: fixture;
|
|
197
|
+
parsed = adapter.parse(fixtureInput);
|
|
198
|
+
if (!Array.isArray(parsed)) {
|
|
199
|
+
throw new Error('Adapter parse must return an array');
|
|
200
|
+
}
|
|
201
|
+
if (!parsed.every(hasNormalizedReviewThreadShape)) {
|
|
202
|
+
throw new Error('Adapter parse must return normalized review threads');
|
|
203
|
+
}
|
|
204
|
+
scores = await adapter.score(parsed, {
|
|
205
|
+
projectRoot,
|
|
206
|
+
sinceCommit: fixture.sinceCommit || null,
|
|
207
|
+
_exec: fixture.gitOutput ? () => fixture.gitOutput : () => '',
|
|
208
|
+
});
|
|
209
|
+
if (!Array.isArray(scores)) {
|
|
210
|
+
throw new Error('Adapter score must return an array');
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
return { success: false, error: `Fixture replay failed: ${error.message}` };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (fixture.expect?.threads !== undefined && parsed.length !== fixture.expect.threads) {
|
|
217
|
+
return {
|
|
218
|
+
success: false,
|
|
219
|
+
error: `Expected ${fixture.expect.threads} parsed threads, got ${parsed.length}`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
success: true,
|
|
225
|
+
output: `Adapter ${name} fixture replay passed: ${parsed.length} parsed thread${parsed.length === 1 ? '' : 's'}, ${scores.length} scored result${scores.length === 1 ? '' : 's'}`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function listAdapters(projectRoot) {
|
|
230
|
+
const adapterDir = getReviewAdapterDir(projectRoot);
|
|
231
|
+
const local = fs.existsSync(adapterDir)
|
|
232
|
+
? fs.readdirSync(adapterDir)
|
|
233
|
+
.filter((file) => file.endsWith('.js'))
|
|
234
|
+
.map((file) => file.replace(/\.js$/, ''))
|
|
235
|
+
.filter((name) => !isReservedAdapterName(name))
|
|
236
|
+
: [];
|
|
237
|
+
return ['greptile', ...local].sort((left, right) => left.localeCompare(right));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function setAdapterEnabled(args, projectRoot, enabled) {
|
|
241
|
+
const name = args[1];
|
|
242
|
+
if (!name) {
|
|
243
|
+
return { success: false, error: `Usage: forge adapter ${enabled ? 'enable' : 'disable'} <name>` };
|
|
244
|
+
}
|
|
245
|
+
const nameError = validateAdapterName(name);
|
|
246
|
+
if (nameError) {
|
|
247
|
+
return { success: false, error: nameError };
|
|
248
|
+
}
|
|
249
|
+
let config;
|
|
250
|
+
try {
|
|
251
|
+
const configDir = path.join(projectRoot, '.forge');
|
|
252
|
+
const configPath = path.join(configDir, 'adapters.json');
|
|
253
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
254
|
+
const parsedConfig = fs.existsSync(configPath)
|
|
255
|
+
? JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
|
256
|
+
: null;
|
|
257
|
+
config = parsedConfig && typeof parsedConfig === 'object' && !Array.isArray(parsedConfig)
|
|
258
|
+
? parsedConfig
|
|
259
|
+
: { review: {} };
|
|
260
|
+
config.review = config.review && typeof config.review === 'object' && !Array.isArray(config.review)
|
|
261
|
+
? config.review
|
|
262
|
+
: {};
|
|
263
|
+
config.review[name] = { ...(config.review[name] || {}), enabled };
|
|
264
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
return { success: false, error: `Failed to update adapter config: ${error.message}` };
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
success: true,
|
|
270
|
+
output: `${enabled ? 'Enabled' : 'Disabled'} review adapter: ${name}`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function handleAdapterCommand(args, projectRoot) {
|
|
275
|
+
const action = args[0];
|
|
276
|
+
|
|
277
|
+
if (action === 'test') {
|
|
278
|
+
return runFixtureReplay(args, projectRoot);
|
|
279
|
+
}
|
|
280
|
+
if (action === 'list') {
|
|
281
|
+
return { success: true, output: listAdapters(projectRoot).join('\n') };
|
|
282
|
+
}
|
|
283
|
+
if (action === 'enable') {
|
|
284
|
+
return setAdapterEnabled(args, projectRoot, true);
|
|
285
|
+
}
|
|
286
|
+
if (action === 'disable') {
|
|
287
|
+
return setAdapterEnabled(args, projectRoot, false);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
success: false,
|
|
292
|
+
error: 'Usage: forge adapter <test|list|enable|disable> ...',
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
module.exports = {
|
|
297
|
+
adapterNameToClassName,
|
|
298
|
+
getAdapterPath,
|
|
299
|
+
handleAdapterCommand,
|
|
300
|
+
hasNormalizedReviewThreadShape,
|
|
301
|
+
isReservedAdapterName,
|
|
302
|
+
listAdapters,
|
|
303
|
+
runFixtureReplay,
|
|
304
|
+
scaffoldAdapter,
|
|
305
|
+
setAdapterEnabled,
|
|
306
|
+
validateAdapterName,
|
|
307
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
IssueAdapter,
|
|
5
|
+
decideIssueAuthority,
|
|
6
|
+
normalizeIssueStatus,
|
|
7
|
+
} = require('../issue-adapter.js');
|
|
8
|
+
|
|
9
|
+
class BeadsIssueAdapter extends IssueAdapter {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
super({
|
|
12
|
+
id: options.id || 'beads',
|
|
13
|
+
kind: 'issue',
|
|
14
|
+
name: options.name || 'Beads Issue Adapter',
|
|
15
|
+
version: options.version || '0.1.0',
|
|
16
|
+
});
|
|
17
|
+
this.runBeadsOperation = options.runBeadsOperation;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
run(operation, args = [], context = {}) {
|
|
21
|
+
if (typeof this.runBeadsOperation !== 'function') {
|
|
22
|
+
throw new TypeError('runBeadsOperation is not configured on this adapter');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return this.runBeadsOperation(operation, args, context, context.deps || {});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
list(args = [], context = {}) {
|
|
29
|
+
return this.run('list', args, context);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ready(args = [], context = {}) {
|
|
33
|
+
return this.run('ready', args, context);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
read(args = [], context = {}) {
|
|
37
|
+
return this.run('show', args, context);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
show(args = [], context = {}) {
|
|
41
|
+
return this.read(args, context);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
create(args = [], context = {}) {
|
|
45
|
+
return this.run('create', args, context);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
search(args = [], context = {}) {
|
|
49
|
+
return this.run('search', args, context);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
stats(args = [], context = {}) {
|
|
53
|
+
return this.run('stats', args, context);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
update(args = [], context = {}) {
|
|
57
|
+
return this.run('update', args, context);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
close(args = [], context = {}) {
|
|
61
|
+
return this.run('close', args, context);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
comment(args = [], context = {}) {
|
|
65
|
+
return this.run('comment', args, context);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
depAdd(args = [], context = {}) {
|
|
69
|
+
return this.run('dep.add', args, context);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
depRemove(args = [], context = {}) {
|
|
73
|
+
return this.run('dep.remove', args, context);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// De-bead parity: claim/release and the KAP-7/KAP-12 derived reads now route
|
|
77
|
+
// through the backend abstraction (the translations moved out of _issue.js). The
|
|
78
|
+
// beads layer maps claim -> `update <id> --claim`; release has no bd equivalent
|
|
79
|
+
// and returns the Kernel-only contract error (handled in runBeadsOperation).
|
|
80
|
+
claim(args = [], context = {}) {
|
|
81
|
+
return this.run('claim', args, context);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
release(args = [], context = {}) {
|
|
85
|
+
return this.run('release', args, context);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
blocked(args = [], context = {}) {
|
|
89
|
+
return this.run('blocked', args, context);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
stale(args = [], context = {}) {
|
|
93
|
+
return this.run('stale', args, context);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
orphans(args = [], context = {}) {
|
|
97
|
+
return this.run('orphans', args, context);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
lint(args = [], context = {}) {
|
|
101
|
+
return this.run('lint', args, context);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Lease ownership is Kernel-only; the beads layer returns the Kernel-only contract
|
|
105
|
+
// error (handled in runBeadsOperation), matching the release passthrough.
|
|
106
|
+
owns(args = [], context = {}) {
|
|
107
|
+
return this.run('owns', args, context);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Active-lease listing is Kernel-only (kernel_claims); the beads layer returns the
|
|
111
|
+
// Kernel-only contract error (handled in runBeadsOperation), matching owns/release.
|
|
112
|
+
claims(args = [], context = {}) {
|
|
113
|
+
return this.run('claims', args, context);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
mapStatus(status, context = {}) {
|
|
117
|
+
return normalizeIssueStatus(status, context);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
decideAuthority(change = {}, context = {}) {
|
|
121
|
+
return decideIssueAuthority(change, context);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
BeadsIssueAdapter,
|
|
127
|
+
};
|