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,211 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `forge preflight` — fast (~2-4 min) blast-radius gate.
|
|
5
|
+
*
|
|
6
|
+
* Gives DETERMINISTIC-GATE PARITY with CI so an agent catches what CI catches
|
|
7
|
+
* WITHOUT waiting on the hanging full suite. Runs four gates in order, fast-fail:
|
|
8
|
+
*
|
|
9
|
+
* 1. lint — eslint --max-warnings 0 on changed files (--all → whole tree)
|
|
10
|
+
* 2. drift — skills mirror + registry sync + agentic-workflow structural asserts
|
|
11
|
+
* 3. sonar — eslint-plugin-sonarjs cognitive-complexity=15 (+ parity rules)
|
|
12
|
+
* 4. affected — only the tests mapped from the changed files
|
|
13
|
+
*
|
|
14
|
+
* Exit non-zero on any gate failure. Run this before `forge push`.
|
|
15
|
+
*
|
|
16
|
+
* @module commands/preflight
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const { execFileSync } = require('node:child_process');
|
|
20
|
+
const { buildGates } = require('../preflight/gates');
|
|
21
|
+
const { runGates } = require('../preflight/runner');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Strictly resolve a base ref to diff HEAD against. Returns the ref name, or
|
|
25
|
+
* null when no base can be established (fresh repo: no origin/HEAD, no upstream,
|
|
26
|
+
* no main/master ref). Unlike test.js's resolver, this never falls back to a
|
|
27
|
+
* default name that doesn't exist — a missing base must be reported, not faked.
|
|
28
|
+
*
|
|
29
|
+
* @param {Function} exec - execFileSync-compatible
|
|
30
|
+
* @returns {string|null}
|
|
31
|
+
*/
|
|
32
|
+
/** Run a git command and return trimmed stdout, or null on any failure. */
|
|
33
|
+
function gitTryOut(exec, args) {
|
|
34
|
+
try {
|
|
35
|
+
return String(exec('git', args, { encoding: 'utf8', timeout: 3000, stdio: ['ignore', 'pipe', 'ignore'] }) || '').trim();
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** True when `ref` resolves to a real object in this repo. */
|
|
42
|
+
function gitVerifyRef(exec, ref) {
|
|
43
|
+
try {
|
|
44
|
+
exec('git', ['rev-parse', '--verify', '--quiet', ref], { stdio: 'ignore', timeout: 3000 });
|
|
45
|
+
return true;
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* True when `ref` resolves to the SAME commit as HEAD. Diffing HEAD against such
|
|
53
|
+
* a ref yields zero files, so it is never a usable integration base.
|
|
54
|
+
*/
|
|
55
|
+
function refEqualsHead(exec, ref) {
|
|
56
|
+
const refSha = gitTryOut(exec, ['rev-parse', '--verify', '--quiet', `${ref}^{commit}`]);
|
|
57
|
+
if (!refSha) return false;
|
|
58
|
+
const headSha = gitTryOut(exec, ['rev-parse', '--verify', '--quiet', 'HEAD^{commit}']);
|
|
59
|
+
return !!headSha && refSha === headSha;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveBaseRef(exec) {
|
|
63
|
+
const originHead = gitTryOut(exec, ['rev-parse', '--abbrev-ref', 'origin/HEAD']);
|
|
64
|
+
if (originHead && originHead !== 'origin/HEAD') return originHead;
|
|
65
|
+
|
|
66
|
+
// `@{upstream}` is the CURRENT branch's OWN remote-tracking ref. On a fully
|
|
67
|
+
// pushed feature branch it points AT HEAD, so diffing against it produces 0
|
|
68
|
+
// changed files and every gate passes vacuously (fail-OPEN). Accept it as a
|
|
69
|
+
// base only when it is a real integration point ahead of us — never when it
|
|
70
|
+
// resolves to our own HEAD. Fall through to a real base (origin/main, …) or
|
|
71
|
+
// FAIL if none exists, rather than manufacture a wrong-but-'resolved' base.
|
|
72
|
+
const upstream = gitTryOut(exec, ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}']);
|
|
73
|
+
if (upstream && !refEqualsHead(exec, upstream)) return upstream;
|
|
74
|
+
|
|
75
|
+
for (const ref of ['origin/main', 'origin/master', 'main', 'master']) {
|
|
76
|
+
if (gitVerifyRef(exec, ref)) return ref;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Resolve the change set for preflight, distinguishing "base resolved, genuinely
|
|
83
|
+
* zero changes" from "no base branch could be resolved". Only the latter is a
|
|
84
|
+
* gate failure — a fresh repo with no base cannot have its blast radius computed,
|
|
85
|
+
* so preflight must NOT pass vacuously.
|
|
86
|
+
*
|
|
87
|
+
* @param {Function} [exec] - execFileSync-compatible (injectable for tests)
|
|
88
|
+
* @param {{ runAll?: boolean }} [opts]
|
|
89
|
+
* @returns {{ resolved: boolean, changedFiles: string[], reason?: string, baseRef?: string }}
|
|
90
|
+
*/
|
|
91
|
+
function resolveChangeSet(exec = execFileSync, { runAll = false } = {}) {
|
|
92
|
+
if (runAll) {
|
|
93
|
+
return { resolved: true, changedFiles: [], reason: 'whole-tree scope (--all)' };
|
|
94
|
+
}
|
|
95
|
+
const baseRef = resolveBaseRef(exec);
|
|
96
|
+
if (!baseRef) {
|
|
97
|
+
return {
|
|
98
|
+
resolved: false,
|
|
99
|
+
changedFiles: [],
|
|
100
|
+
reason: 'could not resolve a base branch (no origin/HEAD, no upstream, no main/master ref)',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// Diff against the SAME base we just resolved. getChangedFiles() re-resolves
|
|
104
|
+
// its own diff ref (and can fall back to a different default branch), so use
|
|
105
|
+
// baseRef directly to keep the file list consistent with the resolved base.
|
|
106
|
+
return { resolved: true, baseRef, changedFiles: changedFilesForBase(exec, baseRef) };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Files changed between the merge-base of HEAD and `baseRef` and HEAD. */
|
|
110
|
+
function changedFilesForBase(exec, baseRef) {
|
|
111
|
+
const mergeBase = gitTryOut(exec, ['merge-base', 'HEAD', baseRef]);
|
|
112
|
+
const range = mergeBase ? `${mergeBase}...HEAD` : `${baseRef}...HEAD`;
|
|
113
|
+
const out = gitTryOut(exec, ['diff', '--name-only', range]);
|
|
114
|
+
if (out == null) return [];
|
|
115
|
+
return out.split(/\r?\n/).filter(Boolean);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Pick the change set: an injected resolveChangeSet wins; a legacy injected
|
|
120
|
+
* resolveChangedFiles vouches for resolution; otherwise resolve strictly.
|
|
121
|
+
*/
|
|
122
|
+
function selectChangeSet(deps, runAll) {
|
|
123
|
+
if (deps.resolveChangeSet) {
|
|
124
|
+
return deps.resolveChangeSet({ runAll });
|
|
125
|
+
}
|
|
126
|
+
if (deps.resolveChangedFiles) {
|
|
127
|
+
return { resolved: true, changedFiles: deps.resolveChangedFiles() || [] };
|
|
128
|
+
}
|
|
129
|
+
return resolveChangeSet(execFileSync, { runAll });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Print the fail-closed remedy when no base branch could be resolved. */
|
|
133
|
+
function reportBaseUnresolved(log, reason) {
|
|
134
|
+
log('forge preflight — fast blast-radius gate (deterministic parity with CI)');
|
|
135
|
+
log('');
|
|
136
|
+
log(`preflight FAILED — ${reason}.`);
|
|
137
|
+
log(' Remedy: give preflight a base branch to diff against, e.g.');
|
|
138
|
+
log(' git fetch origin && git branch --set-upstream-to=origin/main # your default branch');
|
|
139
|
+
log(' (or ensure origin/HEAD is set: git remote set-head origin -a)');
|
|
140
|
+
log(' Or scan the whole tree explicitly: forge preflight --all');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Summary mark for one gate result. */
|
|
144
|
+
function gateMark(result) {
|
|
145
|
+
if (result.skipped) return 'SKIP';
|
|
146
|
+
return result.ok ? 'PASS' : 'FAIL';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = {
|
|
150
|
+
name: 'preflight',
|
|
151
|
+
description: 'Fast deterministic-gate parity with CI (lint, drift/registry, sonar, affected tests)',
|
|
152
|
+
usage: 'forge preflight [--all]',
|
|
153
|
+
flags: {
|
|
154
|
+
'--all': 'Lint/scan the whole tree instead of only changed files',
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {string[]} _args
|
|
159
|
+
* @param {Object} flags
|
|
160
|
+
* @param {string} projectRoot
|
|
161
|
+
* @param {Object} [deps] - injectable { log, resolveChangedFiles, buildGates, runGates }
|
|
162
|
+
* @returns {Promise<{ success: boolean, results?: Array }>}
|
|
163
|
+
*/
|
|
164
|
+
handler: async (_args, flags, projectRoot, deps = {}) => {
|
|
165
|
+
const log = deps.log || console.log;
|
|
166
|
+
const build = deps.buildGates || buildGates;
|
|
167
|
+
const run = deps.runGates || runGates;
|
|
168
|
+
|
|
169
|
+
const runAll = !!(flags && (flags['--all'] || flags.all));
|
|
170
|
+
|
|
171
|
+
// Resolve the change set AND whether a base branch could be established.
|
|
172
|
+
const changeSet = selectChangeSet(deps, runAll) || {};
|
|
173
|
+
|
|
174
|
+
// B2 (N1): an unresolvable base is fail-CLOSED. Computing "0 changed files"
|
|
175
|
+
// from a missing base and passing every gate manufactures false confidence.
|
|
176
|
+
// Anything not EXPLICITLY resolved (including a missing `resolved` field from
|
|
177
|
+
// a future override) fails closed — never fail open on an indeterminate base.
|
|
178
|
+
if (!runAll && !changeSet.resolved) {
|
|
179
|
+
const reason = changeSet.reason || 'base branch could not be resolved';
|
|
180
|
+
reportBaseUnresolved(log, reason);
|
|
181
|
+
return { success: false, results: [], reason, baseUnresolved: true };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const changedFiles = changeSet.changedFiles || [];
|
|
185
|
+
|
|
186
|
+
log('forge preflight — fast blast-radius gate (deterministic parity with CI)');
|
|
187
|
+
log(runAll
|
|
188
|
+
? ' scope: whole tree (--all)'
|
|
189
|
+
: ` scope: ${changedFiles.length} changed file(s) vs base branch`);
|
|
190
|
+
log('');
|
|
191
|
+
|
|
192
|
+
const gates = build({ projectRoot, changedFiles, runAll });
|
|
193
|
+
const { ok, results } = await run(gates, { log: (line) => log(` ${line}`) });
|
|
194
|
+
|
|
195
|
+
log('');
|
|
196
|
+
log('Preflight summary:');
|
|
197
|
+
for (const r of results) {
|
|
198
|
+
const suffix = r.summary ? ` — ${r.summary}` : '';
|
|
199
|
+
log(` [${gateMark(r)}] ${r.name}${suffix}`);
|
|
200
|
+
}
|
|
201
|
+
log('');
|
|
202
|
+
log(ok
|
|
203
|
+
? 'preflight passed — safe to push'
|
|
204
|
+
: 'preflight FAILED — fix the failing gate above before `forge push`');
|
|
205
|
+
|
|
206
|
+
return { success: ok, results };
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
module.exports.resolveChangeSet = resolveChangeSet;
|
|
211
|
+
module.exports.resolveBaseRef = resolveBaseRef;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
buildPrime,
|
|
5
|
+
runOrientationCommand,
|
|
6
|
+
} = require('../orientation');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
name: 'prime',
|
|
10
|
+
description: 'Emit session-entry bounded orientation for agents',
|
|
11
|
+
usage: 'Usage: forge prime [--budget N] [--json]',
|
|
12
|
+
handler: (args, _flags, projectRoot) => runOrientationCommand(buildPrime, args, projectRoot),
|
|
13
|
+
};
|
package/lib/commands/push.js
CHANGED
|
@@ -54,6 +54,62 @@ function isFirstPush(branch, execFn) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Resolve the git worktree top-level directory.
|
|
59
|
+
*
|
|
60
|
+
* The pre-push hook checks for the nonce token relative to its own cwd, which
|
|
61
|
+
* git sets to the worktree root during `git push`. The handler's `projectRoot`
|
|
62
|
+
* is derived from INIT_CWD/cwd and can point at the MAIN repo root when forge
|
|
63
|
+
* runs from a linked worktree, so the token must be written to the real git
|
|
64
|
+
* top-level instead — otherwise lefthook never finds it and re-runs the suite.
|
|
65
|
+
*
|
|
66
|
+
* @param {function} execFn - execFileSync or mock
|
|
67
|
+
* @param {string} fallback - projectRoot to use when git resolution fails
|
|
68
|
+
* @returns {string} Absolute worktree top-level path, or the fallback
|
|
69
|
+
*/
|
|
70
|
+
function resolveGitToplevel(execFn, fallback) {
|
|
71
|
+
try {
|
|
72
|
+
const top = execFn('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim();
|
|
73
|
+
return top || fallback;
|
|
74
|
+
} catch (_err) { /* intentional: fall back to projectRoot outside a git worktree */ // NOSONAR S2486
|
|
75
|
+
return fallback;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Auto-file rail: best-effort, NON-BLOCKING ensure that the branch being pushed has a
|
|
81
|
+
* backing Kernel issue (kernel issue a4b8f56f) — so started work that is being pushed
|
|
82
|
+
* never goes unfiled. Idempotent (deduped by branch via ensureBackingIssue); a
|
|
83
|
+
* main/master/detached/ignore branch is skipped, and ANY failure degrades to null and
|
|
84
|
+
* NEVER aborts the push. Injectable via `deps._ensureBackingIssue` for tests.
|
|
85
|
+
*
|
|
86
|
+
* @param {string} projectRoot
|
|
87
|
+
* @param {function} execFn - execFileSync (for branch resolution)
|
|
88
|
+
* @param {object} deps - DI (may inject `_ensureBackingIssue`, `_kernelDriver`, `_kernelBroker`)
|
|
89
|
+
* @returns {Promise<object|null>}
|
|
90
|
+
*/
|
|
91
|
+
async function autoFileBackingIssueForPush(projectRoot, execFn, deps = {}) {
|
|
92
|
+
try {
|
|
93
|
+
// Lazy require INSIDE the try: a module-load failure must degrade to null and
|
|
94
|
+
// NEVER abort the push (mirrors the worktree helper; CodeRabbit #370).
|
|
95
|
+
const ensure = deps._ensureBackingIssue || require('../kernel/backing-issue').ensureBackingIssue;
|
|
96
|
+
const branch = getCurrentBranch(execFn);
|
|
97
|
+
if (!branch || branch === 'unknown') return null;
|
|
98
|
+
let driver = deps._kernelDriver;
|
|
99
|
+
let broker = deps._kernelBroker;
|
|
100
|
+
if (!driver || !broker) {
|
|
101
|
+
if (!fs.existsSync(path.join(projectRoot, '.git'))) return null;
|
|
102
|
+
const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
|
|
103
|
+
const built = await buildMigratedKernelIssueDeps({ projectRoot });
|
|
104
|
+
driver = driver || built.kernelDriver;
|
|
105
|
+
broker = broker || built.kernelBroker;
|
|
106
|
+
}
|
|
107
|
+
return await ensure({ branch, projectRoot, driver, broker });
|
|
108
|
+
} catch (_err) { /* non-blocking: tracking must never break a push */ // NOSONAR S2486
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
57
113
|
/**
|
|
58
114
|
* Run branch protection check as a subprocess.
|
|
59
115
|
* @param {function} execFn - execFileSync or mock
|
|
@@ -150,6 +206,10 @@ module.exports = {
|
|
|
150
206
|
return { success: false, quickMode, lintPassed: false, pushed: false };
|
|
151
207
|
}
|
|
152
208
|
|
|
209
|
+
// Auto-file rail (non-blocking): ensure the branch being pushed has a backing
|
|
210
|
+
// Kernel issue so started work never goes unfiled. Never aborts the push.
|
|
211
|
+
await autoFileBackingIssueForPush(projectRoot, execFn, deps || {});
|
|
212
|
+
|
|
153
213
|
// Step 2: Lint
|
|
154
214
|
const lintPassed = runLint(spawnFn, pkgManager, projectRoot);
|
|
155
215
|
if (!lintPassed) {
|
|
@@ -163,9 +223,11 @@ module.exports = {
|
|
|
163
223
|
return { success: false, quickMode, lintPassed: true, testsPassed: false, pushed: false };
|
|
164
224
|
}
|
|
165
225
|
|
|
166
|
-
// Step 4: Write nonce token so lefthook skips pre-push hooks
|
|
226
|
+
// Step 4: Write nonce token so lefthook skips pre-push hooks.
|
|
227
|
+
// Write to the git worktree top-level (where the hook checks), not the
|
|
228
|
+
// inherited projectRoot, so the skip works from linked worktrees too.
|
|
167
229
|
try {
|
|
168
|
-
tokenWrite(projectRoot);
|
|
230
|
+
tokenWrite(resolveGitToplevel(execFn, projectRoot));
|
|
169
231
|
} catch (_err) { /* intentional: non-fatal, lefthook will re-run hooks */ // NOSONAR S2486
|
|
170
232
|
log('Warning: Could not write forge push token — lefthook hooks will run again');
|
|
171
233
|
}
|
|
@@ -193,4 +255,9 @@ module.exports = {
|
|
|
193
255
|
pushed: true,
|
|
194
256
|
};
|
|
195
257
|
},
|
|
258
|
+
|
|
259
|
+
// Exposed for unit tests; not part of the CLI surface.
|
|
260
|
+
_internal: {
|
|
261
|
+
autoFileBackingIssueForPush,
|
|
262
|
+
},
|
|
196
263
|
};
|
package/lib/commands/ready.js
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const memoryRouter = require('../memory/router');
|
|
4
|
+
const { stripGlobalFlags } = require('../global-flags');
|
|
5
|
+
const { fenceUntrusted } = require('../untrusted-content');
|
|
6
|
+
|
|
7
|
+
const usage = 'Usage: forge recall [query] [--limit N] [--all] [--json]';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Separate the optional positional query from `--limit N` and `--json`.
|
|
11
|
+
* Global flags (e.g. `-p <dir>`, `--all`) are stripped first so they never
|
|
12
|
+
* corrupt the search query (kernel issue c1e090ff). `--all` is a GLOBAL boolean
|
|
13
|
+
* flag, so the handler reads it from its `flags` argument (or the raw args on a
|
|
14
|
+
* direct call) — not from this parsed query.
|
|
15
|
+
*
|
|
16
|
+
* @param {string[]} rawArgs - Raw command arguments.
|
|
17
|
+
* @returns {{ query: string, limit: (number|undefined), json: boolean }}
|
|
18
|
+
*/
|
|
19
|
+
function parseArgs(rawArgs) {
|
|
20
|
+
const args = stripGlobalFlags(rawArgs);
|
|
21
|
+
const words = [];
|
|
22
|
+
let limit;
|
|
23
|
+
let json = false;
|
|
24
|
+
|
|
25
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
26
|
+
const arg = args[index];
|
|
27
|
+
if (arg === '--json') {
|
|
28
|
+
json = true;
|
|
29
|
+
} else if (arg === '--limit') {
|
|
30
|
+
const value = Number(args[index + 1]);
|
|
31
|
+
if (Number.isInteger(value) && value > 0) {
|
|
32
|
+
limit = value;
|
|
33
|
+
index += 1;
|
|
34
|
+
}
|
|
35
|
+
} else if (arg.startsWith('--limit=')) {
|
|
36
|
+
const value = Number(arg.slice('--limit='.length));
|
|
37
|
+
if (Number.isInteger(value) && value > 0) {
|
|
38
|
+
limit = value;
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
words.push(arg);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { query: words.join(' ').trim(), limit, json };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function formatEntry(entry) {
|
|
49
|
+
const date = entry.timestamp ? entry.timestamp.slice(0, 10) : '';
|
|
50
|
+
const prefix = date ? `${date} ` : '';
|
|
51
|
+
const tagSuffix = entry.tags.length > 0 ? ` [${entry.tags.join(', ')}]` : '';
|
|
52
|
+
// Machine/insights records are LABELED with their source so they are never mistaken for a
|
|
53
|
+
// plain human note; human `remember` notes render clean.
|
|
54
|
+
const marker = entry.machine && entry.sourceAgent ? `(${entry.sourceAgent}) ` : '';
|
|
55
|
+
// Stored note text is UNTRUSTED (a planted memory could carry injected directives),
|
|
56
|
+
// so the human/agent-facing render is provenance-fenced. The `--json` path above
|
|
57
|
+
// keeps the raw note so programmatic consumers/parsers are unaffected.
|
|
58
|
+
const note = fenceUntrusted(entry.note, { source: 'memory' });
|
|
59
|
+
return `- ${prefix}${marker}${note}${tagSuffix}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function handler(args, flags, projectRoot) {
|
|
63
|
+
const { query, limit, json } = parseArgs(args);
|
|
64
|
+
// `--all` is a GLOBAL boolean flag: in production bin/forge.js strips it from
|
|
65
|
+
// args and sets flags.all; on a direct handler call it may still be in args.
|
|
66
|
+
const all = Boolean(flags && flags.all) || args.includes('--all');
|
|
67
|
+
|
|
68
|
+
const { notes, total, capped, scope } = memoryRouter.recall(projectRoot, { query, limit, all });
|
|
69
|
+
|
|
70
|
+
if (json) {
|
|
71
|
+
// Object (not a bare array) so programmatic consumers see the total and whether the
|
|
72
|
+
// result was truncated (raise --limit to page further).
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
output: `${JSON.stringify({ notes, total, capped, scope }, null, 2)}\n`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (notes.length === 0) {
|
|
80
|
+
const reason = query
|
|
81
|
+
? `No notes match "${query}".`
|
|
82
|
+
: 'No notes remembered yet. Use "forge remember <note>" to add one.';
|
|
83
|
+
return { success: true, output: reason };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const noun = scope === 'all' && !query ? 'stored memory record(s)' : 'remembered note(s)';
|
|
87
|
+
let header;
|
|
88
|
+
if (query) {
|
|
89
|
+
// BM25 returns at most `limit`; when full, signal it is the TOP-N, not the whole set.
|
|
90
|
+
header = capped
|
|
91
|
+
? `Top ${notes.length} note(s) matching "${query}" (raise --limit for more):`
|
|
92
|
+
: `${notes.length} note(s) matching "${query}":`;
|
|
93
|
+
} else if (capped) {
|
|
94
|
+
// Never a bare full dump: show the newest N and the true total.
|
|
95
|
+
header = `Showing ${notes.length} of ${total} ${noun} (newest first):`;
|
|
96
|
+
} else {
|
|
97
|
+
header = `${total} ${noun}:`;
|
|
98
|
+
}
|
|
99
|
+
const lines = notes.map(formatEntry);
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
output: [header, ...lines].join('\n'),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
name: 'recall',
|
|
108
|
+
description: 'Retrieve project-memory notes from the kernel-backed memory store',
|
|
109
|
+
usage,
|
|
110
|
+
flags: {
|
|
111
|
+
'--limit': 'Cap the number of notes returned',
|
|
112
|
+
'--all': 'Include machine/insights records in the no-query listing (query already searches all)',
|
|
113
|
+
'--json': 'Emit machine-readable JSON output',
|
|
114
|
+
},
|
|
115
|
+
handler,
|
|
116
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
buildIssueRecap,
|
|
5
|
+
formatOrientationText,
|
|
6
|
+
} = require('../orientation');
|
|
7
|
+
|
|
8
|
+
const usage = 'Usage: forge recap <issue> [--budget N] [--json]';
|
|
9
|
+
|
|
10
|
+
// Options that consume the following token as their value. The issue-id scan
|
|
11
|
+
// must skip these values so `forge recap --budget 220 <issue>` is not misread.
|
|
12
|
+
const OPTIONS_WITH_VALUES = new Set(['--budget']);
|
|
13
|
+
|
|
14
|
+
function readOption(args, name, fallback) {
|
|
15
|
+
const equals = args.find(arg => arg.startsWith(`${name}=`));
|
|
16
|
+
if (equals) return equals.slice(name.length + 1);
|
|
17
|
+
const index = args.indexOf(name);
|
|
18
|
+
if (index >= 0 && args[index + 1] && !args[index + 1].startsWith('-')) return args[index + 1];
|
|
19
|
+
return fallback;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readIssueArg(args) {
|
|
23
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
24
|
+
const arg = args[index];
|
|
25
|
+
if (!arg) continue;
|
|
26
|
+
if (OPTIONS_WITH_VALUES.has(arg)) {
|
|
27
|
+
index += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (arg.startsWith('-')) continue;
|
|
31
|
+
return arg;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// `forge recap <issue>` summarizes a single issue from the deterministic
|
|
37
|
+
// orientation source assembly — it is issue-scoped, not a project-wide recap.
|
|
38
|
+
async function handler(args, _flags, projectRoot) {
|
|
39
|
+
const issueId = readIssueArg(args);
|
|
40
|
+
if (!issueId) {
|
|
41
|
+
// Use `error` (not `output`) so the CLI dispatcher prints the usage line
|
|
42
|
+
// once via console.error instead of also appending a redundant bare
|
|
43
|
+
// "Command failed" (see bin/forge.js registry dispatch).
|
|
44
|
+
return { success: false, error: usage };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const recap = buildIssueRecap(projectRoot, issueId, {
|
|
48
|
+
budgetTokens: readOption(args, '--budget', undefined),
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
success: true,
|
|
52
|
+
output: args.includes('--json') ? `${JSON.stringify(recap, null, 2)}\n` : formatOrientationText(recap),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
name: 'recap',
|
|
58
|
+
description: 'Summarize a single issue from deterministic orientation source files',
|
|
59
|
+
usage,
|
|
60
|
+
handler,
|
|
61
|
+
};
|
|
@@ -23,7 +23,6 @@ const STAGE_NAMES = {
|
|
|
23
23
|
validate: 'Validate',
|
|
24
24
|
ship: 'Ship',
|
|
25
25
|
review: 'Review',
|
|
26
|
-
premerge: 'Premerge',
|
|
27
26
|
};
|
|
28
27
|
|
|
29
28
|
/**
|
|
@@ -116,4 +115,25 @@ function handleRecommend(flags, projectPath) {
|
|
|
116
115
|
return { budgetMode, recommendations };
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
|
|
118
|
+
async function handler(_args = [], flags = {}, projectRoot) {
|
|
119
|
+
const result = handleRecommend(flags, projectRoot);
|
|
120
|
+
if (result.error) {
|
|
121
|
+
return { success: false, error: result.error };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const output = [
|
|
125
|
+
'',
|
|
126
|
+
`🔧 Forge Tool Recommendations (Budget: ${result.budgetMode})`,
|
|
127
|
+
formatRecommendations(result.recommendations),
|
|
128
|
+
].join('\n');
|
|
129
|
+
|
|
130
|
+
return { success: true, output };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
name: 'recommend',
|
|
135
|
+
description: 'Show tool recommendations for the current project',
|
|
136
|
+
handler,
|
|
137
|
+
formatRecommendations,
|
|
138
|
+
handleRecommend
|
|
139
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
SUPPORTED_TARGET,
|
|
5
|
+
buildReadinessReport,
|
|
6
|
+
renderReadinessReport,
|
|
7
|
+
} = require('../release-readiness');
|
|
8
|
+
const { runIssueOperation: defaultRunIssueOperation } = require('../forge-issues');
|
|
9
|
+
const { normalizeArgs, normalizeIssueResult, withResolvedIssueBackend } = require('./_issue');
|
|
10
|
+
|
|
11
|
+
// `forge release <id>` releases a claimed issue; `forge release check` runs the
|
|
12
|
+
// release-readiness gate. The two share the top-level verb, so this command
|
|
13
|
+
// dispatches `check` to the gate and routes everything else through the shared
|
|
14
|
+
// issue dispatch (resolve backend → runIssueOperation('release') → normalize). The
|
|
15
|
+
// Beads backend has no release op and returns the Kernel-only contract error.
|
|
16
|
+
const usage = 'Usage: forge release <id> | forge release check --target 0.1.0 [--json]';
|
|
17
|
+
|
|
18
|
+
async function runReleaseIssue(args, projectRoot, opts = {}) {
|
|
19
|
+
const resolved = withResolvedIssueBackend(projectRoot, opts);
|
|
20
|
+
const runIssueOperation = resolved.runIssueOperation || defaultRunIssueOperation;
|
|
21
|
+
const normalizedArgs = normalizeArgs(args);
|
|
22
|
+
const result = await runIssueOperation('release', normalizedArgs, projectRoot,
|
|
23
|
+
{ ...resolved, kernelBroker: resolved.kernelBroker });
|
|
24
|
+
return normalizeIssueResult(result, 'release', { json: normalizedArgs.includes('--json') });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readOption(args, name, fallback) {
|
|
28
|
+
const equals = args.find(arg => arg.startsWith(`${name}=`));
|
|
29
|
+
if (equals) {
|
|
30
|
+
return equals.slice(name.length + 1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const index = args.indexOf(name);
|
|
34
|
+
if (index >= 0 && args[index + 1] && !args[index + 1].startsWith('-')) {
|
|
35
|
+
return args[index + 1];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return fallback;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseReleaseArgs(args = []) {
|
|
42
|
+
const subcommand = args.find((arg, index) => {
|
|
43
|
+
if (arg.startsWith('-')) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const previous = args[index - 1];
|
|
47
|
+
return previous !== '--target';
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
subcommand,
|
|
52
|
+
target: readOption(args, '--target', SUPPORTED_TARGET),
|
|
53
|
+
json: args.includes('--json'),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function handler(args, _flags, projectRoot, opts = {}) {
|
|
58
|
+
const parsed = parseReleaseArgs(args);
|
|
59
|
+
|
|
60
|
+
if (parsed.subcommand !== 'check') {
|
|
61
|
+
// forge release <id> — release a claimed issue via the shared issue dispatch.
|
|
62
|
+
return runReleaseIssue(args, projectRoot, opts);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const report = buildReadinessReport(projectRoot, { target: parsed.target });
|
|
66
|
+
const output = parsed.json
|
|
67
|
+
? `${JSON.stringify(report, null, 2)}\n`
|
|
68
|
+
: renderReadinessReport(report);
|
|
69
|
+
const error = parsed.json
|
|
70
|
+
? `Forge release readiness check failed for ${report.target}`
|
|
71
|
+
: output;
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
success: report.success,
|
|
75
|
+
report,
|
|
76
|
+
output,
|
|
77
|
+
error: report.success ? undefined : error,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
name: 'release',
|
|
83
|
+
description: 'Release a claimed issue, or run Forge release readiness gates (forge release check)',
|
|
84
|
+
usage,
|
|
85
|
+
flags: {
|
|
86
|
+
'--target <version>': 'Release target to check',
|
|
87
|
+
'--json': 'Emit the readiness report as JSON',
|
|
88
|
+
},
|
|
89
|
+
handler,
|
|
90
|
+
parseReleaseArgs,
|
|
91
|
+
};
|