forge-workflow 0.0.10 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
- package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
- package/.cursor/rules/permissions-guidance.mdc +2 -2
- package/.forge/hooks/check-tdd.js +82 -5
- package/.forge/hooks/forge-native-hook.js +431 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +151 -61
- package/CHANGELOG.md +709 -0
- package/CLAUDE.md +9 -118
- package/QUICKSTART.md +175 -0
- package/README.md +275 -365
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +532 -489
- package/docs/INDEX.md +93 -0
- package/docs/PROJECT_DESIGN.md +685 -0
- package/docs/architecture/index.md +66 -0
- package/docs/architecture/notes/README.md +35 -0
- package/docs/architecture/subsystems/README.md +46 -0
- package/docs/forge/TOOLCHAIN.md +670 -0
- package/docs/forge/VALIDATION.md +82 -0
- package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
- package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
- package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
- package/docs/guides/GREPTILE_SETUP.md +46 -0
- package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
- package/docs/guides/MIGRATION.md +56 -0
- package/docs/guides/SETUP.md +121 -0
- package/docs/guides/SUPPORT.md +190 -0
- package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
- package/docs/guides/memory-backends.md +183 -0
- package/docs/reference/ADAPTERS.md +128 -0
- package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
- package/docs/reference/COMMANDS.md +214 -0
- package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
- package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
- package/docs/reference/HERMES_INTEGRATION.md +118 -0
- package/docs/reference/INSIGHTS_RECAP.md +63 -0
- package/docs/reference/INSTALL.md +164 -0
- package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
- package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
- package/docs/reference/RELEASE.md +68 -0
- package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
- package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
- package/docs/reference/SKILLS.md +35 -0
- package/docs/reference/STATUS_BOARD.md +80 -0
- package/docs/reference/TEMPLATES.md +106 -0
- package/docs/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
- package/docs/reference/VALIDATION.md +82 -0
- package/docs/reference/agent-permissions.md +169 -0
- package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
- package/docs/reference/control-plane-guarantees.md +125 -0
- package/docs/reference/dependency-chain.md +331 -0
- package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
- package/docs/reference/forge-kernel-schema.md +72 -0
- package/docs/reference/kernel-conflict-evaluators.md +27 -0
- package/docs/reference/patch-md-format.md +77 -0
- package/docs/reference/protected-state-surfaces.md +59 -0
- package/docs/reference/shepherd.md +155 -0
- package/docs/reference/superpowers-analysis.md +320 -0
- package/docs/reference/superpowers-integration-options.md +404 -0
- package/docs/reference/test-environment.md +519 -0
- package/docs/reference/upgrade-safety.md +59 -0
- package/lefthook.yml +18 -0
- package/lib/activation/ensure-forge-home.js +135 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1109 -0
- package/lib/adapters/greptile-review-adapter.js +141 -0
- package/lib/adapters/kernel-issue-adapter.js +101 -0
- package/lib/adapters/pr-state-adapter.js +484 -0
- package/lib/adoption-profiles.js +139 -0
- package/lib/agents/README.md +2 -6
- package/lib/agents/claude.plugin.json +3 -8
- package/lib/agents/codex.plugin.json +9 -1
- package/lib/agents/cursor.plugin.json +2 -6
- package/lib/agents/hermes.plugin.json +22 -0
- package/lib/agents-config.js +39 -1236
- package/lib/audit-evidence.js +282 -0
- package/lib/beads-detect.js +60 -0
- package/lib/beads-nudge.js +91 -0
- package/lib/beads-setup.js +121 -0
- package/lib/beads-sync-scaffold.js +25 -101
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_aliases.js +248 -0
- package/lib/commands/_issue.js +780 -77
- package/lib/commands/_manifest.js +93 -0
- package/lib/commands/_registry.js +99 -34
- package/lib/commands/_resolve-command-opts.js +230 -0
- package/lib/commands/_serve-security.js +270 -0
- package/lib/commands/adapter.js +12 -0
- package/lib/commands/add.js +118 -0
- package/lib/commands/audit.js +70 -0
- package/lib/commands/blocked.js +5 -0
- package/lib/commands/board.js +64 -0
- package/lib/commands/claim.js +21 -2
- package/lib/commands/claims.js +7 -0
- package/lib/commands/clean.js +485 -75
- package/lib/commands/close.js +2 -2
- package/lib/commands/comment.js +5 -0
- package/lib/commands/control.js +148 -0
- package/lib/commands/create.js +2 -2
- package/lib/commands/dev.js +185 -7
- package/lib/commands/doc-gate.js +336 -0
- package/lib/commands/doctor.js +156 -0
- package/lib/commands/explain.js +15 -0
- package/lib/commands/export.js +237 -0
- package/lib/commands/gate.js +209 -0
- package/lib/commands/hooks.js +377 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +604 -0
- package/lib/commands/insights.js +79 -0
- package/lib/commands/issue.js +12 -1
- package/lib/commands/issues.js +17 -0
- package/lib/commands/lint.js +5 -0
- package/lib/commands/list.js +2 -2
- package/lib/commands/memory.js +81 -0
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +362 -0
- package/lib/commands/new.js +12 -0
- package/lib/commands/options.js +241 -0
- package/lib/commands/orient.js +13 -0
- package/lib/commands/orphans.js +5 -0
- package/lib/commands/patch.js +67 -0
- package/lib/commands/plan.js +481 -29
- package/lib/commands/pr.js +88 -0
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +135 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +171 -0
- package/lib/commands/recap.js +75 -0
- package/lib/commands/recommend.js +0 -1
- package/lib/commands/release.js +104 -0
- package/lib/commands/remember.js +140 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +900 -971
- package/lib/commands/shepherd.js +501 -0
- package/lib/commands/ship.js +59 -1
- package/lib/commands/show.js +2 -2
- package/lib/commands/stage.js +192 -0
- package/lib/commands/stale.js +5 -0
- package/lib/commands/status.js +158 -21
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +4 -1
- package/lib/commands/test.js +43 -27
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +43 -18
- package/lib/commands/worktree.js +362 -99
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +977 -0
- package/lib/dep-guard/keyword-ripple.js +2 -2
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +35 -9
- package/lib/doc-gate/declaration.js +177 -0
- package/lib/doc-gate/detect.js +289 -0
- package/lib/doc-gate/gate.js +375 -0
- package/lib/doc-gate/okf-config.js +128 -0
- package/lib/doc-gate/okf.js +429 -0
- package/lib/docs-command.js +1161 -6
- package/lib/forge-issues.js +382 -11
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +192 -0
- package/lib/global-flags.js +104 -0
- package/lib/greptile-match.js +7 -63
- package/lib/grounding/context-events.js +230 -0
- package/lib/grounding/read-first.js +112 -0
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +541 -0
- package/lib/inbox.js +391 -0
- package/lib/insights.js +397 -0
- package/lib/issue-adapter.js +156 -0
- package/lib/issue-backend.js +145 -0
- package/lib/issue-render.js +220 -0
- package/lib/kernel/backing-issue.js +311 -0
- package/lib/kernel/broker.js +1218 -0
- package/lib/kernel/cli-broker-factory.js +130 -0
- package/lib/kernel/conflict-signal.js +82 -0
- package/lib/kernel/evaluators.js +195 -0
- package/lib/kernel/fs-class.js +495 -0
- package/lib/kernel/issue-command-contract.js +559 -0
- package/lib/kernel/issue-id-resolver.js +186 -0
- package/lib/kernel/lease-enforcer.js +158 -0
- package/lib/kernel/migrations.js +333 -0
- package/lib/kernel/owned-kernel.js +43 -0
- package/lib/kernel/planning-buckets-schema.js +109 -0
- package/lib/kernel/projection-jsonl-writer.js +450 -0
- package/lib/kernel/readiness-model.js +329 -0
- package/lib/kernel/schema.js +356 -0
- package/lib/kernel/sqlite-driver.js +2540 -0
- package/lib/kernel/taxonomy-validator.js +394 -0
- package/lib/lefthook-check.js +3 -2
- package/lib/lefthook-wiring.js +413 -0
- package/lib/mcp-config-renderer.js +288 -0
- package/lib/memory/graphiti-mcp.js +106 -0
- package/lib/memory/router.js +387 -0
- package/lib/memory/typed-api.js +102 -0
- package/lib/memory-digest.js +195 -0
- package/lib/merge-rules.js +395 -0
- package/lib/migrate-dry-run.js +466 -0
- package/lib/orientation.js +863 -0
- package/lib/package-manager-remediation.js +103 -0
- package/lib/package-root.js +381 -0
- package/lib/patch-intent.js +890 -0
- package/lib/plugin-catalog.js +3 -4
- package/lib/plugin-manager.js +0 -5
- package/lib/pr-bundle.js +186 -0
- package/lib/pr-monitor/auto-actions.js +175 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/digest.js +206 -0
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/gather.js +124 -0
- package/lib/pr-monitor/journal.js +299 -0
- package/lib/pr-monitor/monitor.js +146 -0
- package/lib/pr-monitor/render-sticky.js +192 -0
- package/lib/pr-monitor/upsert-sticky.js +169 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1314 -0
- package/lib/pr-shepherd.js +494 -0
- package/lib/pr-state-validator.js +59 -0
- package/lib/preflight/gates.js +237 -0
- package/lib/preflight/runner.js +116 -0
- package/lib/project-discovery.js +0 -53
- package/lib/project-memory.js +99 -497
- package/lib/protected-path-manifest.js +281 -0
- package/lib/protected-state-surfaces.js +387 -0
- package/lib/release-readiness.js +2105 -0
- package/lib/reset.js +59 -45
- package/lib/review-adapter.js +68 -0
- package/lib/rules-sync.js +260 -0
- package/lib/runtime-health.js +241 -20
- package/lib/safety-config-renderer.js +268 -0
- package/lib/setup-action-log.js +1 -7
- package/lib/setup.js +27 -65
- package/lib/shell-utils.js +76 -6
- package/lib/skills-sync.js +330 -0
- package/lib/smart-status/scoring.js +17 -3
- package/lib/status/beads-snapshot.js +45 -2
- package/lib/status/presenter.js +169 -18
- package/lib/status/snapshot.js +186 -0
- package/lib/sync-backend.js +202 -0
- package/lib/untrusted-content.js +52 -0
- package/lib/upgrade-safety.js +251 -0
- package/lib/workflow/enforce-stage.js +351 -45
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +11 -22
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +37 -35
- package/rules/documentation.md +19 -0
- package/rules/kernel-tracking.md +26 -0
- package/rules/security.md +22 -0
- package/rules/tdd.md +20 -0
- package/rules/workflow.md +27 -0
- package/scripts/auto-backing-issue.js +47 -0
- package/scripts/beads-context.sh +81 -57
- package/scripts/beads-upgrade-smoke.sh +24 -3
- package/scripts/bootstrap-windows-tools.sh +78 -0
- package/scripts/branch-protection.js +2 -3
- package/scripts/check-agents.js +34 -137
- package/scripts/commitlint.js +3 -1
- package/scripts/conflict-detect.sh +3 -0
- package/scripts/dep-guard.sh +22 -3
- package/scripts/file-index.sh +3 -0
- package/scripts/forge-team/lib/claim.sh +34 -18
- package/scripts/forge-team/lib/dashboard.sh +61 -86
- package/scripts/forge-team/lib/epic.sh +99 -263
- package/scripts/forge-team/lib/hooks.sh +26 -28
- package/scripts/forge-team/lib/identity.sh +4 -4
- package/scripts/forge-team/lib/sync-github.sh +49 -84
- package/scripts/forge-team/lib/verify.sh +93 -83
- package/scripts/forge-team/lib/workload.sh +41 -65
- package/scripts/forge-team/tests/claim.test.sh +25 -19
- package/scripts/forge-team/tests/dashboard.test.sh +31 -46
- package/scripts/forge-team/tests/epic.test.sh +52 -71
- package/scripts/forge-team/tests/hooks.test.sh +38 -50
- package/scripts/forge-team/tests/identity.test.sh +3 -3
- package/scripts/forge-team/tests/integration.test.sh +44 -66
- package/scripts/forge-team/tests/sync-github.test.sh +50 -83
- package/scripts/forge-team/tests/verify.test.sh +37 -46
- package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
- package/scripts/forge-team/tests/workload.test.sh +32 -66
- package/scripts/gen-command-manifest.js +153 -0
- package/scripts/gen-embedded-assets.mjs +129 -0
- package/scripts/install.ps1 +139 -0
- package/scripts/install.sh +268 -0
- package/scripts/lib/release-asset.mjs +84 -0
- package/scripts/parity-check.mjs +145 -0
- package/scripts/parity-check.test.mjs +58 -0
- package/scripts/pin-agentic-workflow-images.js +112 -0
- package/scripts/pr-auto-actions.js +93 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/pr-verdict-label.js +50 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +21 -94
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status.sh +60 -57
- package/scripts/spikes/config-race-bench.js +111 -0
- package/scripts/spikes/harness-capability-matrix.js +13 -0
- package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
- package/scripts/spikes/protected-path-manifest.js +20 -0
- package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
- package/scripts/sync-agent-skills.js +62 -0
- package/scripts/sync-utils.sh +3 -0
- package/scripts/test-ci-shard.js +13 -6
- package/scripts/test.js +95 -12
- package/skills/claim-safety/SKILL.md +102 -0
- package/skills/claim-safety/evals/evals.json +46 -0
- package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
- package/skills/dev/evals/evals.json +50 -0
- package/skills/hermes-forge/SKILL.md +185 -0
- package/skills/hermes-forge/evals/evals.json +46 -0
- package/skills/issue-basics/SKILL.md +111 -0
- package/skills/issue-basics/evals/evals.json +46 -0
- package/skills/kernel/SKILL.md +166 -0
- package/skills/kernel/evals/evals.json +50 -0
- package/skills/memory/SKILL.md +102 -0
- package/skills/parallel-deep-research/SKILL.md +14 -11
- package/skills/parallel-deep-research/evals/evals.json +11 -27
- package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
- package/skills/plan/evals/evals.json +42 -0
- package/skills/research/SKILL.md +195 -0
- package/skills/research/evals/evals.json +42 -0
- package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
- package/skills/review/evals/evals.json +42 -0
- package/skills/rollback/SKILL.md +110 -0
- package/skills/rollback/evals/evals.json +46 -0
- package/skills/rollback/references/methods.md +204 -0
- package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
- package/skills/shepherd/SKILL.md +66 -0
- package/skills/shepherd/evals/evals.json +42 -0
- package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
- package/skills/ship/evals/evals.json +42 -0
- package/skills/smith/SKILL.md +142 -0
- package/skills/smith/evals/evals.json +46 -0
- package/skills/smith/references/autonomy-and-gates.md +94 -0
- package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
- package/skills/sonarcloud/evals/evals.json +46 -0
- package/skills/sonarcloud-analysis/SKILL.md +18 -13
- package/skills/sonarcloud-analysis/evals/evals.json +11 -15
- package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
- package/skills/status/evals/evals.json +50 -0
- package/skills/triage-ready/SKILL.md +121 -0
- package/skills/triage-ready/evals/evals.json +42 -0
- package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
- package/skills/validate/evals/evals.json +42 -0
- package/skills/verify/SKILL.md +299 -0
- package/skills/verify/evals/evals.json +50 -0
- package/.claude/commands/dev.md +0 -345
- package/.claude/commands/plan.md +0 -566
- package/.claude/commands/premerge.md +0 -186
- package/.claude/commands/research.md +0 -42
- package/.claude/commands/review.md +0 -451
- package/.claude/commands/rollback.md +0 -721
- package/.claude/commands/ship.md +0 -213
- package/.claude/commands/sonarcloud.md +0 -152
- package/.claude/commands/status.md +0 -90
- package/.claude/commands/validate.md +0 -288
- package/.claude/commands/verify.md +0 -269
- package/.claude/rules/workflow.md +0 -121
- package/.cline/workflows/dev.md +0 -342
- package/.cline/workflows/plan.md +0 -563
- package/.cline/workflows/premerge.md +0 -183
- package/.cline/workflows/research.md +0 -39
- package/.cline/workflows/review.md +0 -448
- package/.cline/workflows/rollback.md +0 -718
- package/.cline/workflows/ship.md +0 -210
- package/.cline/workflows/sonarcloud.md +0 -146
- package/.cline/workflows/status.md +0 -87
- package/.cline/workflows/validate.md +0 -285
- package/.cline/workflows/verify.md +0 -266
- package/.codex/config.toml +0 -11
- package/.codex/skills/dev/SKILL.md +0 -345
- package/.codex/skills/plan/SKILL.md +0 -566
- package/.codex/skills/premerge/SKILL.md +0 -186
- package/.codex/skills/research/SKILL.md +0 -42
- package/.codex/skills/review/SKILL.md +0 -451
- package/.codex/skills/rollback/SKILL.md +0 -721
- package/.codex/skills/ship/SKILL.md +0 -213
- package/.codex/skills/sonarcloud/SKILL.md +0 -149
- package/.codex/skills/status/SKILL.md +0 -90
- package/.codex/skills/validate/SKILL.md +0 -288
- package/.codex/skills/verify/SKILL.md +0 -269
- package/.cursor/commands/dev.md +0 -342
- package/.cursor/commands/plan.md +0 -563
- package/.cursor/commands/premerge.md +0 -183
- package/.cursor/commands/research.md +0 -39
- package/.cursor/commands/review.md +0 -448
- package/.cursor/commands/ship.md +0 -210
- package/.cursor/commands/sonarcloud.md +0 -146
- package/.cursor/commands/status.md +0 -87
- package/.cursor/commands/validate.md +0 -285
- package/.cursor/commands/verify.md +0 -266
- package/.cursorrules +0 -149
- package/.github/prompts/premerge.prompt.md +0 -188
- package/.github/prompts/research.prompt.md +0 -44
- package/.github/prompts/rollback.prompt.md +0 -723
- package/.github/prompts/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -89
- package/.github/workflows/github-to-beads.yml +0 -100
- package/.kilocode/workflows/dev.md +0 -346
- package/.kilocode/workflows/plan.md +0 -567
- package/.kilocode/workflows/premerge.md +0 -187
- package/.kilocode/workflows/research.md +0 -43
- package/.kilocode/workflows/review.md +0 -452
- package/.kilocode/workflows/rollback.md +0 -722
- package/.kilocode/workflows/ship.md +0 -214
- package/.kilocode/workflows/sonarcloud.md +0 -150
- package/.kilocode/workflows/status.md +0 -91
- package/.kilocode/workflows/validate.md +0 -289
- package/.kilocode/workflows/verify.md +0 -270
- package/.opencode/commands/dev.md +0 -345
- package/.opencode/commands/plan.md +0 -566
- package/.opencode/commands/premerge.md +0 -186
- package/.opencode/commands/research.md +0 -42
- package/.opencode/commands/review.md +0 -451
- package/.opencode/commands/rollback.md +0 -721
- package/.opencode/commands/ship.md +0 -213
- package/.opencode/commands/sonarcloud.md +0 -149
- package/.opencode/commands/status.md +0 -90
- package/.opencode/commands/validate.md +0 -288
- package/.opencode/commands/verify.md +0 -269
- package/.roo/commands/dev.md +0 -346
- package/.roo/commands/plan.md +0 -567
- package/.roo/commands/premerge.md +0 -187
- package/.roo/commands/research.md +0 -43
- package/.roo/commands/review.md +0 -452
- package/.roo/commands/rollback.md +0 -722
- package/.roo/commands/ship.md +0 -214
- package/.roo/commands/sonarcloud.md +0 -150
- package/.roo/commands/status.md +0 -91
- package/.roo/commands/validate.md +0 -289
- package/.roo/commands/verify.md +0 -270
- package/docs/BEADS_GITHUB_SYNC.md +0 -281
- package/docs/GREPTILE_SETUP.md +0 -400
- package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
- package/docs/SETUP.md +0 -663
- package/docs/VALIDATION.md +0 -363
- package/lib/agents/cline.plugin.json +0 -29
- package/lib/agents/copilot.plugin.json +0 -24
- package/lib/agents/kilocode.plugin.json +0 -22
- package/lib/agents/opencode.plugin.json +0 -23
- package/lib/agents/roo.plugin.json +0 -30
- package/lib/beads-bootstrap.js +0 -225
- package/lib/beads-health-check.js +0 -188
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -584
- package/scripts/github-beads-sync/comment.mjs +0 -64
- package/scripts/github-beads-sync/config.mjs +0 -148
- package/scripts/github-beads-sync/github-api.mjs +0 -131
- package/scripts/github-beads-sync/index.mjs +0 -356
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -132
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
- package/scripts/github-beads-sync/run-bd.mjs +0 -161
- package/scripts/github-beads-sync/sanitize.mjs +0 -121
- package/scripts/github-beads-sync.config.json +0 -26
- package/scripts/sync-commands.js +0 -600
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { execFileSync } = require('node:child_process');
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
buildMigrationDryRunReport,
|
|
10
|
+
renderMigrationDryRunReport,
|
|
11
|
+
} = require('../migrate-dry-run');
|
|
12
|
+
const {
|
|
13
|
+
loadBeadsSnapshotFromDirectory,
|
|
14
|
+
importBeadsSnapshot,
|
|
15
|
+
} = require('../adapters/beads-kernel-compat');
|
|
16
|
+
const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
|
|
17
|
+
|
|
18
|
+
function hasArg(args, name) {
|
|
19
|
+
return Array.isArray(args) && args.includes(name);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse `forge migrate` options from positional args (with a parsed flags object
|
|
24
|
+
* as a fallback). Supported: --from <source>, --source <dir>, --dry-run, --json.
|
|
25
|
+
* Unknown tokens are ignored so the legacy v2→v3 PoC flags pass through untouched.
|
|
26
|
+
*/
|
|
27
|
+
function parseMigrateOptions(args = [], flags = {}) {
|
|
28
|
+
const options = {
|
|
29
|
+
from: flags.from,
|
|
30
|
+
source: flags.source,
|
|
31
|
+
dryRun: Boolean(flags.dryRun || flags['dry-run']),
|
|
32
|
+
json: Boolean(flags.json),
|
|
33
|
+
errors: [],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const takeValue = (arg, prefix, current, index) => {
|
|
37
|
+
if (arg.startsWith(`${prefix}=`)) {
|
|
38
|
+
const value = arg.slice(prefix.length + 1);
|
|
39
|
+
if (!value) options.errors.push(`${prefix} requires a value`);
|
|
40
|
+
return { value: value || current, next: index };
|
|
41
|
+
}
|
|
42
|
+
const candidate = args[index + 1];
|
|
43
|
+
if (candidate && !candidate.startsWith('--')) {
|
|
44
|
+
return { value: candidate, next: index + 1 };
|
|
45
|
+
}
|
|
46
|
+
options.errors.push(`${prefix} requires a value`);
|
|
47
|
+
return { value: current, next: index };
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
let i = 0;
|
|
51
|
+
while (i < args.length) {
|
|
52
|
+
const arg = args[i];
|
|
53
|
+
if (arg === '--dry-run') options.dryRun = true;
|
|
54
|
+
else if (arg === '--json') options.json = true;
|
|
55
|
+
else if (arg === '--from' || arg.startsWith('--from=')) {
|
|
56
|
+
const parsed = takeValue(arg, '--from', options.from, i);
|
|
57
|
+
options.from = parsed.value;
|
|
58
|
+
i = parsed.next;
|
|
59
|
+
} else if (arg === '--source' || arg.startsWith('--source=')) {
|
|
60
|
+
const parsed = takeValue(arg, '--source', options.source, i);
|
|
61
|
+
options.source = parsed.value;
|
|
62
|
+
i = parsed.next;
|
|
63
|
+
}
|
|
64
|
+
i += 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return options;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function directoryHasJsonl(dir, fsImpl) {
|
|
71
|
+
try {
|
|
72
|
+
return fsImpl.readdirSync(dir).some(entry => entry.endsWith('.jsonl'));
|
|
73
|
+
} catch (_err) {
|
|
74
|
+
/* intentional: unreadable directory has no usable jsonl */ // NOSONAR S2486
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Export a Dolt-only Beads store to JSONL in a temp dir via `bd export --all`,
|
|
81
|
+
* then return that dir for the loader. Defensive: a missing `bd` on PATH yields a
|
|
82
|
+
* clear, actionable error instead of a stack trace.
|
|
83
|
+
*
|
|
84
|
+
* NOTE (flagged ambiguity): the exact on-disk shape `bd export --all` emits is not
|
|
85
|
+
* pinned in this repo. We capture stdout into `<temp>/issues.jsonl`, which the
|
|
86
|
+
* loader reads. Operators with a different layout should prefer `--source <dir>`.
|
|
87
|
+
*/
|
|
88
|
+
function exportDoltStoreToJsonl(beadsDir, projectRoot, deps) {
|
|
89
|
+
const exec = deps.execFileSync || execFileSync;
|
|
90
|
+
const fsImpl = deps.fs || fs;
|
|
91
|
+
const tempDir = fsImpl.mkdtempSync(path.join(os.tmpdir(), 'forge-migrate-bd-'));
|
|
92
|
+
try {
|
|
93
|
+
const stdout = exec('bd', ['export', '--all'], {
|
|
94
|
+
cwd: projectRoot || process.cwd(),
|
|
95
|
+
encoding: 'utf8',
|
|
96
|
+
});
|
|
97
|
+
fsImpl.writeFileSync(path.join(tempDir, 'issues.jsonl'), stdout || '');
|
|
98
|
+
return { dir: tempDir, kind: 'dolt-export', temp: tempDir };
|
|
99
|
+
} catch (err) {
|
|
100
|
+
fsImpl.rmSync(tempDir, { recursive: true, force: true });
|
|
101
|
+
const message = err && err.message ? err.message : String(err);
|
|
102
|
+
if (/ENOENT|not found|not recognized|no such file/i.test(message)) {
|
|
103
|
+
return {
|
|
104
|
+
error:
|
|
105
|
+
"Beads CLI 'bd' is not on PATH — cannot export the Dolt store at " +
|
|
106
|
+
`${beadsDir}. Install bd, or pass --source <dir> with exported *.jsonl files.`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return { error: `bd export --all failed: ${message}` };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Resolve the directory holding Beads JSONL the loader will read.
|
|
115
|
+
* 1. --source wins (used as given).
|
|
116
|
+
* 2. else auto-detect a `.beads/` dir containing *.jsonl.
|
|
117
|
+
* 3. else a Dolt-only `.beads/` (no jsonl) → `bd export --all` into a temp dir.
|
|
118
|
+
* 4. else nothing found.
|
|
119
|
+
*/
|
|
120
|
+
function resolveBeadsSource(options, projectRoot, deps) {
|
|
121
|
+
const fsImpl = deps.fs || fs;
|
|
122
|
+
const root = projectRoot || process.cwd();
|
|
123
|
+
|
|
124
|
+
if (options.source) {
|
|
125
|
+
const dir = path.resolve(root, options.source);
|
|
126
|
+
if (!fsImpl.existsSync(dir)) {
|
|
127
|
+
return { error: `No beads data found at --source ${dir} (directory does not exist)` };
|
|
128
|
+
}
|
|
129
|
+
return { dir, kind: 'source' };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const beadsDir = path.join(root, '.beads');
|
|
133
|
+
if (fsImpl.existsSync(beadsDir)) {
|
|
134
|
+
if (directoryHasJsonl(beadsDir, fsImpl)) {
|
|
135
|
+
return { dir: beadsDir, kind: 'jsonl-autodetect' };
|
|
136
|
+
}
|
|
137
|
+
return exportDoltStoreToJsonl(beadsDir, root, deps);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
error:
|
|
142
|
+
'No beads data found. Looked for a .beads/ directory with *.jsonl under ' +
|
|
143
|
+
`${root}. Pass --source <dir> to point at an exported Beads store.`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function cleanupResolved(resolved, deps) {
|
|
148
|
+
if (resolved && resolved.temp) {
|
|
149
|
+
const fsImpl = deps.fs || fs;
|
|
150
|
+
fsImpl.rmSync(resolved.temp, { recursive: true, force: true });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function summarizeGaps(gaps) {
|
|
155
|
+
const items = Array.isArray(gaps) ? gaps : [];
|
|
156
|
+
return {
|
|
157
|
+
count: items.length,
|
|
158
|
+
items: items.map(gap => ({ field: gap.field, reason: gap.reason })),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function renderHuman(payload) {
|
|
163
|
+
if (payload.error) {
|
|
164
|
+
return `forge migrate failed: ${payload.error}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const gaps = payload.gaps || { count: 0, items: [] };
|
|
168
|
+
const gapBrief = gaps.count > 0
|
|
169
|
+
? ` (${gaps.items.map(item => item.field).join(', ')})`
|
|
170
|
+
: '';
|
|
171
|
+
|
|
172
|
+
if (payload.dryRun) {
|
|
173
|
+
return [
|
|
174
|
+
`Dry run — would import beads → kernel from ${payload.source}:`,
|
|
175
|
+
` issues: ${payload.planned.issues}, comments: ${payload.planned.comments}, ` +
|
|
176
|
+
`dependencies: ${payload.planned.dependencies}, events: ${payload.planned.events ?? 0}`,
|
|
177
|
+
` gaps: ${gaps.count}${gapBrief}`,
|
|
178
|
+
' (nothing written to the kernel)',
|
|
179
|
+
].join('\n');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const imported = payload.imported;
|
|
183
|
+
const importedEvents = imported.events || { inserted: 0, skipped: 0 };
|
|
184
|
+
return [
|
|
185
|
+
`Migrated beads → kernel from ${payload.source}:`,
|
|
186
|
+
` issues: ${imported.issues.inserted} imported, ${imported.issues.skipped} skipped`,
|
|
187
|
+
` comments: ${imported.comments.inserted} imported, ${imported.comments.skipped || 0} skipped`,
|
|
188
|
+
` dependencies: ${imported.dependencies.inserted} imported, ` +
|
|
189
|
+
`${imported.dependencies.skipped} skipped`,
|
|
190
|
+
` events: ${importedEvents.inserted} imported, ${importedEvents.skipped || 0} skipped`,
|
|
191
|
+
` gaps: ${gaps.count}${gapBrief}`,
|
|
192
|
+
].join('\n');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function finalize(options, payload, exitCode) {
|
|
196
|
+
const output = options.json ? JSON.stringify(payload, null, 2) : renderHuman(payload);
|
|
197
|
+
const result = { ...payload, output, json: Boolean(options.json) };
|
|
198
|
+
if (payload.success === false && Number.isInteger(exitCode)) {
|
|
199
|
+
result.exitCode = exitCode;
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Beads → Forge Kernel transport. Reuses the faithful-import spine end-to-end:
|
|
206
|
+
* loadBeadsSnapshotFromDirectory → importBeadsSnapshot → broker.importIssues.
|
|
207
|
+
*/
|
|
208
|
+
async function runBeadsMigration(options, projectRoot, opts) {
|
|
209
|
+
const now = opts._now || new Date().toISOString();
|
|
210
|
+
const deps = { execFileSync: opts._execFileSync, fs: opts._fs };
|
|
211
|
+
|
|
212
|
+
const resolved = resolveBeadsSource(options, projectRoot, deps);
|
|
213
|
+
if (resolved.error) {
|
|
214
|
+
return finalize(options, { success: false, from: 'beads', error: resolved.error }, 1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let snapshot;
|
|
218
|
+
try {
|
|
219
|
+
snapshot = loadBeadsSnapshotFromDirectory(resolved.dir);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
cleanupResolved(resolved, deps);
|
|
222
|
+
return finalize(
|
|
223
|
+
options,
|
|
224
|
+
{ success: false, from: 'beads', source: resolved.dir, error: `Failed to read beads data: ${err.message}` },
|
|
225
|
+
1,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (snapshot.issues.length === 0 && snapshot.comments.length === 0 && snapshot.dependencies.length === 0) {
|
|
230
|
+
cleanupResolved(resolved, deps);
|
|
231
|
+
return finalize(
|
|
232
|
+
options,
|
|
233
|
+
{ success: false, from: 'beads', source: resolved.dir, error: `No beads data found in ${resolved.dir}` },
|
|
234
|
+
1,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let kernel;
|
|
239
|
+
let report;
|
|
240
|
+
try {
|
|
241
|
+
({ kernel, report } = importBeadsSnapshot(snapshot, { importedAt: now }));
|
|
242
|
+
} catch (err) {
|
|
243
|
+
cleanupResolved(resolved, deps);
|
|
244
|
+
return finalize(
|
|
245
|
+
options,
|
|
246
|
+
{ success: false, from: 'beads', source: resolved.dir, error: `Failed to map beads data: ${err.message}` },
|
|
247
|
+
1,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const gaps = summarizeGaps(report?.gaps);
|
|
251
|
+
const planned = {
|
|
252
|
+
issues: kernel.issues.length,
|
|
253
|
+
comments: kernel.comments.length,
|
|
254
|
+
dependencies: kernel.dependencies.length,
|
|
255
|
+
events: Array.isArray(kernel.activityEvents) ? kernel.activityEvents.length : 0,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
if (options.dryRun) {
|
|
259
|
+
cleanupResolved(resolved, deps);
|
|
260
|
+
return finalize(options, {
|
|
261
|
+
success: true,
|
|
262
|
+
from: 'beads',
|
|
263
|
+
dryRun: true,
|
|
264
|
+
source: resolved.dir,
|
|
265
|
+
sourceKind: resolved.kind,
|
|
266
|
+
planned,
|
|
267
|
+
gaps,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
let broker = opts._broker;
|
|
273
|
+
if (!broker) {
|
|
274
|
+
const built = await buildMigratedKernelIssueDeps({ projectRoot });
|
|
275
|
+
broker = built.kernelBroker;
|
|
276
|
+
}
|
|
277
|
+
const summary = await broker.importIssues(kernel, { now });
|
|
278
|
+
cleanupResolved(resolved, deps);
|
|
279
|
+
return finalize(options, {
|
|
280
|
+
success: true,
|
|
281
|
+
from: 'beads',
|
|
282
|
+
dryRun: false,
|
|
283
|
+
source: resolved.dir,
|
|
284
|
+
sourceKind: resolved.kind,
|
|
285
|
+
planned,
|
|
286
|
+
imported: {
|
|
287
|
+
issues: summary.issues,
|
|
288
|
+
comments: summary.comments,
|
|
289
|
+
dependencies: summary.dependencies,
|
|
290
|
+
events: summary.events,
|
|
291
|
+
},
|
|
292
|
+
gaps,
|
|
293
|
+
});
|
|
294
|
+
} catch (err) {
|
|
295
|
+
cleanupResolved(resolved, deps);
|
|
296
|
+
return finalize(
|
|
297
|
+
options,
|
|
298
|
+
{ success: false, from: 'beads', source: resolved.dir, error: `Kernel import failed: ${err.message}` },
|
|
299
|
+
1,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
module.exports = {
|
|
305
|
+
name: 'migrate',
|
|
306
|
+
description: 'Migrate a Beads issue store into the Forge Kernel with --from beads, '
|
|
307
|
+
+ 'or preview the v2→v3 migration (preview only — --dry-run required; applying it is not yet available)',
|
|
308
|
+
usage: 'forge migrate --from beads [--dry-run] [--source <dir>] [--json]',
|
|
309
|
+
flags: {
|
|
310
|
+
'--from <source>': 'Migration source. Use "beads" to import a Beads issue store into the Kernel.',
|
|
311
|
+
'--source <dir>': 'Directory of exported beads *.jsonl files (defaults to auto-detecting .beads/). '
|
|
312
|
+
+ 'A split .beads layout is tolerated: files missing from the given dir are also read from a '
|
|
313
|
+
+ 'backup/ subdir and the parent dir, so events (under .beads/backup/) and interactions '
|
|
314
|
+
+ '(at .beads/interactions.jsonl) are both picked up regardless of which you point at.',
|
|
315
|
+
'--dry-run': 'Read + map only; print what WOULD be imported without writing to the Kernel. '
|
|
316
|
+
+ 'Without --from, this is the only supported mode (preview only).',
|
|
317
|
+
'--json': 'Emit a structured JSON result (counts + gaps + dryRun flag).',
|
|
318
|
+
'--fixture-corpus': 'v2→v3 preview only: also dry-run the source-tree v2 fixture corpus when available.',
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
async handler(args, flags, projectRoot, opts = {}) {
|
|
322
|
+
const options = parseMigrateOptions(args, flags);
|
|
323
|
+
if (options.errors.length > 0) {
|
|
324
|
+
return finalize(options, { success: false, error: options.errors.join('; ') }, 1);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// New beads → kernel transport, gated on `--from beads`. Any other (or absent)
|
|
328
|
+
// `--from` falls through to the pre-existing v2→v3 dry-run PoC so its contract
|
|
329
|
+
// (and tests) stay intact.
|
|
330
|
+
if (options.from) {
|
|
331
|
+
if (options.from === 'beads') {
|
|
332
|
+
return runBeadsMigration(options, projectRoot, opts);
|
|
333
|
+
}
|
|
334
|
+
return finalize(
|
|
335
|
+
options,
|
|
336
|
+
{ success: false, error: `Unsupported migration source '${options.from}'. Supported sources: beads.` },
|
|
337
|
+
1,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// --- legacy v2 → v3 dry-run PoC (unchanged) ---
|
|
342
|
+
const dryRun = flags?.dryRun === true || hasArg(args, '--dry-run');
|
|
343
|
+
if (!dryRun) {
|
|
344
|
+
return {
|
|
345
|
+
success: false,
|
|
346
|
+
error: "forge migrate currently supports preview only. Run 'forge migrate --dry-run' "
|
|
347
|
+
+ 'to see what would change; applying migrations is not yet available.',
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const report = buildMigrationDryRunReport(projectRoot, {
|
|
352
|
+
fixtureCorpus: hasArg(args, '--fixture-corpus'),
|
|
353
|
+
});
|
|
354
|
+
const output = renderMigrationDryRunReport(report);
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
success: report.ok,
|
|
358
|
+
output,
|
|
359
|
+
error: report.ok ? undefined : output,
|
|
360
|
+
};
|
|
361
|
+
},
|
|
362
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { scaffoldAdapter } = require('../adapter-cli');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: 'new',
|
|
7
|
+
description: 'Create Forge extension scaffolds',
|
|
8
|
+
usage: 'forge new adapter <name> --kind=review --template=greptile',
|
|
9
|
+
async handler(args, _flags, projectRoot) {
|
|
10
|
+
return scaffoldAdapter(args, projectRoot);
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getDefaultRuntimeGraph,
|
|
5
|
+
getResolvedRuntimeGraph,
|
|
6
|
+
lintRuntimeGraphConfig,
|
|
7
|
+
} = require('../core/runtime-graph');
|
|
8
|
+
const { describeControl } = require('../control-plane');
|
|
9
|
+
|
|
10
|
+
const COLLECTIONS = {
|
|
11
|
+
stages: { key: 'phases', label: 'Stages' },
|
|
12
|
+
gates: { key: 'gates', label: 'Gates' },
|
|
13
|
+
adapters: { key: 'adapters', label: 'Adapters' },
|
|
14
|
+
roles: { key: 'roles', label: 'Roles' },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function hasJson(args) {
|
|
18
|
+
return args.includes('--json');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function withoutFlags(args) {
|
|
22
|
+
return args.filter(arg => arg !== '--json');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function jsonOutput(value) {
|
|
26
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function summarizeRole(item) {
|
|
30
|
+
const parts = [`skill=${item.skill}`];
|
|
31
|
+
if (item.ideology) parts.push(`ideology=${item.ideology}`);
|
|
32
|
+
return `${item.id} (${parts.join(', ')})`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function summarizeItem(item) {
|
|
36
|
+
if (item.role) return summarizeRole(item);
|
|
37
|
+
const state = item.enabled === false ? 'disabled' : 'enabled';
|
|
38
|
+
const locked = item.locked ? ', locked' : '';
|
|
39
|
+
const label = item.label ?? item.kind ?? item.command ?? 'graph primitive';
|
|
40
|
+
const badge = item.badge ? ` [${item.badge}]` : '';
|
|
41
|
+
return `${item.id} (${state}${locked}) - ${label}${badge}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function renderCollection(label, items) {
|
|
45
|
+
return [
|
|
46
|
+
label,
|
|
47
|
+
...items.map(item => `- ${summarizeItem(item)}`),
|
|
48
|
+
].join('\n') + '\n';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function allPrimitiveEntries(graph) {
|
|
52
|
+
return [
|
|
53
|
+
...graph.phases.map(item => ['stage', item]),
|
|
54
|
+
...graph.gates.map(item => ['gate', item]),
|
|
55
|
+
...graph.adapters.map(item => ['adapter', item]),
|
|
56
|
+
...graph.rails.map(item => ['rail', item]),
|
|
57
|
+
...graph.actions.map(item => ['action', item]),
|
|
58
|
+
...graph.artifacts.map(item => ['artifact', item]),
|
|
59
|
+
...graph.evaluatorRegions.map(item => ['evaluatorRegion', item]),
|
|
60
|
+
...graph.evidence.map(item => ['evidence', item]),
|
|
61
|
+
...(graph.planning?.subSkills ?? []).map(item => ['planningSubSkill', item]),
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function findPrimitive(graph, id) {
|
|
66
|
+
return allPrimitiveEntries(graph).find(([type, item]) => item.id === id || item.key === id || `${type}.${item.id}` === id);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function renderWhy(type, item) {
|
|
70
|
+
const lines = [
|
|
71
|
+
`${item.id}`,
|
|
72
|
+
`type: ${type}`,
|
|
73
|
+
`state: ${item.enabled === false ? 'disabled' : 'enabled'}`,
|
|
74
|
+
`source: ${item.configSource ?? 'package-defaults'}`,
|
|
75
|
+
];
|
|
76
|
+
if (item.locked) lines.push('locked: true');
|
|
77
|
+
if (item.requires) lines.push(`requires: ${item.requires.join(', ')}`);
|
|
78
|
+
if (item.reads?.length) lines.push(`reads: ${item.reads.join(', ')}`);
|
|
79
|
+
if (item.writes?.length) lines.push(`writes: ${item.writes.join(', ')}`);
|
|
80
|
+
if (item.description) lines.push(`description: ${item.description}`);
|
|
81
|
+
return `${lines.join('\n')}\n`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function primitiveMap(graph, key) {
|
|
85
|
+
return new Map((graph[key] ?? []).map(item => [item.id, item]));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function collectDiff(defaultGraph, resolvedGraph) {
|
|
89
|
+
const changes = [];
|
|
90
|
+
for (const key of ['phases', 'gates', 'adapters', 'rails']) {
|
|
91
|
+
const defaults = primitiveMap(defaultGraph, key);
|
|
92
|
+
const resolved = primitiveMap(resolvedGraph, key);
|
|
93
|
+
for (const [id, afterItem] of resolved) {
|
|
94
|
+
const beforeItem = defaults.get(id);
|
|
95
|
+
if (!beforeItem) continue;
|
|
96
|
+
for (const field of ['enabled', 'disabled', 'configSource', 'config']) {
|
|
97
|
+
const before = beforeItem[field];
|
|
98
|
+
const after = afterItem[field];
|
|
99
|
+
if (JSON.stringify(before) !== JSON.stringify(after)) {
|
|
100
|
+
changes.push({ collection: key, id, field, before, after });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (JSON.stringify(defaultGraph.protectedPaths) !== JSON.stringify(resolvedGraph.protectedPaths)) {
|
|
106
|
+
changes.push({
|
|
107
|
+
collection: 'protectedPaths',
|
|
108
|
+
id: 'protectedPaths',
|
|
109
|
+
field: 'patterns',
|
|
110
|
+
before: defaultGraph.protectedPaths,
|
|
111
|
+
after: resolvedGraph.protectedPaths,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (JSON.stringify(defaultGraph.planning?.template) !== JSON.stringify(resolvedGraph.planning?.template)) {
|
|
115
|
+
changes.push({
|
|
116
|
+
collection: 'planning',
|
|
117
|
+
id: 'planning.template',
|
|
118
|
+
field: 'config',
|
|
119
|
+
before: defaultGraph.planning?.template,
|
|
120
|
+
after: resolvedGraph.planning?.template,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return changes;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function renderDiff(changes) {
|
|
127
|
+
if (changes.length === 0) {
|
|
128
|
+
return 'No graph config changes.\n';
|
|
129
|
+
}
|
|
130
|
+
return changes
|
|
131
|
+
.map(change => `${change.id}.${change.field}: ${JSON.stringify(change.before)} -> ${JSON.stringify(change.after)}`)
|
|
132
|
+
.join('\n') + '\n';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function renderLint(result) {
|
|
136
|
+
if (result.ok) {
|
|
137
|
+
return 'Graph config lint passed.\n';
|
|
138
|
+
}
|
|
139
|
+
return [
|
|
140
|
+
'Graph config lint failed.',
|
|
141
|
+
...result.errors.map(error => `- ${error.code}: ${error.message}`),
|
|
142
|
+
].join('\n') + '\n';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function usage() {
|
|
146
|
+
return [
|
|
147
|
+
'Usage: forge options <stages|gates|adapters|roles|diff|why <id>|lint> [--json]',
|
|
148
|
+
'',
|
|
149
|
+
'Inspect resolved runtime graph primitives and project config effects.',
|
|
150
|
+
].join('\n');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function lintCommand(projectRoot, json) {
|
|
154
|
+
const result = lintRuntimeGraphConfig({ projectRoot });
|
|
155
|
+
const output = json ? jsonOutput(result) : renderLint(result);
|
|
156
|
+
return {
|
|
157
|
+
success: result.ok,
|
|
158
|
+
output,
|
|
159
|
+
error: result.ok ? undefined : output,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function resolveGraphResult(projectRoot, json) {
|
|
164
|
+
try {
|
|
165
|
+
return { success: true, graph: getResolvedRuntimeGraph({ projectRoot }) };
|
|
166
|
+
} catch (err) {
|
|
167
|
+
const output = json
|
|
168
|
+
? jsonOutput({ ok: false, errors: err.message.split('\n').map(message => ({ message })) })
|
|
169
|
+
: `${err.message}\n`;
|
|
170
|
+
return { success: false, output, error: output };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Gates carry enforcement-locus badges (724356ea): decorate each with the
|
|
175
|
+
// honest surface/state/locus/badge from the guarantee matrix so the read view
|
|
176
|
+
// never implies enforcement a surface lacks (human gates read PERMISSION,
|
|
177
|
+
// ordinary gates read ENFORCED).
|
|
178
|
+
function decorateWithLocus(subcommand, items) {
|
|
179
|
+
if (subcommand !== 'gates') return items;
|
|
180
|
+
return items.map(item => ({ ...item, ...describeControl(item) }));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function collectionCommand(graph, subcommand, json) {
|
|
184
|
+
const { key, label } = COLLECTIONS[subcommand];
|
|
185
|
+
const items = decorateWithLocus(subcommand, graph[key]);
|
|
186
|
+
return {
|
|
187
|
+
success: true,
|
|
188
|
+
output: json ? jsonOutput({ kind: subcommand, items }) : renderCollection(label, items),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function whyCommand(graph, id, json) {
|
|
193
|
+
const match = id ? findPrimitive(graph, id) : null;
|
|
194
|
+
if (!match) {
|
|
195
|
+
const message = `Unknown graph primitive: ${id ?? '<missing>'}`;
|
|
196
|
+
const output = json
|
|
197
|
+
? jsonOutput({ ok: false, errors: [{ code: 'UNKNOWN_GRAPH_PRIMITIVE', message }] })
|
|
198
|
+
: message;
|
|
199
|
+
return { success: false, output, error: output };
|
|
200
|
+
}
|
|
201
|
+
const [type, item] = match;
|
|
202
|
+
return {
|
|
203
|
+
success: true,
|
|
204
|
+
output: json ? jsonOutput({ type, item }) : renderWhy(type, item),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function diffCommand(graph, json) {
|
|
209
|
+
const changes = collectDiff(getDefaultRuntimeGraph(), graph);
|
|
210
|
+
return {
|
|
211
|
+
success: true,
|
|
212
|
+
output: json ? jsonOutput({ changes }) : renderDiff(changes),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function handler(args, _flags, projectRoot) {
|
|
217
|
+
const json = hasJson(args);
|
|
218
|
+
const tokens = withoutFlags(args);
|
|
219
|
+
const subcommand = tokens[0];
|
|
220
|
+
|
|
221
|
+
if (!subcommand) return { success: false, error: usage() };
|
|
222
|
+
if (subcommand === 'lint') return lintCommand(projectRoot, json);
|
|
223
|
+
|
|
224
|
+
const graphResult = resolveGraphResult(projectRoot, json);
|
|
225
|
+
if (!graphResult.success) return graphResult;
|
|
226
|
+
if (COLLECTIONS[subcommand]) return collectionCommand(graphResult.graph, subcommand, json);
|
|
227
|
+
if (subcommand === 'why') return whyCommand(graphResult.graph, tokens[1], json);
|
|
228
|
+
if (subcommand === 'diff') return diffCommand(graphResult.graph, json);
|
|
229
|
+
return { success: false, error: usage() };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
module.exports = {
|
|
233
|
+
name: 'options',
|
|
234
|
+
description: 'Inspect runtime graph primitives and config',
|
|
235
|
+
usage: usage(),
|
|
236
|
+
flags: {
|
|
237
|
+
'--json': 'Emit machine-readable JSON output',
|
|
238
|
+
},
|
|
239
|
+
handler,
|
|
240
|
+
collectDiff,
|
|
241
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
buildOrientation,
|
|
5
|
+
runOrientationCommand,
|
|
6
|
+
} = require('../orientation');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
name: 'orient',
|
|
10
|
+
description: 'Emit bounded project orientation from deterministic source files',
|
|
11
|
+
usage: 'Usage: forge orient [--budget N] [--json]',
|
|
12
|
+
handler: (args, _flags, projectRoot) => runOrientationCommand(buildOrientation, args, projectRoot),
|
|
13
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
recordPatchIntentFromDiff,
|
|
5
|
+
resolvePatchIntentRecords,
|
|
6
|
+
} = require('../patch-intent');
|
|
7
|
+
|
|
8
|
+
const usage = [
|
|
9
|
+
'forge patch record --from-diff',
|
|
10
|
+
'forge patch status',
|
|
11
|
+
].join('\n');
|
|
12
|
+
|
|
13
|
+
function formatRecordSummary(result) {
|
|
14
|
+
const count = result.records.length;
|
|
15
|
+
const label = count === 1 ? 'patch intent' : 'patch intents';
|
|
16
|
+
return [
|
|
17
|
+
`Recorded ${count} ${label} in ${result.path}.`,
|
|
18
|
+
...result.records.map(record => `- ${record.id} -> ${record.anchorId} (${record.path})`),
|
|
19
|
+
].join('\n');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function handler(args, _flags, projectRoot) {
|
|
23
|
+
const [subcommand, ...rest] = args;
|
|
24
|
+
|
|
25
|
+
if (subcommand === 'record') {
|
|
26
|
+
if (!rest.includes('--from-diff')) {
|
|
27
|
+
return {
|
|
28
|
+
success: false,
|
|
29
|
+
error: `Missing --from-diff.\n\nUsage:\n${usage}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const result = recordPatchIntentFromDiff(projectRoot);
|
|
33
|
+
return {
|
|
34
|
+
success: true,
|
|
35
|
+
output: formatRecordSummary(result),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (subcommand === 'status') {
|
|
40
|
+
const result = resolvePatchIntentRecords(projectRoot);
|
|
41
|
+
const lines = [
|
|
42
|
+
`Patch intent file: ${result.path}`,
|
|
43
|
+
`Records: ${result.records.length}`,
|
|
44
|
+
`Orphans: ${result.orphans.length}`,
|
|
45
|
+
];
|
|
46
|
+
for (const record of result.records) {
|
|
47
|
+
lines.push(`- ${record.id}: ${record.status} ${record.anchorId} -> ${record.currentPath || record.path}`);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
success: true,
|
|
51
|
+
output: lines.join('\n'),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
error: `Unknown patch subcommand: ${subcommand || '(none)'}\n\nUsage:\n${usage}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
name: 'patch',
|
|
63
|
+
description: 'Record and inspect patch intent anchored in patch.md',
|
|
64
|
+
usage,
|
|
65
|
+
handler,
|
|
66
|
+
};
|
|
67
|
+
|