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,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* package-manager-remediation.js — Detect the caller's package manager from
|
|
3
|
+
* lockfile presence so remediation messages (e.g. "lefthook is missing, run
|
|
4
|
+
* X") give the correct install command instead of hard-coding a single tool.
|
|
5
|
+
*
|
|
6
|
+
* This is deliberately separate from lib/detection-utils.js#detectPackageManager,
|
|
7
|
+
* which shells out to probe installed binaries and prints setup-wizard output.
|
|
8
|
+
* Remediation messages need a fast, side-effect-free, synchronous lookup that
|
|
9
|
+
* never spawns a process — it runs on every stage-gate health check.
|
|
10
|
+
*
|
|
11
|
+
* @module lib/package-manager-remediation
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('node:fs');
|
|
15
|
+
const path = require('node:path');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Ordered lockfile → package manager mapping. First match wins.
|
|
19
|
+
* `install` is the command to fetch already-declared dependencies.
|
|
20
|
+
* `addDev(pkg)` is the command to add a new dev dependency and install it.
|
|
21
|
+
*/
|
|
22
|
+
const PACKAGE_MANAGERS = [
|
|
23
|
+
{
|
|
24
|
+
name: 'bun',
|
|
25
|
+
lockFiles: ['bun.lockb', 'bun.lock'],
|
|
26
|
+
install: 'bun install',
|
|
27
|
+
addDev: pkg => `bun add -D ${pkg} && bun install`
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'pnpm',
|
|
31
|
+
lockFiles: ['pnpm-lock.yaml'],
|
|
32
|
+
install: 'pnpm install',
|
|
33
|
+
addDev: pkg => `pnpm add -D ${pkg}`
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'yarn',
|
|
37
|
+
lockFiles: ['yarn.lock'],
|
|
38
|
+
install: 'yarn install',
|
|
39
|
+
addDev: pkg => `yarn add -D ${pkg}`
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'npm',
|
|
43
|
+
lockFiles: ['package-lock.json'],
|
|
44
|
+
install: 'npm install',
|
|
45
|
+
addDev: pkg => `npm install -D ${pkg}`
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// npm is the safest universal fallback when no lockfile is present or
|
|
50
|
+
// recognized — it ships with Node itself, so it is always a documented,
|
|
51
|
+
// reasonable default rather than assuming any particular toolchain.
|
|
52
|
+
const DEFAULT_MANAGER = PACKAGE_MANAGERS[PACKAGE_MANAGERS.length - 1];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Detect the package manager in use for a project from lockfile presence.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} [projectRoot] - Absolute path to the project root. Falls
|
|
58
|
+
* back to process.cwd() when omitted or not a non-empty string.
|
|
59
|
+
* @returns {{ name: string, install: string, addDev: (pkg: string) => string }}
|
|
60
|
+
* The detected (or default) package manager descriptor.
|
|
61
|
+
*/
|
|
62
|
+
function detectPackageManagerForRemediation(projectRoot) {
|
|
63
|
+
const root = typeof projectRoot === 'string' && projectRoot.trim()
|
|
64
|
+
? projectRoot
|
|
65
|
+
: process.cwd();
|
|
66
|
+
|
|
67
|
+
for (const manager of PACKAGE_MANAGERS) {
|
|
68
|
+
const hasLockFile = manager.lockFiles.some(file =>
|
|
69
|
+
fs.existsSync(path.join(root, file))
|
|
70
|
+
);
|
|
71
|
+
if (hasLockFile) return manager;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return DEFAULT_MANAGER;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get the install command for the detected package manager (e.g. when a
|
|
79
|
+
* dependency is already declared but not installed).
|
|
80
|
+
*
|
|
81
|
+
* @param {string} [projectRoot] - Absolute path to the project root.
|
|
82
|
+
* @returns {string} Install command, e.g. "npm install".
|
|
83
|
+
*/
|
|
84
|
+
function getInstallCommand(projectRoot) {
|
|
85
|
+
return detectPackageManagerForRemediation(projectRoot).install;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the "add as dev dependency" command for the detected package manager.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} [projectRoot] - Absolute path to the project root.
|
|
92
|
+
* @param {string} pkg - Package name to add, e.g. "lefthook".
|
|
93
|
+
* @returns {string} Add-dev-dependency command, e.g. "npm install -D lefthook".
|
|
94
|
+
*/
|
|
95
|
+
function getAddDevCommand(projectRoot, pkg) {
|
|
96
|
+
return detectPackageManagerForRemediation(projectRoot).addDev(pkg);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
detectPackageManagerForRemediation,
|
|
101
|
+
getInstallCommand,
|
|
102
|
+
getAddDevCommand
|
|
103
|
+
};
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* global FORGE_COMPILED */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @module package-root
|
|
7
|
+
*
|
|
8
|
+
* Dual-channel resolution of the Forge "package root" — the directory that ships
|
|
9
|
+
* the runtime assets `forge setup` (and a few runtime commands) copy into a
|
|
10
|
+
* target project (`skills/`, `rules/`, essential docs, `AGENTS.md`, hook scripts,
|
|
11
|
+
* workflow runtime scripts).
|
|
12
|
+
*
|
|
13
|
+
* Two distribution channels must both work:
|
|
14
|
+
*
|
|
15
|
+
* 1. npm / npx install → the package is a real directory on disk. The root is
|
|
16
|
+
* the Forge repo/package root (`lib/` → `..`). Historical path; unchanged.
|
|
17
|
+
*
|
|
18
|
+
* 2. `bun build --compile` single-file binary → there is NO package directory
|
|
19
|
+
* on disk. The runtime assets are embedded at compile time (Bun import
|
|
20
|
+
* attributes, see `scripts/gen-embedded-assets.mjs`) and this module
|
|
21
|
+
* materializes them into a temp directory on first use, then hands that
|
|
22
|
+
* directory to the existing (unchanged) copy logic. This "extract then
|
|
23
|
+
* delegate" keeps every downstream asset consumer working without a rewrite.
|
|
24
|
+
*
|
|
25
|
+
* Every consumer that READS a packaged asset off the package root goes through
|
|
26
|
+
* `getPackageRoot(diskFallback)`:
|
|
27
|
+
* - npm/dev: returns `diskFallback` (verified to actually contain assets).
|
|
28
|
+
* - compiled: returns the memoized, sentinel-guarded extraction directory.
|
|
29
|
+
*
|
|
30
|
+
* Module `require()`s that resolve code (`require(path.join(packageDir,'lib',…))`)
|
|
31
|
+
* are NOT asset reads and are intentionally left alone — those are handled by the
|
|
32
|
+
* bundler when compiling, not by this helper.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const fs = require('node:fs');
|
|
36
|
+
const path = require('node:path');
|
|
37
|
+
const os = require('node:os');
|
|
38
|
+
|
|
39
|
+
/** On-disk package root for the npm/dev channel: `lib/` → repo root. */
|
|
40
|
+
const DISK_PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
41
|
+
|
|
42
|
+
/** Filename written into the extraction dir after a fully-successful extraction. */
|
|
43
|
+
const SENTINEL_FILE = '.forge-assets-complete';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Asset roots that Forge reads from the package root. Directory entries are
|
|
47
|
+
* embedded recursively; file entries individually. The generator
|
|
48
|
+
* (`scripts/gen-embedded-assets.mjs`) walks exactly this list, so it is the
|
|
49
|
+
* single source of truth for "what ships inside the compiled binary".
|
|
50
|
+
*
|
|
51
|
+
* Kept in sync with the setup/runtime consumers (asserted by
|
|
52
|
+
* test/embedded-assets-drift.test.js):
|
|
53
|
+
* - skills/** → populateAgentSkills, listCanonicalSkills, codex skills, reset
|
|
54
|
+
* - rules/** → renderRulesForHarness (agents-config)
|
|
55
|
+
* - AGENTS.md → workflow contract copied into the project
|
|
56
|
+
* - .claude/scripts/** → load-env.sh, review-resolve.sh
|
|
57
|
+
* - .forge/hooks/** → check-tdd.js, forge-native-hook.js
|
|
58
|
+
* - scripts/** → WORKFLOW_RUNTIME_ASSETS (team + workflow scripts), `forge team`
|
|
59
|
+
*
|
|
60
|
+
* NOTE: the essential docs (TOOLCHAIN.md / VALIDATION.md) are intentionally NOT
|
|
61
|
+
* embedded — copyEssentialDocs reads `docs/<doc>` which does not exist (the files
|
|
62
|
+
* live under docs/reference/ + docs/forge/), so it is a no-op in BOTH channels
|
|
63
|
+
* today. Embedding follows the copyEssentialDocs source-path fix (deferred). Every
|
|
64
|
+
* root listed here MUST exist on disk — the generator fails closed if one is absent.
|
|
65
|
+
*/
|
|
66
|
+
const ASSET_ROOTS = Object.freeze([
|
|
67
|
+
'skills',
|
|
68
|
+
'rules',
|
|
69
|
+
'AGENTS.md',
|
|
70
|
+
'.claude/scripts',
|
|
71
|
+
'.forge/hooks',
|
|
72
|
+
'scripts',
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Compile-time signal injected by `build:binary` via `--define FORGE_COMPILED=true`.
|
|
77
|
+
* The `global` directive above tells ESLint the identifier exists; the `typeof`
|
|
78
|
+
* guard prevents a ReferenceError under node where it is never defined.
|
|
79
|
+
*/
|
|
80
|
+
function isCompiledByDefine() {
|
|
81
|
+
try {
|
|
82
|
+
return typeof FORGE_COMPILED !== 'undefined' && FORGE_COMPILED === true;
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* True when running inside a `bun build --compile` single-file executable that
|
|
90
|
+
* carries embedded assets. Primary signal is the injected `FORGE_COMPILED`
|
|
91
|
+
* constant; `Bun.embeddedFiles` is a runtime FALLBACK. Under `node` (npm) and a
|
|
92
|
+
* plain `bun test` (not compiled) both are false → the on-disk path is used.
|
|
93
|
+
*
|
|
94
|
+
* @param {*} [bun] - Bun runtime object; injectable for tests (the real
|
|
95
|
+
* `globalThis.Bun` is a read-only property and cannot be mocked by assignment).
|
|
96
|
+
*/
|
|
97
|
+
function isCompiledBinary(bun = globalThis.Bun) {
|
|
98
|
+
if (isCompiledByDefine()) return true;
|
|
99
|
+
try {
|
|
100
|
+
if (!bun || bun.embeddedFiles == null) return false;
|
|
101
|
+
return Array.from(bun.embeddedFiles).length > 0;
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** A directory qualifies as a real on-disk package root if it carries the core assets. */
|
|
108
|
+
function hasDiskAssets(dir) {
|
|
109
|
+
return (
|
|
110
|
+
!!dir &&
|
|
111
|
+
fs.existsSync(path.join(dir, 'skills')) &&
|
|
112
|
+
fs.existsSync(path.join(dir, 'AGENTS.md'))
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Resolve a stable version tag for the extraction directory + sentinel. */
|
|
117
|
+
function resolveVersion() {
|
|
118
|
+
try {
|
|
119
|
+
// Literal path so the bundler embeds package.json into the binary too.
|
|
120
|
+
return require('../package.json').version || 'dev';
|
|
121
|
+
} catch {
|
|
122
|
+
return 'dev';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Load the compile-time embedded-asset manifest. Only ever reached inside a
|
|
128
|
+
* compiled binary, where the generated module is bundled at build time. The
|
|
129
|
+
* literal specifier lets Bun's compiler discover and embed it; the guard in
|
|
130
|
+
* `getPackageRoot` ensures node/npm never executes this line (the generated
|
|
131
|
+
* file is gitignored and absent there).
|
|
132
|
+
*
|
|
133
|
+
* @returns {{ EMBEDDED_ASSETS: Record<string,string>, EXECUTABLE_ASSETS: string[] }}
|
|
134
|
+
*/
|
|
135
|
+
function loadEmbeddedManifest() {
|
|
136
|
+
return require('./embedded-assets.generated.mjs');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Split a relative path into segments for platform-correct joining. Splits on
|
|
141
|
+
* BOTH `/` and `\` so a backslash-separated key cannot smuggle a `..` segment
|
|
142
|
+
* past the traversal guard on Windows.
|
|
143
|
+
*/
|
|
144
|
+
function relSegments(relPosix) {
|
|
145
|
+
return relPosix.split(/[/\\]/).filter(Boolean);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Extract every embedded asset into `destDir`, preserving relative paths and
|
|
150
|
+
* restoring the executable bit on scripts/hooks (files-only embedding drops
|
|
151
|
+
* mode). Node `fs` reads the `/$bunfs/…` embedded paths directly. Returns the
|
|
152
|
+
* number of files written so the caller can assert completeness.
|
|
153
|
+
*
|
|
154
|
+
* @param {string} destDir
|
|
155
|
+
* @param {Record<string,string>} [assets] - relPath → source path map. Defaults
|
|
156
|
+
* to the compile-time embedded manifest; injectable for unit tests.
|
|
157
|
+
* @param {string[]} [executable] - relPaths needing chmod +x. Defaults to manifest.
|
|
158
|
+
* @returns {number} count of files written
|
|
159
|
+
*/
|
|
160
|
+
function extractEmbeddedAssets(destDir, assets, executable) {
|
|
161
|
+
if (!assets) {
|
|
162
|
+
const manifest = loadEmbeddedManifest();
|
|
163
|
+
assets = manifest.EMBEDDED_ASSETS;
|
|
164
|
+
executable = manifest.EXECUTABLE_ASSETS || [];
|
|
165
|
+
}
|
|
166
|
+
const execSet = new Set(executable || []);
|
|
167
|
+
let count = 0;
|
|
168
|
+
for (const [relPath, embeddedPath] of Object.entries(assets)) {
|
|
169
|
+
// Defense-in-depth: the manifest is compile-time-trusted, but never let a
|
|
170
|
+
// key escape destDir via traversal or an absolute path.
|
|
171
|
+
const segs = relSegments(relPath);
|
|
172
|
+
if (path.isAbsolute(relPath) || segs.includes('..') || segs.length === 0) {
|
|
173
|
+
throw new Error(`Refusing to extract unsafe embedded asset path: ${JSON.stringify(relPath)}`);
|
|
174
|
+
}
|
|
175
|
+
const outPath = path.join(destDir, ...segs);
|
|
176
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
177
|
+
// read+write (not copyFileSync): Bun's embedded `/$bunfs/…` source paths
|
|
178
|
+
// support readFileSync but NOT the copyfile syscall. Binary buffer copy — no
|
|
179
|
+
// encoding/line-ending translation. Works identically for real disk files.
|
|
180
|
+
fs.writeFileSync(outPath, fs.readFileSync(embeddedPath));
|
|
181
|
+
if (execSet.has(relPath)) {
|
|
182
|
+
try {
|
|
183
|
+
// Owner-only rwx (0o700) — this is a per-user cache dir; no group/other
|
|
184
|
+
// needs access. chmod is a no-op on Windows.
|
|
185
|
+
fs.chmodSync(outPath, 0o700);
|
|
186
|
+
} catch {
|
|
187
|
+
// Non-fatal: mode restore best-effort; the copy already succeeded.
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
count += 1;
|
|
191
|
+
}
|
|
192
|
+
return count;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Per-USER cache root for extracted assets. NOT `os.tmpdir()` — a world-writable
|
|
197
|
+
* shared tmp lets a local attacker pre-plant `forge-assets-<version>/` with a
|
|
198
|
+
* valid sentinel + malicious hook scripts that `forge setup` would then install.
|
|
199
|
+
* Windows: `%LOCALAPPDATA%`; POSIX: `$XDG_CACHE_HOME` or `~/.cache`. The `forge`
|
|
200
|
+
* dir is created `0o700` on POSIX so only the owner can populate it.
|
|
201
|
+
*/
|
|
202
|
+
function userCacheRoot() {
|
|
203
|
+
let base;
|
|
204
|
+
if (process.platform === 'win32') {
|
|
205
|
+
base = process.env.LOCALAPPDATA || process.env.APPDATA || os.tmpdir();
|
|
206
|
+
} else {
|
|
207
|
+
base = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
|
|
208
|
+
}
|
|
209
|
+
const root = path.join(base, 'forge');
|
|
210
|
+
fs.mkdirSync(root, { recursive: true });
|
|
211
|
+
if (process.platform !== 'win32') {
|
|
212
|
+
try {
|
|
213
|
+
fs.chmodSync(root, 0o700);
|
|
214
|
+
} catch {
|
|
215
|
+
// Best-effort ownership hardening; not fatal.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return root;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** True iff `dir` holds a completion sentinel matching this version. */
|
|
222
|
+
function isCompleteExtraction(dir, version) {
|
|
223
|
+
try {
|
|
224
|
+
const sentinel = path.join(dir, SENTINEL_FILE);
|
|
225
|
+
return fs.existsSync(sentinel) && fs.readFileSync(sentinel, 'utf8').trim() === version;
|
|
226
|
+
} catch {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Assert every expected relpath exists on disk under `dir` and is non-empty. */
|
|
232
|
+
function assertAllPresent(dir, assets) {
|
|
233
|
+
const missing = [];
|
|
234
|
+
for (const rel of Object.keys(assets)) {
|
|
235
|
+
const p = path.join(dir, ...relSegments(rel));
|
|
236
|
+
let ok = false;
|
|
237
|
+
try {
|
|
238
|
+
ok = fs.statSync(p).size > 0;
|
|
239
|
+
} catch {
|
|
240
|
+
// Missing or unreadable → counts as absent below.
|
|
241
|
+
}
|
|
242
|
+
if (!ok) missing.push(rel);
|
|
243
|
+
}
|
|
244
|
+
if (missing.length > 0) {
|
|
245
|
+
const sample = missing.slice(0, 10).join(', ');
|
|
246
|
+
throw new Error(
|
|
247
|
+
`Forge embedded-asset extraction incomplete: ${missing.length} file(s) missing or empty in ${dir} ` +
|
|
248
|
+
`(e.g. ${sample}${missing.length > 10 ? ', …' : ''}). Rebuild with \`bun run build:binary\`.`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
let _extractedRoot = null;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Materialize embedded assets, atomically and concurrency-safely.
|
|
257
|
+
*
|
|
258
|
+
* Extraction writes into a PRIVATE `mkdtempSync` sibling dir (all files + the
|
|
259
|
+
* completion sentinel), then `renameSync`s it into the final version-stamped
|
|
260
|
+
* path in ONE atomic step. A half-written temp NEVER acquires the final name, so
|
|
261
|
+
* a crash mid-extraction or a second process racing us can never observe (or
|
|
262
|
+
* bless) a partially-populated dir — and no process ever `rmSync`es a dir another
|
|
263
|
+
* process is reading. If the final path already exists (another process won),
|
|
264
|
+
* we discard our temp and adopt theirs.
|
|
265
|
+
*/
|
|
266
|
+
function ensureExtracted() {
|
|
267
|
+
if (_extractedRoot) return _extractedRoot;
|
|
268
|
+
const version = resolveVersion();
|
|
269
|
+
const manifest = loadEmbeddedManifest();
|
|
270
|
+
const cacheRoot = userCacheRoot();
|
|
271
|
+
|
|
272
|
+
// Cache identity = version + an asset-content FINGERPRINT (emitted by the
|
|
273
|
+
// generator). Same-version rebuilds with a changed asset set therefore land in
|
|
274
|
+
// a DIFFERENT dir, so a stale extraction is never silently reused.
|
|
275
|
+
const fp = String(manifest.ASSET_FINGERPRINT || 'nofp').slice(0, 16);
|
|
276
|
+
const finalDir = path.join(cacheRoot, `assets-${version}-${fp}`);
|
|
277
|
+
|
|
278
|
+
// Fast path: a completed extraction already exists — but do NOT trust the
|
|
279
|
+
// sentinel alone; verify the expected files are actually present + non-empty
|
|
280
|
+
// (a truncated/partly-deleted cache must re-extract, not be adopted).
|
|
281
|
+
if (isCompleteExtraction(finalDir, version)) {
|
|
282
|
+
try {
|
|
283
|
+
assertAllPresent(finalDir, manifest.EMBEDDED_ASSETS);
|
|
284
|
+
_extractedRoot = finalDir;
|
|
285
|
+
return finalDir;
|
|
286
|
+
} catch {
|
|
287
|
+
// Cache incomplete/corrupt — fall through to a fresh extraction.
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const tmpDir = fs.mkdtempSync(path.join(cacheRoot, `.assets-${version}-`));
|
|
292
|
+
try {
|
|
293
|
+
extractEmbeddedAssets(tmpDir, manifest.EMBEDDED_ASSETS, manifest.EXECUTABLE_ASSETS || []);
|
|
294
|
+
assertAllPresent(tmpDir, manifest.EMBEDDED_ASSETS);
|
|
295
|
+
fs.writeFileSync(path.join(tmpDir, SENTINEL_FILE), version, 'utf8');
|
|
296
|
+
try {
|
|
297
|
+
fs.renameSync(tmpDir, finalDir);
|
|
298
|
+
} catch (err) {
|
|
299
|
+
// Another process already materialized finalDir (rename onto a non-empty
|
|
300
|
+
// dir fails) — discard ours and adopt theirs if it is complete.
|
|
301
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
302
|
+
if (isCompleteExtraction(finalDir, version)) {
|
|
303
|
+
_extractedRoot = finalDir;
|
|
304
|
+
return finalDir;
|
|
305
|
+
}
|
|
306
|
+
throw err;
|
|
307
|
+
}
|
|
308
|
+
} catch (err) {
|
|
309
|
+
try {
|
|
310
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
311
|
+
} catch {
|
|
312
|
+
// Best-effort cleanup of the private temp dir.
|
|
313
|
+
}
|
|
314
|
+
throw err;
|
|
315
|
+
}
|
|
316
|
+
_extractedRoot = finalDir;
|
|
317
|
+
return finalDir;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* The directory to read packaged assets from, for BOTH channels.
|
|
322
|
+
*
|
|
323
|
+
* Resolution order (safe-default): a real on-disk package (npm/npx/dev) wins;
|
|
324
|
+
* otherwise a compiled binary extracts its embedded assets; otherwise BOTH
|
|
325
|
+
* channels failed and we throw loudly listing both attempts — never silently
|
|
326
|
+
* return an empty dir that lets setup "succeed" with nothing.
|
|
327
|
+
*
|
|
328
|
+
* @param {string} [diskFallback] - The caller's own on-disk package root.
|
|
329
|
+
* @returns {string}
|
|
330
|
+
*/
|
|
331
|
+
function getPackageRoot(diskFallback = DISK_PACKAGE_ROOT) {
|
|
332
|
+
// 1. Disk channel — a real package dir that actually carries assets.
|
|
333
|
+
if (hasDiskAssets(diskFallback)) {
|
|
334
|
+
return diskFallback;
|
|
335
|
+
}
|
|
336
|
+
// 2. Embedded channel — compiled binary (or disk assets simply absent).
|
|
337
|
+
if (isCompiledBinary()) {
|
|
338
|
+
return ensureExtracted();
|
|
339
|
+
}
|
|
340
|
+
// 3. Both channels failed — fail loud, never return an asset-less dir.
|
|
341
|
+
throw new Error(
|
|
342
|
+
'Cannot resolve Forge runtime assets. Tried:\n' +
|
|
343
|
+
` 1. on-disk package root: ${diskFallback} (missing skills/ or AGENTS.md)\n` +
|
|
344
|
+
' 2. embedded assets: not a compiled binary (no FORGE_COMPILED / Bun.embeddedFiles)\n' +
|
|
345
|
+
'A published npm install should always satisfy (1); a single-file binary should satisfy (2).'
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* List the relative POSIX paths of all embedded assets (compiled binary only).
|
|
351
|
+
* Minimal read surface for steps 3/4; returns [] outside a compiled binary.
|
|
352
|
+
*/
|
|
353
|
+
function listAssets() {
|
|
354
|
+
if (!isCompiledBinary()) return [];
|
|
355
|
+
try {
|
|
356
|
+
// Deterministic, locale-INDEPENDENT codepoint order (must match the generator
|
|
357
|
+
// so the embed set is byte-identical across cross-compile targets).
|
|
358
|
+
return Object.keys(loadEmbeddedManifest().EMBEDDED_ASSETS).sort((a, b) =>
|
|
359
|
+
a < b ? -1 : a > b ? 1 : 0
|
|
360
|
+
);
|
|
361
|
+
} catch {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Test-only: reset the memoized extraction directory. */
|
|
367
|
+
function _resetForTests() {
|
|
368
|
+
_extractedRoot = null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
module.exports = {
|
|
372
|
+
getPackageRoot,
|
|
373
|
+
isCompiledBinary,
|
|
374
|
+
extractEmbeddedAssets,
|
|
375
|
+
listAssets,
|
|
376
|
+
hasDiskAssets,
|
|
377
|
+
ASSET_ROOTS,
|
|
378
|
+
DISK_PACKAGE_ROOT,
|
|
379
|
+
SENTINEL_FILE,
|
|
380
|
+
_resetForTests,
|
|
381
|
+
};
|