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,270 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared-machine hardening for `forge serve` — three cheap "ALWAYS" controls.
|
|
5
|
+
*
|
|
6
|
+
* `forge serve` binds loopback only and token-gates every read/write, but on a
|
|
7
|
+
* multi-user box the process still lives in a shared filesystem. These controls
|
|
8
|
+
* raise the floor without adding accounts, a daemon, or cloud:
|
|
9
|
+
*
|
|
10
|
+
* 1. serve.lock — a single-instance guard so a second `forge serve` for the
|
|
11
|
+
* SAME project can't silently start and port-squat. Stale locks (dead PID)
|
|
12
|
+
* are reclaimed; a live holder blocks. Released cleanly on exit.
|
|
13
|
+
* 2. securePath() — create the lock/journal and their directory owner-only
|
|
14
|
+
* (files 0o600, dirs 0o700) AT CREATION, plus a startup audit that warns
|
|
15
|
+
* loudly if a sensitive path is group/other-readable.
|
|
16
|
+
* 3. A hash-chained mutation journal — each appended record carries
|
|
17
|
+
* sha256(prevHash ‖ record), so an attacker cannot silently edit or delete
|
|
18
|
+
* a past entry without breaking the chain. verifyJournal() proves it.
|
|
19
|
+
*
|
|
20
|
+
* Windows honesty: chmod on Windows only toggles the read-only bit — it does NOT
|
|
21
|
+
* change NTFS ACLs — and `fs.stat().mode` does not reflect ACLs. So on Windows
|
|
22
|
+
* securePath() is a best-effort no-op (reported as `applied:false`) and the
|
|
23
|
+
* audit refuses to raise false alarms from meaningless mode bits (it flags the
|
|
24
|
+
* platform caveat instead). The lock + hash-chain logic is platform-independent.
|
|
25
|
+
*
|
|
26
|
+
* State lives under `.forge/serve/` (owner-only) so we never re-chmod the shared
|
|
27
|
+
* `.forge/` config directory out from under other tooling.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const fs = require('node:fs');
|
|
31
|
+
const path = require('node:path');
|
|
32
|
+
const crypto = require('node:crypto');
|
|
33
|
+
|
|
34
|
+
const SECRET_FILE_MODE = 0o600;
|
|
35
|
+
const SECRET_DIR_MODE = 0o700;
|
|
36
|
+
const GENESIS_HASH = '0'.repeat(64);
|
|
37
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Paths
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
function serveStateDir(projectRoot) {
|
|
44
|
+
return path.join(projectRoot, '.forge', 'serve');
|
|
45
|
+
}
|
|
46
|
+
function lockPath(projectRoot) {
|
|
47
|
+
return path.join(serveStateDir(projectRoot), 'serve.lock');
|
|
48
|
+
}
|
|
49
|
+
function journalPath(projectRoot) {
|
|
50
|
+
return path.join(serveStateDir(projectRoot), 'journal.jsonl');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// 2. Perms — securePath / writeSecret / ensureSecureDir + startup audit
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
// Tighten an existing path to owner-only. POSIX: real chmod. Windows: chmod only
|
|
58
|
+
// flips the read-only bit (ACLs untouched), so we report applied:false — the
|
|
59
|
+
// caller and the security doc treat Windows perms as best-effort.
|
|
60
|
+
function securePath(target, { dir = false } = {}) {
|
|
61
|
+
const mode = dir ? SECRET_DIR_MODE : SECRET_FILE_MODE;
|
|
62
|
+
try {
|
|
63
|
+
fs.chmodSync(target, mode);
|
|
64
|
+
return { ok: true, applied: !IS_WINDOWS, mode };
|
|
65
|
+
} catch (err) {
|
|
66
|
+
return { ok: false, applied: false, error: err.message };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Create/overwrite a file with owner-only perms AT CREATION. The `mode` option
|
|
71
|
+
// governs the perms for a NEW file; an existing file keeps its perms on write,
|
|
72
|
+
// so we chmod afterwards to be certain it is tight either way.
|
|
73
|
+
function writeSecret(target, data) {
|
|
74
|
+
ensureSecureDir(path.dirname(target));
|
|
75
|
+
fs.writeFileSync(target, data, { mode: SECRET_FILE_MODE });
|
|
76
|
+
securePath(target);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Create a directory owner-only (recursive). Tighten it afterwards in case it
|
|
80
|
+
// already existed with looser perms that we own.
|
|
81
|
+
function ensureSecureDir(dir) {
|
|
82
|
+
fs.mkdirSync(dir, { recursive: true, mode: SECRET_DIR_MODE });
|
|
83
|
+
securePath(dir, { dir: true });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Inspect one path: does it exist, and is it group/other-readable? On Windows
|
|
87
|
+
// the mode bits are meaningless for ACLs, so we never mark it world-readable —
|
|
88
|
+
// we surface `platformCaveat:true` so the caller can print an honest note.
|
|
89
|
+
function auditPath(target) {
|
|
90
|
+
let st;
|
|
91
|
+
try {
|
|
92
|
+
st = fs.statSync(target);
|
|
93
|
+
} catch {
|
|
94
|
+
return { path: target, exists: false, worldReadable: false, platformCaveat: IS_WINDOWS };
|
|
95
|
+
}
|
|
96
|
+
const mode = st.mode & 0o777;
|
|
97
|
+
const worldReadable = !IS_WINDOWS && (mode & 0o077) !== 0;
|
|
98
|
+
return { path: target, exists: true, mode, worldReadable, platformCaveat: IS_WINDOWS };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Audit a set of sensitive paths. `ok:false` when any existing path is
|
|
102
|
+
// group/other-readable (POSIX). Callers warn loudly on the offenders.
|
|
103
|
+
function auditPaths(targets) {
|
|
104
|
+
const results = targets.map(auditPath);
|
|
105
|
+
const offenders = results.filter((r) => r.exists && r.worldReadable);
|
|
106
|
+
return { ok: offenders.length === 0, offenders, results };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// 1. serve.lock — single-instance guard
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
function readLock(file) {
|
|
114
|
+
try {
|
|
115
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Is `pid` a live process? `process.kill(pid, 0)` sends no signal but throws
|
|
122
|
+
// ESRCH when the pid is gone. EPERM means it exists but isn't ours — still live.
|
|
123
|
+
function pidAlive(pid) {
|
|
124
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
125
|
+
try {
|
|
126
|
+
process.kill(pid, 0);
|
|
127
|
+
return true;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
return err.code === 'EPERM';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Try to claim the single-instance lock for this project.
|
|
134
|
+
// -> { ok:true, file } first claim
|
|
135
|
+
// -> { ok:true, file, reclaimed } stale lock (dead holder) reclaimed
|
|
136
|
+
// -> { ok:false, held } a LIVE holder already owns it (blocked)
|
|
137
|
+
// `pid` / `isAlive` are injectable for testing; production uses the real pid and
|
|
138
|
+
// a real liveness probe.
|
|
139
|
+
function acquireLock(projectRoot, { pid = process.pid, port = null, isAlive = pidAlive } = {}) {
|
|
140
|
+
const file = lockPath(projectRoot);
|
|
141
|
+
ensureSecureDir(path.dirname(file));
|
|
142
|
+
const payload = JSON.stringify({ pid, port, startedAt: new Date().toISOString() });
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// Exclusive create: EEXIST if a lock is already present.
|
|
146
|
+
const fd = fs.openSync(file, 'wx', SECRET_FILE_MODE);
|
|
147
|
+
try {
|
|
148
|
+
fs.writeSync(fd, payload);
|
|
149
|
+
} finally {
|
|
150
|
+
fs.closeSync(fd);
|
|
151
|
+
}
|
|
152
|
+
securePath(file);
|
|
153
|
+
return { ok: true, file };
|
|
154
|
+
} catch (err) {
|
|
155
|
+
if (err.code !== 'EEXIST') throw err;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// A lock exists. If a DIFFERENT process still holds it and is alive, block.
|
|
159
|
+
const held = readLock(file);
|
|
160
|
+
if (held && held.pid !== pid && isAlive(held.pid)) {
|
|
161
|
+
return { ok: false, held };
|
|
162
|
+
}
|
|
163
|
+
// Otherwise it is stale (dead holder, unreadable, or already ours) — reclaim.
|
|
164
|
+
// NOTE: reclaim is best-effort and not itself atomic across a simultaneous
|
|
165
|
+
// double-reclaim race; acceptable for a cheap loopback dev-server guard.
|
|
166
|
+
fs.writeFileSync(file, payload, { mode: SECRET_FILE_MODE });
|
|
167
|
+
securePath(file);
|
|
168
|
+
return { ok: true, file, reclaimed: true };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Release ONLY if the lock is ours (matching pid), so a reclaimed/foreign lock
|
|
172
|
+
// is never deleted out from under its owner.
|
|
173
|
+
function releaseLock(projectRoot, { pid = process.pid } = {}) {
|
|
174
|
+
const file = lockPath(projectRoot);
|
|
175
|
+
try {
|
|
176
|
+
const held = readLock(file);
|
|
177
|
+
if (held && held.pid === pid) fs.rmSync(file, { force: true });
|
|
178
|
+
} catch {
|
|
179
|
+
/* best effort — a failed release just leaves a stale lock to be reclaimed */
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// 3. Hash-chained tamper-evident journal
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
// hash = sha256(prevHash ‖ JSON(record-without-hash)). The prevHash is embedded
|
|
188
|
+
// in the record too, so both a payload edit and a record deletion break verify.
|
|
189
|
+
function recordHash(prevHash, record) {
|
|
190
|
+
return crypto.createHash('sha256').update(`${prevHash}\n${JSON.stringify(record)}`).digest('hex');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function readLines(file) {
|
|
194
|
+
try {
|
|
195
|
+
return fs.readFileSync(file, 'utf8').split('\n').filter(Boolean);
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function lastHash(lines) {
|
|
202
|
+
if (!lines || !lines.length) return GENESIS_HASH;
|
|
203
|
+
try {
|
|
204
|
+
return JSON.parse(lines[lines.length - 1]).hash || GENESIS_HASH;
|
|
205
|
+
} catch {
|
|
206
|
+
return GENESIS_HASH;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Append a tamper-evident record. `entry` is arbitrary JSON (e.g. { verb, ok,
|
|
211
|
+
// actor, origin }). Returns { seq, hash }. Best-effort perms on first create.
|
|
212
|
+
function appendJournal(projectRoot, entry) {
|
|
213
|
+
const file = journalPath(projectRoot);
|
|
214
|
+
ensureSecureDir(path.dirname(file));
|
|
215
|
+
const existed = fs.existsSync(file);
|
|
216
|
+
const lines = existed ? readLines(file) : [];
|
|
217
|
+
const prevHash = lastHash(lines);
|
|
218
|
+
const seq = lines ? lines.length : 0;
|
|
219
|
+
// Key order is fixed (seq, ts, prevHash, ...entry) so the stored record,
|
|
220
|
+
// minus its hash, re-serializes identically at verify time.
|
|
221
|
+
const record = { seq, ts: new Date().toISOString(), prevHash, ...entry };
|
|
222
|
+
const hash = recordHash(prevHash, record);
|
|
223
|
+
fs.appendFileSync(file, `${JSON.stringify({ ...record, hash })}\n`, { mode: SECRET_FILE_MODE });
|
|
224
|
+
if (!existed) securePath(file);
|
|
225
|
+
return { seq, hash };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Re-walk the chain from genesis. Returns { ok:true, entries } or
|
|
229
|
+
// { ok:false, brokenAt, reason } at the first record that fails to verify.
|
|
230
|
+
function verifyJournal(projectRoot) {
|
|
231
|
+
const file = journalPath(projectRoot);
|
|
232
|
+
const lines = readLines(file);
|
|
233
|
+
if (lines === null) return { ok: true, entries: 0, empty: true };
|
|
234
|
+
let prevHash = GENESIS_HASH;
|
|
235
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
236
|
+
let parsed;
|
|
237
|
+
try {
|
|
238
|
+
parsed = JSON.parse(lines[i]);
|
|
239
|
+
} catch {
|
|
240
|
+
return { ok: false, brokenAt: i, reason: 'unparseable record' };
|
|
241
|
+
}
|
|
242
|
+
const { hash, ...rest } = parsed;
|
|
243
|
+
if (rest.prevHash !== prevHash) {
|
|
244
|
+
return { ok: false, brokenAt: i, reason: 'prevHash mismatch (record inserted or deleted)' };
|
|
245
|
+
}
|
|
246
|
+
if (recordHash(prevHash, rest) !== hash) {
|
|
247
|
+
return { ok: false, brokenAt: i, reason: 'hash mismatch (record edited)' };
|
|
248
|
+
}
|
|
249
|
+
prevHash = hash;
|
|
250
|
+
}
|
|
251
|
+
return { ok: true, entries: lines.length, head: prevHash };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = {
|
|
255
|
+
serveStateDir,
|
|
256
|
+
lockPath,
|
|
257
|
+
journalPath,
|
|
258
|
+
securePath,
|
|
259
|
+
writeSecret,
|
|
260
|
+
ensureSecureDir,
|
|
261
|
+
auditPath,
|
|
262
|
+
auditPaths,
|
|
263
|
+
acquireLock,
|
|
264
|
+
releaseLock,
|
|
265
|
+
readLock,
|
|
266
|
+
pidAlive,
|
|
267
|
+
appendJournal,
|
|
268
|
+
verifyJournal,
|
|
269
|
+
GENESIS_HASH,
|
|
270
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { handleAdapterCommand } = require('../adapter-cli');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: 'adapter',
|
|
7
|
+
description: 'Manage and test review adapters',
|
|
8
|
+
usage: 'forge adapter <test|list|enable|disable> ...',
|
|
9
|
+
async handler(args, _flags, projectRoot) {
|
|
10
|
+
return handleAdapterCommand(args, projectRoot);
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { addLockEntry } = require('../forge-lock');
|
|
5
|
+
|
|
6
|
+
function hasArg(args, name) {
|
|
7
|
+
return Array.isArray(args) && args.includes(name);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function argValue(args, name) {
|
|
11
|
+
const index = args.indexOf(name);
|
|
12
|
+
if (index >= 0 && index + 1 < args.length && !args[index + 1].startsWith('-')) {
|
|
13
|
+
return args[index + 1];
|
|
14
|
+
}
|
|
15
|
+
const prefix = `${name}=`;
|
|
16
|
+
const match = args.find(arg => arg.startsWith(prefix));
|
|
17
|
+
return match ? match.slice(prefix.length) : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function positionalArgs(args) {
|
|
21
|
+
const positionals = [];
|
|
22
|
+
const flagsWithValues = new Set(['--name', '--path', '-p']);
|
|
23
|
+
for (let index = 0; index < args.length; index++) {
|
|
24
|
+
const arg = args[index];
|
|
25
|
+
if (arg.startsWith('--') && arg.includes('=')) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (arg.startsWith('-')) {
|
|
29
|
+
if (flagsWithValues.has(arg) && index + 1 < args.length && !args[index + 1].startsWith('-')) {
|
|
30
|
+
index++;
|
|
31
|
+
}
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
positionals.push(arg);
|
|
35
|
+
}
|
|
36
|
+
return positionals;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function stripKnownExtension(base) {
|
|
40
|
+
const lowerBase = base.toLowerCase();
|
|
41
|
+
for (const suffix of ['.plugin.json', '.tar.gz', '.json', '.tgz']) {
|
|
42
|
+
if (lowerBase.endsWith(suffix)) {
|
|
43
|
+
return base.slice(0, base.length - suffix.length);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return base;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeNameSegment(value) {
|
|
50
|
+
let normalized = '';
|
|
51
|
+
let previousWasSeparator = true;
|
|
52
|
+
for (const character of value) {
|
|
53
|
+
const isAllowed = (
|
|
54
|
+
(character >= 'A' && character <= 'Z')
|
|
55
|
+
|| (character >= 'a' && character <= 'z')
|
|
56
|
+
|| (character >= '0' && character <= '9')
|
|
57
|
+
|| character === '.'
|
|
58
|
+
|| character === '_'
|
|
59
|
+
|| character === '-'
|
|
60
|
+
);
|
|
61
|
+
if (isAllowed) {
|
|
62
|
+
normalized += character;
|
|
63
|
+
previousWasSeparator = false;
|
|
64
|
+
} else if (!previousWasSeparator) {
|
|
65
|
+
normalized += '-';
|
|
66
|
+
previousWasSeparator = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return normalized.endsWith('-') ? normalized.slice(0, -1) : normalized;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function deriveName(source) {
|
|
73
|
+
const sourceText = String(source || '');
|
|
74
|
+
const base = path.basename(sourceText.endsWith('/') ? sourceText.slice(0, -1) : sourceText);
|
|
75
|
+
return normalizeNameSegment(stripKnownExtension(base)) || 'extension';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function usage() {
|
|
79
|
+
return 'Usage: forge add <source> [--name <id>] [--allow-untrusted]';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function handler(args, _flags, projectRoot) {
|
|
83
|
+
const [source] = positionalArgs(args);
|
|
84
|
+
if (!source) {
|
|
85
|
+
return { success: false, error: usage() };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const result = addLockEntry(projectRoot, {
|
|
90
|
+
name: argValue(args, '--name') || deriveName(source),
|
|
91
|
+
source,
|
|
92
|
+
allowUntrusted: hasArg(args, '--allow-untrusted'),
|
|
93
|
+
});
|
|
94
|
+
const trustNote = result.entry.trust.trusted
|
|
95
|
+
? 'trusted source verified'
|
|
96
|
+
: 'untrusted source accepted by explicit policy';
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
output: `Added ${result.entry.name} to forge.lock (${trustNote}).`,
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
const message = error.message.includes('Untrusted source')
|
|
103
|
+
? `${error.message}\nUse --allow-untrusted only for sources you intentionally trust for this project.`
|
|
104
|
+
: error.message;
|
|
105
|
+
return { success: false, error: message };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = {
|
|
110
|
+
name: 'add',
|
|
111
|
+
description: 'Record an extension source in forge.lock',
|
|
112
|
+
usage: usage(),
|
|
113
|
+
flags: {
|
|
114
|
+
'--name': 'Lockfile extension id to record',
|
|
115
|
+
'--allow-untrusted': 'Explicitly record an untrusted remote/package source',
|
|
116
|
+
},
|
|
117
|
+
handler,
|
|
118
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { verifyForgeLock } = require('../forge-lock');
|
|
4
|
+
|
|
5
|
+
const usage = 'Usage: forge audit verify';
|
|
6
|
+
|
|
7
|
+
function positionalArgs(args) {
|
|
8
|
+
const positionals = [];
|
|
9
|
+
const flagsWithValues = new Set(['--path', '-p']);
|
|
10
|
+
for (let index = 0; index < args.length; index++) {
|
|
11
|
+
const arg = args[index];
|
|
12
|
+
if (arg.startsWith('--') && arg.includes('=')) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (arg.startsWith('-')) {
|
|
16
|
+
if (flagsWithValues.has(arg) && index + 1 < args.length && !args[index + 1].startsWith('-')) {
|
|
17
|
+
index++;
|
|
18
|
+
}
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
positionals.push(arg);
|
|
22
|
+
}
|
|
23
|
+
return positionals;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderVerifyReport(report) {
|
|
27
|
+
const lines = [
|
|
28
|
+
'Forge lock audit verify',
|
|
29
|
+
`Result: ${report.ok ? 'PASS' : 'FAIL'}`,
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
if (report.results.length === 0) {
|
|
33
|
+
lines.push('[PASS] forge.lock: no extensions recorded');
|
|
34
|
+
} else {
|
|
35
|
+
for (const result of report.results) {
|
|
36
|
+
lines.push(`[${result.status.toUpperCase()}] ${result.name}: ${result.reason}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return `${lines.join('\n')}\n`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function handler(args, _flags, projectRoot) {
|
|
44
|
+
const [subcommand] = positionalArgs(args);
|
|
45
|
+
if (subcommand !== 'verify') {
|
|
46
|
+
return { success: false, error: usage };
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const report = verifyForgeLock(projectRoot);
|
|
50
|
+
const output = renderVerifyReport(report);
|
|
51
|
+
return {
|
|
52
|
+
success: report.ok,
|
|
53
|
+
output,
|
|
54
|
+
error: report.ok ? undefined : output,
|
|
55
|
+
};
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
error: error.message || String(error),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
name: 'audit',
|
|
66
|
+
description: 'Verify Forge lockfile and audit state',
|
|
67
|
+
usage,
|
|
68
|
+
handler,
|
|
69
|
+
renderVerifyReport,
|
|
70
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { readBeadsSnapshot } = require('../status/beads-snapshot');
|
|
4
|
+
const { buildBoardJson, formatBoard } = require('../status/presenter');
|
|
5
|
+
const { detectRepoContext } = require('./status');
|
|
6
|
+
|
|
7
|
+
function hasJsonFlag(args = [], flags = {}) {
|
|
8
|
+
return flags.json === true || flags['--json'] === true || args.includes('--json');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getFlagValue(args, dashedName, camelName) {
|
|
12
|
+
const inlineDashed = args.find(arg => typeof arg === 'string' && arg.startsWith(`${dashedName}=`));
|
|
13
|
+
if (inlineDashed) {
|
|
14
|
+
return inlineDashed.slice(dashedName.length + 1);
|
|
15
|
+
}
|
|
16
|
+
const inlineCamel = args.find(arg => typeof arg === 'string' && arg.startsWith(`${camelName}=`));
|
|
17
|
+
if (inlineCamel) {
|
|
18
|
+
return inlineCamel.slice(camelName.length + 1);
|
|
19
|
+
}
|
|
20
|
+
const dashedIndex = args.indexOf(dashedName);
|
|
21
|
+
if (dashedIndex !== -1 && dashedIndex + 1 < args.length) {
|
|
22
|
+
const candidate = args[dashedIndex + 1];
|
|
23
|
+
return typeof candidate === 'string' && candidate.startsWith('--') ? null : candidate;
|
|
24
|
+
}
|
|
25
|
+
const camelIndex = args.indexOf(camelName);
|
|
26
|
+
if (camelIndex !== -1 && camelIndex + 1 < args.length) {
|
|
27
|
+
const candidate = args[camelIndex + 1];
|
|
28
|
+
return typeof candidate === 'string' && candidate.startsWith('--') ? null : candidate;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function handler(args = [], flags = {}, projectRoot = process.cwd()) {
|
|
34
|
+
const firstDefined = (...values) => values.find(value => value !== undefined && value !== null);
|
|
35
|
+
const context = detectRepoContext(projectRoot);
|
|
36
|
+
const snapshot = readBeadsSnapshot(projectRoot, {
|
|
37
|
+
now: firstDefined(flags.now, flags['--now'], getFlagValue(args, '--now', '--now')),
|
|
38
|
+
staleAfterDays: firstDefined(
|
|
39
|
+
flags.staleAfterDays,
|
|
40
|
+
flags['--stale-after-days'],
|
|
41
|
+
flags['--staleAfterDays'],
|
|
42
|
+
getFlagValue(args, '--stale-after-days', '--staleAfterDays')
|
|
43
|
+
),
|
|
44
|
+
});
|
|
45
|
+
const payload = { context, snapshot };
|
|
46
|
+
const output = hasJsonFlag(args, flags)
|
|
47
|
+
? JSON.stringify(buildBoardJson(payload), null, 2)
|
|
48
|
+
: formatBoard(payload);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
success: true,
|
|
52
|
+
context,
|
|
53
|
+
snapshot,
|
|
54
|
+
output,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
name: 'board',
|
|
60
|
+
description: 'Show a local team runtime board from kernel state',
|
|
61
|
+
handler,
|
|
62
|
+
getFlagValue,
|
|
63
|
+
hasJsonFlag,
|
|
64
|
+
};
|
package/lib/commands/claim.js
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { runIssueSubcommand } = require('./_issue');
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// `forge claim <id>` claims an issue through the SHARED issue dispatch
|
|
6
|
+
// (runIssueSubcommand): backend resolution (Kernel via --kernel /
|
|
7
|
+
// --issue-backend kernel / FORGE_ISSUE_BACKEND=kernel, Beads otherwise; the
|
|
8
|
+
// Beads backend translates claim to `update <id> --claim`), contract
|
|
9
|
+
// normalization, AND the check-after-write verification loop
|
|
10
|
+
// (gate.issue_verify). This command previously inlined its own copy of the
|
|
11
|
+
// dispatch, which silently BYPASSED the boundary verify — the exact surface
|
|
12
|
+
// where the d71a824b phantom-claim replay lied to a losing agent — so it now
|
|
13
|
+
// delegates like create/update/close/comment do.
|
|
14
|
+
async function handler(args, _flags, projectRoot, opts = {}) {
|
|
15
|
+
return runIssueSubcommand('claim', args, projectRoot, opts);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
name: 'claim',
|
|
20
|
+
description: 'Claim an issue via Forge',
|
|
21
|
+
usage: 'forge claim <id> [flags]',
|
|
22
|
+
flags: {},
|
|
23
|
+
handler,
|
|
24
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { createIssueSubcommand } = require('./_issue');
|
|
4
|
+
|
|
5
|
+
// `forge claims` — the active-lease/claims read (issue 7dc229d4). A bare passthrough
|
|
6
|
+
// to `forge issue claims`, mirroring the other kernel derived reads (blocked/stale).
|
|
7
|
+
module.exports = createIssueSubcommand('claims');
|