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
|
@@ -7,7 +7,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
7
7
|
TEST_TMP="$(mktemp -d)"
|
|
8
8
|
trap 'rm -rf "$TEST_TMP"' EXIT
|
|
9
9
|
|
|
10
|
-
# Override TEAM_MAP_ROOT so tests don't touch real .
|
|
10
|
+
# Override TEAM_MAP_ROOT so tests don't touch real .forge/
|
|
11
11
|
export TEAM_MAP_ROOT="$TEST_TMP"
|
|
12
12
|
|
|
13
13
|
# Fix "now" so stale detection is deterministic
|
|
@@ -27,75 +27,41 @@ esac
|
|
|
27
27
|
MOCK
|
|
28
28
|
chmod +x "$mock_dir/gh"
|
|
29
29
|
|
|
30
|
-
# ── Create mock
|
|
31
|
-
|
|
30
|
+
# ── Create mock forge ─────────────────────────────────────────────────────
|
|
31
|
+
# Emits Kernel-shaped JSON ({"data":{"issues":[...]}}) for `issue list`.
|
|
32
|
+
cat > "$mock_dir/forge" << 'MOCK'
|
|
32
33
|
#!/usr/bin/env bash
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
echo "○ forge-bbb · Feature B [● P2 · OPEN]"
|
|
52
|
-
echo "Owner: devtwo"
|
|
53
|
-
echo "Updated: 2026-03-25T10:00:00Z"
|
|
54
|
-
;;
|
|
55
|
-
"show forge-ccc")
|
|
56
|
-
echo "◐ forge-ccc · Feature C [● P1 · IN_PROGRESS]"
|
|
57
|
-
echo "Owner: devone"
|
|
58
|
-
echo "Updated: 2026-03-27T09:00:00Z"
|
|
59
|
-
echo ""
|
|
60
|
-
echo "DEPENDS ON"
|
|
61
|
-
echo " → forge-aaa: Feature A"
|
|
62
|
-
;;
|
|
63
|
-
"show forge-m1n8.6")
|
|
64
|
-
# Dotted sub-ID — bd show must receive the full ID including .6
|
|
65
|
-
echo "◐ forge-m1n8.6 · Sub-feature X [● P2 · IN_PROGRESS]"
|
|
66
|
-
echo "Owner: devthree"
|
|
67
|
-
echo "Updated: 2026-03-27T08:00:00Z"
|
|
68
|
-
;;
|
|
69
|
-
"show forge-m1n8")
|
|
70
|
-
# Parent epic — if a buggy parser truncates forge-m1n8.6 to forge-m1n8,
|
|
71
|
-
# the workload would get this WRONG owner and trigger the guard below.
|
|
72
|
-
echo "◐ forge-m1n8 · PARENT EPIC (WRONG — dotted ID was truncated)"
|
|
73
|
-
echo "Owner: WRONG_OWNER_DO_NOT_USE"
|
|
74
|
-
echo "Updated: 2026-03-20T10:00:00Z"
|
|
75
|
-
;;
|
|
76
|
-
esac
|
|
34
|
+
args="$*"
|
|
35
|
+
emit() { printf '{"data":{"issues":%s}}\n' "$1"; }
|
|
36
|
+
|
|
37
|
+
# forge-aaa BLOCKS forge-ccc (kernel: the blocker carries the dependency edge),
|
|
38
|
+
# so forge-ccc is the blocked one — blocked_by resolves to forge-aaa by reverse scan.
|
|
39
|
+
aaa='{"id":"forge-aaa","title":"Feature A","status":"in_progress","assignee":"devone","updated_at":"2026-03-27T10:00:00Z","dependencies":["forge-ccc"]}'
|
|
40
|
+
bbb='{"id":"forge-bbb","title":"Feature B","status":"open","assignee":"devtwo","updated_at":"2026-03-25T10:00:00Z","dependencies":[]}'
|
|
41
|
+
ccc='{"id":"forge-ccc","title":"Feature C","status":"in_progress","assignee":"devone","updated_at":"2026-03-27T09:00:00Z","dependencies":[]}'
|
|
42
|
+
# Dotted sub-ID: the id flows through verbatim from JSON, never truncated.
|
|
43
|
+
m1n8='{"id":"forge-m1n8.6","title":"Sub-feature X (dotted ID)","status":"in_progress","assignee":"devthree","updated_at":"2026-03-27T08:00:00Z","dependencies":[]}'
|
|
44
|
+
|
|
45
|
+
if [[ "$args" == *"issue list"* ]]; then
|
|
46
|
+
if [[ "$args" == *"--status=open"* ]]; then emit "[$bbb]"
|
|
47
|
+
elif [[ "$args" == *"--status=in_progress"* ]]; then emit "[$aaa,$ccc,$m1n8]"
|
|
48
|
+
else emit "[]"; fi
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
exit 0
|
|
77
52
|
MOCK
|
|
78
|
-
chmod +x "$mock_dir/
|
|
53
|
+
chmod +x "$mock_dir/forge"
|
|
79
54
|
|
|
80
|
-
# ── Create mock
|
|
81
|
-
cat > "$mock_dir/
|
|
55
|
+
# ── Create mock forge with no issues ──────────────────────────────────────
|
|
56
|
+
cat > "$mock_dir/forge-empty" << 'MOCK'
|
|
82
57
|
#!/usr/bin/env bash
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
echo ""
|
|
86
|
-
;;
|
|
87
|
-
"list --status=open")
|
|
88
|
-
echo "○ forge-should-not-appear · Open fallback sentinel"
|
|
89
|
-
;;
|
|
90
|
-
"list --status=in_progress")
|
|
91
|
-
echo "◐ forge-should-not-appear-2 · In-progress fallback sentinel"
|
|
92
|
-
;;
|
|
93
|
-
esac
|
|
58
|
+
printf '{"data":{"issues":[]}}\n'
|
|
59
|
+
exit 0
|
|
94
60
|
MOCK
|
|
95
|
-
chmod +x "$mock_dir/
|
|
61
|
+
chmod +x "$mock_dir/forge-empty"
|
|
96
62
|
|
|
97
63
|
export GH_CMD="$mock_dir/gh"
|
|
98
|
-
export
|
|
64
|
+
export FORGE_CMD="$mock_dir/forge"
|
|
99
65
|
|
|
100
66
|
# Source the library under test
|
|
101
67
|
source "$SCRIPT_DIR/lib/workload.sh"
|
|
@@ -183,13 +149,13 @@ assert_not_contains "excludes devtwo" "Developer: devtwo" "$output"
|
|
|
183
149
|
echo ""
|
|
184
150
|
echo "── Test 4: No issues → No active work ──"
|
|
185
151
|
unset _GITHUB_USER_CACHE
|
|
186
|
-
export
|
|
152
|
+
export FORGE_CMD="$mock_dir/forge-empty"
|
|
187
153
|
rc=0
|
|
188
154
|
output="$(cmd_workload 2>/dev/null)" || rc=$?
|
|
189
155
|
assert_exit "exits 0" 0 "$rc"
|
|
190
156
|
assert_contains "shows no active work message" "No active work" "$output"
|
|
191
|
-
assert_not_contains "
|
|
192
|
-
export
|
|
157
|
+
assert_not_contains "empty issue lists yield no issue entries" "forge-" "$output"
|
|
158
|
+
export FORGE_CMD="$mock_dir/forge"
|
|
193
159
|
|
|
194
160
|
# ── Test 5: Stale assignment flagged (>48h) ──────────────────────────────
|
|
195
161
|
echo ""
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generate the static command manifest — scripts/gen-command-manifest.js
|
|
6
|
+
*
|
|
7
|
+
* Static bundlers (`bun build --compile`) need a static require graph, but the
|
|
8
|
+
* command registry discovers commands at runtime via `fs.readdirSync` +
|
|
9
|
+
* `require(filePath)`. This generator walks `lib/commands/*.js` and emits
|
|
10
|
+
* `lib/commands/_manifest.js`, a checked-in module that `require`s every command
|
|
11
|
+
* module by a *static* relative path. The registry loads the manifest as its
|
|
12
|
+
* fast, bundleable path and keeps readdir auto-discovery as a dev/extension
|
|
13
|
+
* fallback (see lib/commands/_registry.js).
|
|
14
|
+
*
|
|
15
|
+
* The output is deterministic (sorted by filename) and timestamp-free, and the
|
|
16
|
+
* file is only rewritten when its content actually changes, so no-op
|
|
17
|
+
* regeneration never dirties the working tree.
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* node scripts/gen-command-manifest.js # write if changed
|
|
21
|
+
* node scripts/gen-command-manifest.js --check # exit 1 if out of date
|
|
22
|
+
*
|
|
23
|
+
* @module scripts/gen-command-manifest
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const path = require('node:path');
|
|
28
|
+
|
|
29
|
+
const COMMANDS_DIR = path.resolve(__dirname, '..', 'lib', 'commands');
|
|
30
|
+
const MANIFEST_PATH = path.join(COMMANDS_DIR, '_manifest.js');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* List the command module filenames the manifest should enumerate.
|
|
34
|
+
*
|
|
35
|
+
* Mirrors the registry's own filter: `.js` files that do not start with `_`
|
|
36
|
+
* (underscore-prefixed files are registry internals, not commands), sorted for
|
|
37
|
+
* deterministic output.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} [commandsDir] - Directory to scan (defaults to lib/commands)
|
|
40
|
+
* @returns {string[]} Sorted command filenames (e.g. `['add.js', 'audit.js']`)
|
|
41
|
+
*/
|
|
42
|
+
function listCommandFiles(commandsDir = COMMANDS_DIR) {
|
|
43
|
+
return fs
|
|
44
|
+
.readdirSync(commandsDir)
|
|
45
|
+
.filter(f => f.endsWith('.js') && !f.startsWith('_'))
|
|
46
|
+
.sort();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Render the manifest source from a list of command filenames.
|
|
51
|
+
*
|
|
52
|
+
* @param {string[]} files - Sorted command filenames
|
|
53
|
+
* @returns {string} Full source text for lib/commands/_manifest.js
|
|
54
|
+
*/
|
|
55
|
+
function renderManifest(files) {
|
|
56
|
+
const header = [
|
|
57
|
+
'/**',
|
|
58
|
+
' * Static Command Manifest — GENERATED FILE, DO NOT EDIT.',
|
|
59
|
+
' *',
|
|
60
|
+
' * Regenerate with: node scripts/gen-command-manifest.js',
|
|
61
|
+
' * Drift is enforced by test/structural/command-manifest-drift.test.js.',
|
|
62
|
+
' *',
|
|
63
|
+
' * This module `require`s every command by a static relative path so',
|
|
64
|
+
' * `bun build --compile` can statically bundle the command graph. The registry',
|
|
65
|
+
' * (lib/commands/_registry.js) consumes `commands` as its fast, bundleable path',
|
|
66
|
+
' * and falls back to `fs.readdirSync` auto-discovery for dev/extension commands.',
|
|
67
|
+
' *',
|
|
68
|
+
' * @module commands/_manifest',
|
|
69
|
+
' */',
|
|
70
|
+
'',
|
|
71
|
+
"'use strict';",
|
|
72
|
+
'',
|
|
73
|
+
'/**',
|
|
74
|
+
' * @typedef {Object} ManifestEntry',
|
|
75
|
+
' * @property {string} file - Command filename (e.g. `status.js`)',
|
|
76
|
+
' * @property {import("./_registry").CommandModule} module - The required command module',
|
|
77
|
+
' */',
|
|
78
|
+
'',
|
|
79
|
+
'/** @type {ManifestEntry[]} */',
|
|
80
|
+
'const commands = [',
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const entries = files.map(file => {
|
|
84
|
+
const modPath = `./${file.replace(/\.js$/, '')}`;
|
|
85
|
+
return ` { file: ${JSON.stringify(file)}, module: require(${JSON.stringify(modPath)}) },`;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const footer = [
|
|
89
|
+
'];',
|
|
90
|
+
'',
|
|
91
|
+
'module.exports = {',
|
|
92
|
+
' // Absolute path of the canonical commands directory this manifest describes.',
|
|
93
|
+
' // The registry applies the manifest only when asked to load this exact dir.',
|
|
94
|
+
' dir: __dirname,',
|
|
95
|
+
' commands,',
|
|
96
|
+
'};',
|
|
97
|
+
'',
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
return [...header, ...entries, ...footer].join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Generate (or check) the manifest.
|
|
105
|
+
*
|
|
106
|
+
* @param {{ check?: boolean, commandsDir?: string, manifestPath?: string }} [opts]
|
|
107
|
+
* @returns {{ changed: boolean, files: string[], path: string }}
|
|
108
|
+
*/
|
|
109
|
+
function generate(opts = {}) {
|
|
110
|
+
const commandsDir = opts.commandsDir ?? COMMANDS_DIR;
|
|
111
|
+
const manifestPath = opts.manifestPath ?? MANIFEST_PATH;
|
|
112
|
+
const files = listCommandFiles(commandsDir);
|
|
113
|
+
const next = renderManifest(files);
|
|
114
|
+
|
|
115
|
+
const current = fs.existsSync(manifestPath)
|
|
116
|
+
? fs.readFileSync(manifestPath, 'utf8')
|
|
117
|
+
: null;
|
|
118
|
+
|
|
119
|
+
const changed = current !== next;
|
|
120
|
+
|
|
121
|
+
if (changed && !opts.check) {
|
|
122
|
+
fs.writeFileSync(manifestPath, next, 'utf8');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { changed, files, path: manifestPath };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { listCommandFiles, renderManifest, generate, COMMANDS_DIR, MANIFEST_PATH };
|
|
129
|
+
|
|
130
|
+
// CLI entry point
|
|
131
|
+
if (require.main === module) {
|
|
132
|
+
const check = process.argv.includes('--check');
|
|
133
|
+
const result = generate({ check });
|
|
134
|
+
|
|
135
|
+
if (check && result.changed) {
|
|
136
|
+
console.error(
|
|
137
|
+
'[gen-command-manifest] lib/commands/_manifest.js is out of date.\n' +
|
|
138
|
+
'Run: node scripts/gen-command-manifest.js'
|
|
139
|
+
);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (result.changed) {
|
|
144
|
+
console.log(
|
|
145
|
+
`[gen-command-manifest] wrote ${path.relative(process.cwd(), result.path)} ` +
|
|
146
|
+
`(${result.files.length} commands)`
|
|
147
|
+
);
|
|
148
|
+
} else {
|
|
149
|
+
console.log(
|
|
150
|
+
`[gen-command-manifest] up to date (${result.files.length} commands)`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Generate lib/embedded-assets.generated.mjs — a Bun-embeddable manifest that
|
|
2
|
+
// statically imports every runtime asset (with { type: "file" }) and maps its
|
|
3
|
+
// real relative POSIX path to the embedded-file path Bun resolves at compile time.
|
|
4
|
+
//
|
|
5
|
+
// WHY a generated per-file map instead of `bun build --compile ... skills/**`:
|
|
6
|
+
// Bun's `embeddedFiles` exposes only a HASHED, directory-less `blob.name`
|
|
7
|
+
// (e.g. `SKILL-a1b2c3.md`), so every skill's `SKILL.md` would collide and the
|
|
8
|
+
// tree could not be reconstructed. A generated map keyed by the true relative
|
|
9
|
+
// path preserves the directory structure exactly; each value is that specific
|
|
10
|
+
// file's `/$bunfs/…` path, so `Bun.file(value)` / `fs` reads the right bytes.
|
|
11
|
+
//
|
|
12
|
+
// The output is gitignored and regenerated by the `build:binary` script right
|
|
13
|
+
// before `bun build --compile`, so it can never drift from the source tree. The
|
|
14
|
+
// completeness test (test/embedded-assets-drift.test.js) walks the same roots and
|
|
15
|
+
// asserts the embed set == the on-disk asset set, both ways.
|
|
16
|
+
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import crypto from 'node:crypto';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import pkgRoot from '../lib/package-root.js';
|
|
22
|
+
|
|
23
|
+
const { ASSET_ROOTS } = pkgRoot;
|
|
24
|
+
|
|
25
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
27
|
+
const OUT_FILE = path.join(REPO_ROOT, 'lib', 'embedded-assets.generated.mjs');
|
|
28
|
+
|
|
29
|
+
// Deterministic, locale-INDEPENDENT codepoint comparator. Locale-default sort would
|
|
30
|
+
// diverge by machine locale and produce different embed bytes across the step-3
|
|
31
|
+
// cross-compile targets, so sorting is pinned to codepoint order everywhere.
|
|
32
|
+
const byteCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
33
|
+
|
|
34
|
+
/** True when the asset needs the executable bit restored on extraction (files-only embedding drops mode). */
|
|
35
|
+
export function isExecutableAsset(relPosixPath) {
|
|
36
|
+
return relPosixPath.endsWith('.sh') || relPosixPath.startsWith('.forge/hooks/');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Recursively list files under an absolute path, returning repo-relative posix paths. Symlinks are skipped (materialize content, never embed links). */
|
|
40
|
+
function walk(absPath, relPath, out) {
|
|
41
|
+
const stat = fs.lstatSync(absPath);
|
|
42
|
+
if (stat.isSymbolicLink()) return;
|
|
43
|
+
if (stat.isDirectory()) {
|
|
44
|
+
for (const entry of fs.readdirSync(absPath).sort(byteCompare)) {
|
|
45
|
+
walk(path.join(absPath, entry), `${relPath}/${entry}`, out);
|
|
46
|
+
}
|
|
47
|
+
} else if (stat.isFile()) {
|
|
48
|
+
out.push(relPath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Collect every embeddable asset as a sorted list of repo-relative posix paths. */
|
|
53
|
+
export function collectAssetFiles(repoRoot = REPO_ROOT, roots = ASSET_ROOTS) {
|
|
54
|
+
const files = [];
|
|
55
|
+
for (const root of roots) {
|
|
56
|
+
const abs = path.join(repoRoot, root);
|
|
57
|
+
if (!fs.existsSync(abs)) {
|
|
58
|
+
// Fail CLOSED: a configured asset root that is absent would silently ship an
|
|
59
|
+
// incomplete manifest. Error instead so the build stops loudly.
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Configured ASSET_ROOT does not exist: ${root} (looked in ${abs}). ` +
|
|
62
|
+
'Fix ASSET_ROOTS in lib/package-root.js or restore the missing path.'
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
walk(abs, root.split(path.sep).join('/'), files);
|
|
66
|
+
}
|
|
67
|
+
return [...new Set(files)].sort(byteCompare);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Content fingerprint over the asset SET: sha256 of each relpath + its bytes, in
|
|
72
|
+
* sorted order. Emitted into the manifest so the runtime cache dir identity
|
|
73
|
+
* changes whenever any asset content or the set changes (invalidates stale
|
|
74
|
+
* same-version extractions). Deterministic across machines.
|
|
75
|
+
*/
|
|
76
|
+
export function assetFingerprint(repoRoot = REPO_ROOT, files = collectAssetFiles(repoRoot)) {
|
|
77
|
+
const h = crypto.createHash('sha256');
|
|
78
|
+
for (const rel of files) {
|
|
79
|
+
h.update(rel);
|
|
80
|
+
h.update('\0');
|
|
81
|
+
h.update(fs.readFileSync(path.join(repoRoot, ...rel.split('/'))));
|
|
82
|
+
}
|
|
83
|
+
return h.digest('hex');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function render(files, fingerprint) {
|
|
87
|
+
const imports = [];
|
|
88
|
+
const entries = [];
|
|
89
|
+
const execEntries = [];
|
|
90
|
+
files.forEach((rel, i) => {
|
|
91
|
+
const ident = `a${i}`;
|
|
92
|
+
// Specifier is relative to lib/embedded-assets.generated.mjs → prefix `../`.
|
|
93
|
+
const specifier = JSON.stringify(`../${rel}`);
|
|
94
|
+
imports.push(`import ${ident} from ${specifier} with { type: "file" };`);
|
|
95
|
+
entries.push(` ${JSON.stringify(rel)}: ${ident},`);
|
|
96
|
+
if (isExecutableAsset(rel)) execEntries.push(` ${JSON.stringify(rel)},`);
|
|
97
|
+
});
|
|
98
|
+
return [
|
|
99
|
+
'// AUTO-GENERATED by scripts/gen-embedded-assets.mjs — DO NOT EDIT.',
|
|
100
|
+
'// Gitignored; regenerated before every `bun build --compile`.',
|
|
101
|
+
...imports,
|
|
102
|
+
'',
|
|
103
|
+
'export const EMBEDDED_ASSETS = {',
|
|
104
|
+
...entries,
|
|
105
|
+
'};',
|
|
106
|
+
'',
|
|
107
|
+
'export const EXECUTABLE_ASSETS = [',
|
|
108
|
+
...execEntries,
|
|
109
|
+
'];',
|
|
110
|
+
'',
|
|
111
|
+
`export const ASSET_FINGERPRINT = ${JSON.stringify(fingerprint)};`,
|
|
112
|
+
'',
|
|
113
|
+
].join('\n');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function main() {
|
|
117
|
+
const files = collectAssetFiles();
|
|
118
|
+
const fingerprint = assetFingerprint(REPO_ROOT, files);
|
|
119
|
+
fs.mkdirSync(path.dirname(OUT_FILE), { recursive: true });
|
|
120
|
+
fs.writeFileSync(OUT_FILE, render(files, fingerprint), 'utf8');
|
|
121
|
+
const execCount = files.filter(isExecutableAsset).length;
|
|
122
|
+
console.log(
|
|
123
|
+
`Embedded ${files.length} asset file(s) (${execCount} executable), fingerprint ${fingerprint.slice(0, 16)} → ${path.relative(REPO_ROOT, OUT_FILE)}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (import.meta.main || process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
128
|
+
main();
|
|
129
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Forge single-binary installer for Windows (x64).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Downloads the forge-windows-x64.exe binary from the GitHub Release, installs it
|
|
7
|
+
to %LOCALAPPDATA%\Programs\forge\forge.exe, and prints a PATH note plus the next
|
|
8
|
+
step. The binary bundles Forge's JavaScript but NOT its external prerequisites:
|
|
9
|
+
git, gh (GitHub CLI) and Git Bash must still be installed separately.
|
|
10
|
+
|
|
11
|
+
.PARAMETER Version
|
|
12
|
+
Release tag to install (default: latest), e.g. v1.2.3.
|
|
13
|
+
|
|
14
|
+
.PARAMETER Dir
|
|
15
|
+
Install directory (default: $env:LOCALAPPDATA\Programs\forge).
|
|
16
|
+
|
|
17
|
+
.PARAMETER PrintAsset
|
|
18
|
+
Print the resolved asset name and exit (used for verification/testing).
|
|
19
|
+
|
|
20
|
+
.EXAMPLE
|
|
21
|
+
irm https://raw.githubusercontent.com/harshanandak/forge/master/scripts/install.ps1 | iex
|
|
22
|
+
|
|
23
|
+
.EXAMPLE
|
|
24
|
+
# Pin a version (download the script, then run it with an argument):
|
|
25
|
+
$s = irm https://raw.githubusercontent.com/harshanandak/forge/master/scripts/install.ps1
|
|
26
|
+
& ([scriptblock]::Create($s)) -Version v1.2.3
|
|
27
|
+
#>
|
|
28
|
+
[CmdletBinding()]
|
|
29
|
+
param(
|
|
30
|
+
[string]$Version = 'latest',
|
|
31
|
+
[string]$Dir = (Join-Path $env:LOCALAPPDATA 'Programs\forge'),
|
|
32
|
+
[switch]$PrintAsset,
|
|
33
|
+
[switch]$SkipChecksum
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
$ErrorActionPreference = 'Stop'
|
|
37
|
+
Set-StrictMode -Version Latest
|
|
38
|
+
|
|
39
|
+
$Repo = if ($env:FORGE_REPO) { $env:FORGE_REPO } else { 'harshanandak/forge' }
|
|
40
|
+
if ($Version -eq 'latest' -and $env:FORGE_VERSION) { $Version = $env:FORGE_VERSION }
|
|
41
|
+
|
|
42
|
+
# Canonical asset-name mapping for Windows - the only target this script installs.
|
|
43
|
+
# Mirrors scripts/lib/release-asset.mjs (forge-<os>-<arch>[.exe]).
|
|
44
|
+
$Arch = if ($env:FORGE_ARCH) { $env:FORGE_ARCH } else { 'x64' }
|
|
45
|
+
if ($Arch -ne 'x64') {
|
|
46
|
+
throw "forge-install: unsupported architecture '$Arch'. Only windows-x64 is published."
|
|
47
|
+
}
|
|
48
|
+
$Asset = "forge-windows-$Arch.exe"
|
|
49
|
+
|
|
50
|
+
if ($PrintAsset) {
|
|
51
|
+
Write-Output $Asset
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Resolve version + URL. The /releases/latest/download/<asset> path redirects to
|
|
56
|
+
# the newest release, so no JSON API call is needed to download.
|
|
57
|
+
if ($Version -eq 'latest') {
|
|
58
|
+
$Url = "https://github.com/$Repo/releases/latest/download/$Asset"
|
|
59
|
+
try {
|
|
60
|
+
$rel = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest" `
|
|
61
|
+
-Headers @{ 'User-Agent' = 'forge-install' }
|
|
62
|
+
$ResolvedVersion = $rel.tag_name
|
|
63
|
+
} catch {
|
|
64
|
+
$ResolvedVersion = 'latest'
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
$Url = "https://github.com/$Repo/releases/download/$Version/$Asset"
|
|
68
|
+
$ResolvedVersion = $Version
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Write-Host "forge-install: platform windows / $Arch -> asset $Asset"
|
|
72
|
+
Write-Host "forge-install: downloading $Asset ($ResolvedVersion)"
|
|
73
|
+
|
|
74
|
+
New-Item -ItemType Directory -Force -Path $Dir | Out-Null
|
|
75
|
+
$Dest = Join-Path $Dir 'forge.exe'
|
|
76
|
+
$Tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("forge-install-" + [System.Guid]::NewGuid().ToString('N') + ".exe")
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
Invoke-WebRequest -Uri $Url -OutFile $Tmp -UseBasicParsing -TimeoutSec 300
|
|
80
|
+
} catch {
|
|
81
|
+
throw "forge-install: download failed from $Url - is the release published for windows-x64? ($_)"
|
|
82
|
+
}
|
|
83
|
+
if (-not (Test-Path $Tmp) -or (Get-Item $Tmp).Length -eq 0) {
|
|
84
|
+
throw "forge-install: downloaded file is empty - asset '$Asset' may not exist in release '$ResolvedVersion'."
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Integrity verification (SHA-256) BEFORE installing. A mismatch is fatal
|
|
88
|
+
# (corrupted or substituted download). If the manifest cannot be fetched we warn
|
|
89
|
+
# and continue; pass -SkipChecksum or set FORGE_SKIP_CHECKSUM=1 to bypass.
|
|
90
|
+
if ($SkipChecksum -or $env:FORGE_SKIP_CHECKSUM -eq '1') {
|
|
91
|
+
Write-Host "forge-install: skipping integrity check (requested)."
|
|
92
|
+
} else {
|
|
93
|
+
$SumsUrl = $Url.Substring(0, $Url.LastIndexOf('/') + 1) + 'checksums.txt'
|
|
94
|
+
$SumsTmp = Join-Path ([System.IO.Path]::GetTempPath()) ("forge-install-" + [System.Guid]::NewGuid().ToString('N') + ".txt")
|
|
95
|
+
$expected = $null
|
|
96
|
+
$manifestFetched = $false
|
|
97
|
+
try {
|
|
98
|
+
Invoke-WebRequest -Uri $SumsUrl -OutFile $SumsTmp -UseBasicParsing -TimeoutSec 30
|
|
99
|
+
$manifestFetched = $true
|
|
100
|
+
foreach ($line in (Get-Content $SumsTmp)) {
|
|
101
|
+
# Lines are "<hash> <name>" (text) or "<hash> *<name>" (binary).
|
|
102
|
+
if ($line -match '^\s*([0-9a-fA-F]{64})\s+\*?(.+?)\s*$' -and $Matches[2] -eq $Asset) {
|
|
103
|
+
$expected = $Matches[1].ToLower()
|
|
104
|
+
break
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
Write-Host "forge-install: could not download checksums.txt - skipping integrity check."
|
|
109
|
+
} finally {
|
|
110
|
+
if (Test-Path $SumsTmp) { Remove-Item -Force $SumsTmp }
|
|
111
|
+
}
|
|
112
|
+
if ($expected) {
|
|
113
|
+
$actual = (Get-FileHash -Algorithm SHA256 -Path $Tmp).Hash.ToLower()
|
|
114
|
+
if ($actual -ne $expected) {
|
|
115
|
+
Remove-Item -Force $Tmp
|
|
116
|
+
throw "forge-install: checksum mismatch for $Asset`n expected: $expected`n actual: $actual`nThe download may be corrupted or tampered with - aborting."
|
|
117
|
+
}
|
|
118
|
+
Write-Host "forge-install: checksum verified (sha256)."
|
|
119
|
+
} elseif ($manifestFetched) {
|
|
120
|
+
Write-Host "forge-install: $Asset not listed in checksums.txt - skipping integrity check."
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
Move-Item -Force -Path $Tmp -Destination $Dest
|
|
125
|
+
|
|
126
|
+
Write-Host ""
|
|
127
|
+
Write-Host "forge-install: installed forge $ResolvedVersion -> $Dest"
|
|
128
|
+
|
|
129
|
+
# PATH note: check the user PATH, not just the current process.
|
|
130
|
+
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
131
|
+
if (-not ($userPath -and ($userPath -split ';' | Where-Object { $_ -eq $Dir }))) {
|
|
132
|
+
Write-Host ""
|
|
133
|
+
Write-Host "$Dir is not on your PATH. Add it (new terminals will pick it up):"
|
|
134
|
+
Write-Host " [Environment]::SetEnvironmentVariable('Path', `"$Dir;`$([Environment]::GetEnvironmentVariable('Path','User'))`", 'User')"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
Write-Host ""
|
|
138
|
+
Write-Host "Next: run 'forge setup' in a git repo to wire up Forge."
|
|
139
|
+
Write-Host "Note: git, gh (GitHub CLI) and Git Bash must be installed separately - the binary bundles Forge, not those tools."
|