forge-workflow 0.0.10 → 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 +150 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -118
- 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 +461 -489
- package/docs/INDEX.md +93 -0
- package/docs/PROJECT_DESIGN.md +685 -0
- package/docs/architecture/index.md +66 -0
- package/docs/architecture/notes/README.md +35 -0
- package/docs/architecture/subsystems/README.md +46 -0
- package/docs/forge/TOOLCHAIN.md +670 -0
- package/docs/forge/VALIDATION.md +82 -0
- package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
- package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
- package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
- package/docs/guides/GREPTILE_SETUP.md +46 -0
- package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
- package/docs/guides/MIGRATION.md +56 -0
- package/docs/guides/SETUP.md +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/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
- package/docs/reference/VALIDATION.md +82 -0
- package/docs/reference/agent-permissions.md +169 -0
- package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
- package/docs/reference/control-plane-guarantees.md +125 -0
- package/docs/reference/dependency-chain.md +331 -0
- package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
- package/docs/reference/forge-kernel-schema.md +72 -0
- package/docs/reference/kernel-conflict-evaluators.md +27 -0
- package/docs/reference/patch-md-format.md +77 -0
- package/docs/reference/protected-state-surfaces.md +59 -0
- package/docs/reference/shepherd.md +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 +121 -0
- package/lib/beads-sync-scaffold.js +25 -101
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +741 -77
- 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 +17 -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 +0 -1
- 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 +838 -972
- 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 +158 -21
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +4 -1
- package/lib/commands/test.js +43 -27
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +43 -18
- package/lib/commands/worktree.js +307 -100
- 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 +2 -2
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +35 -9
- package/lib/doc-gate/declaration.js +177 -0
- package/lib/doc-gate/detect.js +289 -0
- package/lib/doc-gate/gate.js +375 -0
- package/lib/doc-gate/okf-config.js +128 -0
- package/lib/doc-gate/okf.js +429 -0
- package/lib/docs-command.js +1161 -6
- package/lib/forge-issues.js +382 -11
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +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/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 +3 -2
- package/lib/lefthook-wiring.js +413 -0
- package/lib/mcp-config-renderer.js +288 -0
- package/lib/memory/graphiti-mcp.js +106 -0
- package/lib/memory/router.js +387 -0
- package/lib/memory/typed-api.js +102 -0
- package/lib/memory-digest.js +195 -0
- package/lib/merge-rules.js +395 -0
- package/lib/migrate-dry-run.js +466 -0
- package/lib/orientation.js +863 -0
- package/lib/package-manager-remediation.js +103 -0
- package/lib/package-root.js +381 -0
- package/lib/patch-intent.js +890 -0
- package/lib/plugin-catalog.js +3 -4
- package/lib/plugin-manager.js +0 -5
- package/lib/pr-bundle.js +186 -0
- package/lib/pr-monitor/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 +99 -497
- 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 +241 -20
- package/lib/safety-config-renderer.js +268 -0
- package/lib/setup-action-log.js +1 -7
- package/lib/setup.js +27 -65
- package/lib/shell-utils.js +76 -6
- package/lib/skills-sync.js +330 -0
- package/lib/smart-status/scoring.js +17 -3
- package/lib/status/beads-snapshot.js +45 -2
- package/lib/status/presenter.js +169 -18
- package/lib/status/snapshot.js +186 -0
- package/lib/sync-backend.js +202 -0
- package/lib/untrusted-content.js +52 -0
- package/lib/upgrade-safety.js +199 -0
- package/lib/workflow/enforce-stage.js +296 -47
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +11 -22
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +37 -35
- package/rules/documentation.md +19 -0
- package/rules/kernel-tracking.md +26 -0
- package/rules/security.md +22 -0
- package/rules/tdd.md +20 -0
- package/rules/workflow.md +27 -0
- package/scripts/auto-backing-issue.js +47 -0
- package/scripts/beads-context.sh +81 -57
- package/scripts/beads-upgrade-smoke.sh +24 -3
- package/scripts/bootstrap-windows-tools.sh +78 -0
- package/scripts/branch-protection.js +2 -3
- package/scripts/check-agents.js +34 -137
- package/scripts/commitlint.js +3 -1
- package/scripts/conflict-detect.sh +3 -0
- package/scripts/dep-guard.sh +22 -3
- package/scripts/file-index.sh +3 -0
- package/scripts/forge-team/lib/claim.sh +34 -18
- package/scripts/forge-team/lib/dashboard.sh +61 -86
- package/scripts/forge-team/lib/epic.sh +99 -263
- package/scripts/forge-team/lib/hooks.sh +26 -28
- package/scripts/forge-team/lib/identity.sh +4 -4
- package/scripts/forge-team/lib/sync-github.sh +49 -84
- package/scripts/forge-team/lib/verify.sh +93 -83
- package/scripts/forge-team/lib/workload.sh +41 -65
- package/scripts/forge-team/tests/claim.test.sh +25 -19
- package/scripts/forge-team/tests/dashboard.test.sh +31 -46
- package/scripts/forge-team/tests/epic.test.sh +52 -71
- package/scripts/forge-team/tests/hooks.test.sh +38 -50
- package/scripts/forge-team/tests/identity.test.sh +3 -3
- package/scripts/forge-team/tests/integration.test.sh +44 -66
- package/scripts/forge-team/tests/sync-github.test.sh +50 -83
- package/scripts/forge-team/tests/verify.test.sh +37 -46
- package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
- package/scripts/forge-team/tests/workload.test.sh +32 -66
- package/scripts/gen-command-manifest.js +153 -0
- package/scripts/gen-embedded-assets.mjs +129 -0
- package/scripts/install.ps1 +139 -0
- package/scripts/install.sh +268 -0
- package/scripts/lib/release-asset.mjs +84 -0
- package/scripts/parity-check.mjs +145 -0
- package/scripts/parity-check.test.mjs +58 -0
- package/scripts/pin-agentic-workflow-images.js +112 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +21 -94
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status.sh +60 -57
- package/scripts/spikes/config-race-bench.js +111 -0
- package/scripts/spikes/harness-capability-matrix.js +13 -0
- package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
- package/scripts/spikes/protected-path-manifest.js +20 -0
- package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
- package/scripts/sync-agent-skills.js +62 -0
- package/scripts/sync-utils.sh +3 -0
- package/scripts/test-ci-shard.js +13 -6
- package/scripts/test.js +95 -12
- package/skills/claim-safety/SKILL.md +102 -0
- package/skills/claim-safety/evals/evals.json +46 -0
- package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
- package/skills/dev/evals/evals.json +50 -0
- package/skills/hermes-forge/SKILL.md +185 -0
- package/skills/hermes-forge/evals/evals.json +46 -0
- package/skills/issue-basics/SKILL.md +111 -0
- package/skills/issue-basics/evals/evals.json +46 -0
- package/skills/kernel/SKILL.md +166 -0
- package/skills/kernel/evals/evals.json +50 -0
- package/skills/memory/SKILL.md +102 -0
- package/skills/parallel-deep-research/SKILL.md +14 -11
- package/skills/parallel-deep-research/evals/evals.json +11 -27
- package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
- package/skills/plan/evals/evals.json +42 -0
- package/skills/research/SKILL.md +195 -0
- package/skills/research/evals/evals.json +42 -0
- package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
- package/skills/review/evals/evals.json +42 -0
- package/skills/rollback/SKILL.md +110 -0
- package/skills/rollback/evals/evals.json +46 -0
- package/skills/rollback/references/methods.md +204 -0
- package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
- package/skills/shepherd/SKILL.md +66 -0
- package/skills/shepherd/evals/evals.json +42 -0
- package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
- package/skills/ship/evals/evals.json +42 -0
- package/skills/smith/SKILL.md +142 -0
- package/skills/smith/evals/evals.json +46 -0
- package/skills/smith/references/autonomy-and-gates.md +94 -0
- package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
- package/skills/sonarcloud/evals/evals.json +46 -0
- package/skills/sonarcloud-analysis/SKILL.md +18 -13
- package/skills/sonarcloud-analysis/evals/evals.json +11 -15
- package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
- package/skills/status/evals/evals.json +50 -0
- package/skills/triage-ready/SKILL.md +121 -0
- package/skills/triage-ready/evals/evals.json +42 -0
- package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
- package/skills/validate/evals/evals.json +42 -0
- package/skills/verify/SKILL.md +299 -0
- package/skills/verify/evals/evals.json +50 -0
- package/.claude/commands/dev.md +0 -345
- package/.claude/commands/plan.md +0 -566
- package/.claude/commands/premerge.md +0 -186
- package/.claude/commands/research.md +0 -42
- package/.claude/commands/review.md +0 -451
- package/.claude/commands/rollback.md +0 -721
- package/.claude/commands/ship.md +0 -213
- package/.claude/commands/sonarcloud.md +0 -152
- package/.claude/commands/status.md +0 -90
- package/.claude/commands/validate.md +0 -288
- package/.claude/commands/verify.md +0 -269
- package/.claude/rules/workflow.md +0 -121
- package/.cline/workflows/dev.md +0 -342
- package/.cline/workflows/plan.md +0 -563
- package/.cline/workflows/premerge.md +0 -183
- package/.cline/workflows/research.md +0 -39
- package/.cline/workflows/review.md +0 -448
- package/.cline/workflows/rollback.md +0 -718
- package/.cline/workflows/ship.md +0 -210
- package/.cline/workflows/sonarcloud.md +0 -146
- package/.cline/workflows/status.md +0 -87
- package/.cline/workflows/validate.md +0 -285
- package/.cline/workflows/verify.md +0 -266
- package/.codex/config.toml +0 -11
- package/.codex/skills/dev/SKILL.md +0 -345
- package/.codex/skills/plan/SKILL.md +0 -566
- package/.codex/skills/premerge/SKILL.md +0 -186
- package/.codex/skills/research/SKILL.md +0 -42
- package/.codex/skills/review/SKILL.md +0 -451
- package/.codex/skills/rollback/SKILL.md +0 -721
- package/.codex/skills/ship/SKILL.md +0 -213
- package/.codex/skills/sonarcloud/SKILL.md +0 -149
- package/.codex/skills/status/SKILL.md +0 -90
- package/.codex/skills/validate/SKILL.md +0 -288
- package/.codex/skills/verify/SKILL.md +0 -269
- package/.cursor/commands/dev.md +0 -342
- package/.cursor/commands/plan.md +0 -563
- package/.cursor/commands/premerge.md +0 -183
- package/.cursor/commands/research.md +0 -39
- package/.cursor/commands/review.md +0 -448
- package/.cursor/commands/ship.md +0 -210
- package/.cursor/commands/sonarcloud.md +0 -146
- package/.cursor/commands/status.md +0 -87
- package/.cursor/commands/validate.md +0 -285
- package/.cursor/commands/verify.md +0 -266
- package/.cursorrules +0 -149
- package/.github/prompts/premerge.prompt.md +0 -188
- package/.github/prompts/research.prompt.md +0 -44
- package/.github/prompts/rollback.prompt.md +0 -723
- package/.github/prompts/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -89
- package/.github/workflows/github-to-beads.yml +0 -100
- package/.kilocode/workflows/dev.md +0 -346
- package/.kilocode/workflows/plan.md +0 -567
- package/.kilocode/workflows/premerge.md +0 -187
- package/.kilocode/workflows/research.md +0 -43
- package/.kilocode/workflows/review.md +0 -452
- package/.kilocode/workflows/rollback.md +0 -722
- package/.kilocode/workflows/ship.md +0 -214
- package/.kilocode/workflows/sonarcloud.md +0 -150
- package/.kilocode/workflows/status.md +0 -91
- package/.kilocode/workflows/validate.md +0 -289
- package/.kilocode/workflows/verify.md +0 -270
- package/.opencode/commands/dev.md +0 -345
- package/.opencode/commands/plan.md +0 -566
- package/.opencode/commands/premerge.md +0 -186
- package/.opencode/commands/research.md +0 -42
- package/.opencode/commands/review.md +0 -451
- package/.opencode/commands/rollback.md +0 -721
- package/.opencode/commands/ship.md +0 -213
- package/.opencode/commands/sonarcloud.md +0 -149
- package/.opencode/commands/status.md +0 -90
- package/.opencode/commands/validate.md +0 -288
- package/.opencode/commands/verify.md +0 -269
- package/.roo/commands/dev.md +0 -346
- package/.roo/commands/plan.md +0 -567
- package/.roo/commands/premerge.md +0 -187
- package/.roo/commands/research.md +0 -43
- package/.roo/commands/review.md +0 -452
- package/.roo/commands/rollback.md +0 -722
- package/.roo/commands/ship.md +0 -214
- package/.roo/commands/sonarcloud.md +0 -150
- package/.roo/commands/status.md +0 -91
- package/.roo/commands/validate.md +0 -289
- package/.roo/commands/verify.md +0 -270
- package/docs/BEADS_GITHUB_SYNC.md +0 -281
- package/docs/GREPTILE_SETUP.md +0 -400
- package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
- package/docs/SETUP.md +0 -663
- package/docs/VALIDATION.md +0 -363
- package/lib/agents/cline.plugin.json +0 -29
- package/lib/agents/copilot.plugin.json +0 -24
- package/lib/agents/kilocode.plugin.json +0 -22
- package/lib/agents/opencode.plugin.json +0 -23
- package/lib/agents/roo.plugin.json +0 -30
- package/lib/beads-bootstrap.js +0 -225
- package/lib/beads-health-check.js +0 -188
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -584
- package/scripts/github-beads-sync/comment.mjs +0 -64
- package/scripts/github-beads-sync/config.mjs +0 -148
- package/scripts/github-beads-sync/github-api.mjs +0 -131
- package/scripts/github-beads-sync/index.mjs +0 -356
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -132
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
- package/scripts/github-beads-sync/run-bd.mjs +0 -161
- package/scripts/github-beads-sync/sanitize.mjs +0 -121
- package/scripts/github-beads-sync.config.json +0 -26
- package/scripts/sync-commands.js +0 -600
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
const { execFileSync } = require('node:child_process');
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
|
|
5
|
+
const VERDICT_LABELS = {
|
|
6
|
+
PASS: 'good',
|
|
7
|
+
FAIL: 'bad',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const REVIEWER_ROLES = new Set(['spec_reviewer', 'quality_reviewer']);
|
|
11
|
+
const SECRET_KEY_PATTERN = /(api(?:[_-]|\s)?key|authorization|credential|password|private(?:[_-]|\s)?key|secret|token)/i;
|
|
12
|
+
const SECRET_TEXT_KEY_PATTERN = String.raw`(?:api(?:[_-]|\s)?key|authorization|credential|password|private(?:[_-]|\s)?key|secret|token)`;
|
|
13
|
+
const SECRET_TEXT_PATTERNS = [
|
|
14
|
+
{
|
|
15
|
+
pattern: /\bBearer\s+\S+/gi,
|
|
16
|
+
replace: () => 'Bearer [REDACTED]',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
pattern: new RegExp(`(\\b${SECRET_TEXT_KEY_PATTERN}\\b\\s*=\\s*)([^&,\r\n]+)`, 'gi'),
|
|
20
|
+
replace: (_match, prefix) => `${prefix}[REDACTED]`,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
pattern: new RegExp(`(\\b${SECRET_TEXT_KEY_PATTERN}\\b\\s+is\\s+)([^&,\r\n]+)`, 'gi'),
|
|
24
|
+
replace: (_match, prefix) => `${prefix}[REDACTED]`,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
pattern: new RegExp(
|
|
28
|
+
`(\\b${SECRET_TEXT_KEY_PATTERN}\\b\\s+)(?!is\\b)([^&,\r\n]*?)(?=\\s+\\b${SECRET_TEXT_KEY_PATTERN}\\b\\s+|[&,\r\n]|$)`,
|
|
29
|
+
'gi'
|
|
30
|
+
),
|
|
31
|
+
replace: (_match, prefix) => `${prefix}[REDACTED]`,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
pattern: new RegExp(
|
|
35
|
+
`((?:"|')?${SECRET_TEXT_KEY_PATTERN}(?:"|')?\\s*:\\s*)(?:"[^"]*"|'[^']*'|[^,}\\]\r\n]+)`,
|
|
36
|
+
'gi'
|
|
37
|
+
),
|
|
38
|
+
replace: (_match, prefix) => `${prefix}"[REDACTED]"`,
|
|
39
|
+
},
|
|
40
|
+
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
function defaultRunCommand(command, args, options = {}) {
|
|
44
|
+
return execFileSync(command, args, {
|
|
45
|
+
cwd: options.cwd || process.cwd(),
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
input: options.input,
|
|
48
|
+
timeout: options.timeout || 120000,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function redactString(value) {
|
|
53
|
+
return SECRET_TEXT_PATTERNS.reduce(
|
|
54
|
+
(current, entry) => {
|
|
55
|
+
if (entry && typeof entry === 'object' && !(entry instanceof RegExp)) {
|
|
56
|
+
return current.replace(entry.pattern, entry.replace);
|
|
57
|
+
}
|
|
58
|
+
return current.replace(entry, () => '[REDACTED]');
|
|
59
|
+
},
|
|
60
|
+
value,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function redact(value, key = '') {
|
|
65
|
+
if (SECRET_KEY_PATTERN.test(key)) {
|
|
66
|
+
return '[REDACTED]';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof value === 'string') {
|
|
70
|
+
return redactString(value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return value.map(item => redact(item));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (value && typeof value === 'object') {
|
|
78
|
+
return Object.fromEntries(
|
|
79
|
+
Object.entries(value).map(([entryKey, entryValue]) => [entryKey, redact(entryValue, entryKey)]),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function asJsonString(value) {
|
|
87
|
+
if (typeof value === 'string') return redactString(value);
|
|
88
|
+
return JSON.stringify(redact(value));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function buildSubagentAuditPayload(event) {
|
|
92
|
+
if (!event || typeof event !== 'object') {
|
|
93
|
+
throw new TypeError('Audit event must be an object');
|
|
94
|
+
}
|
|
95
|
+
if (!event.command) {
|
|
96
|
+
throw new TypeError('Audit event command is required');
|
|
97
|
+
}
|
|
98
|
+
if (!event.role) {
|
|
99
|
+
throw new TypeError('Audit event role is required');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const metadata =
|
|
103
|
+
event.metadata && typeof event.metadata === 'object' && !Array.isArray(event.metadata)
|
|
104
|
+
? event.metadata
|
|
105
|
+
: {};
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
kind: 'llm_call',
|
|
109
|
+
command: event.command,
|
|
110
|
+
issueId: event.issueId,
|
|
111
|
+
role: event.role,
|
|
112
|
+
phase: event.phase,
|
|
113
|
+
taskId: event.taskId,
|
|
114
|
+
taskTitle: event.taskTitle,
|
|
115
|
+
model: event.model || 'unknown',
|
|
116
|
+
prompt: asJsonString({
|
|
117
|
+
command: event.command,
|
|
118
|
+
role: event.role,
|
|
119
|
+
phase: event.phase,
|
|
120
|
+
taskId: event.taskId,
|
|
121
|
+
taskTitle: event.taskTitle,
|
|
122
|
+
content: event.prompt || '',
|
|
123
|
+
}),
|
|
124
|
+
response: asJsonString({
|
|
125
|
+
command: event.command,
|
|
126
|
+
role: event.role,
|
|
127
|
+
verdict: event.verdict || 'UNKNOWN',
|
|
128
|
+
content: event.response || '',
|
|
129
|
+
}),
|
|
130
|
+
metadata: redact(metadata),
|
|
131
|
+
verdict: event.verdict,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function parseRecordId(output, { requireJson = false } = {}) {
|
|
136
|
+
if (!output) return null;
|
|
137
|
+
try {
|
|
138
|
+
const parsed = JSON.parse(output);
|
|
139
|
+
return typeof parsed.id === 'string' ? parsed.id : null;
|
|
140
|
+
} catch (_error) {
|
|
141
|
+
if (requireJson) return null;
|
|
142
|
+
const match = /\bint-[A-Za-z0-9_-]+\b/.exec(String(output));
|
|
143
|
+
return match ? match[0] : null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function hasAuditMetaJsonSupport(runCommand = defaultRunCommand, options = {}) {
|
|
148
|
+
const output = runCommand('bd', ['audit', 'record', '--help'], options);
|
|
149
|
+
return String(output).includes('--meta-json');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function writeFallbackMetadata(payload, entryId, options) {
|
|
153
|
+
if (!entryId || !payload.metadata || Object.keys(payload.metadata).length === 0) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const cwd = options.cwd || process.cwd();
|
|
158
|
+
const fsImpl = options.fs || fs;
|
|
159
|
+
const forgeDir = path.join(cwd, '.forge').replace(/\\/g, '/');
|
|
160
|
+
const logPath = path.join(forgeDir, 'log.jsonl').replace(/\\/g, '/');
|
|
161
|
+
const line = {
|
|
162
|
+
kind: 'forge.auditEvidence',
|
|
163
|
+
sourceOfTruth: 'beads',
|
|
164
|
+
beadsEntryId: entryId,
|
|
165
|
+
command: redact(payload.command),
|
|
166
|
+
role: redact(payload.role),
|
|
167
|
+
phase: redact(payload.phase),
|
|
168
|
+
taskId: redact(payload.taskId),
|
|
169
|
+
taskTitle: redact(payload.taskTitle),
|
|
170
|
+
metadata: redact(payload.metadata),
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
fsImpl.mkdirSync(forgeDir, { recursive: true });
|
|
175
|
+
fsImpl.appendFileSync(logPath, `${JSON.stringify(line)}\n`);
|
|
176
|
+
return { path: logPath, line };
|
|
177
|
+
} catch (error) {
|
|
178
|
+
return { path: logPath, line, skipped: true, error: error.message };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function recordSubagentAuditEvent(event, options = {}) {
|
|
183
|
+
const payload = buildSubagentAuditPayload(event);
|
|
184
|
+
const runCommand = options.runCommand || defaultRunCommand;
|
|
185
|
+
const metaJsonSupported =
|
|
186
|
+
typeof options.metaJsonSupported === 'boolean'
|
|
187
|
+
? options.metaJsonSupported
|
|
188
|
+
: hasAuditMetaJsonSupport(runCommand, { cwd: options.cwd || process.cwd() });
|
|
189
|
+
const args = [
|
|
190
|
+
'audit',
|
|
191
|
+
'record',
|
|
192
|
+
'--json',
|
|
193
|
+
'--kind',
|
|
194
|
+
'llm_call',
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
if (payload.issueId) {
|
|
198
|
+
args.push('--issue-id', payload.issueId);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
args.push(
|
|
202
|
+
'--model',
|
|
203
|
+
payload.model,
|
|
204
|
+
'--prompt',
|
|
205
|
+
payload.prompt,
|
|
206
|
+
'--response',
|
|
207
|
+
payload.response,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
if (metaJsonSupported && Object.keys(payload.metadata).length > 0) {
|
|
211
|
+
args.push('--meta-json', JSON.stringify(payload.metadata));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const output = runCommand('bd', args, { cwd: options.cwd || process.cwd() });
|
|
215
|
+
const entryId = parseRecordId(output, { requireJson: true });
|
|
216
|
+
const fallback = metaJsonSupported ? null : writeFallbackMetadata(payload, entryId, options);
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
success: Boolean(entryId),
|
|
220
|
+
entryId,
|
|
221
|
+
output,
|
|
222
|
+
payload,
|
|
223
|
+
fallback,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function labelSubagentAuditEvent(entryId, event, options = {}) {
|
|
228
|
+
const verdict = String(event?.verdict || '').toUpperCase();
|
|
229
|
+
const role = event?.role;
|
|
230
|
+
const label = VERDICT_LABELS[verdict];
|
|
231
|
+
|
|
232
|
+
if (!entryId || !REVIEWER_ROLES.has(role) || !label) {
|
|
233
|
+
return { skipped: true };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const runCommand = options.runCommand || defaultRunCommand;
|
|
237
|
+
const reason = `${role} verdict: ${verdict}`;
|
|
238
|
+
let output;
|
|
239
|
+
try {
|
|
240
|
+
output = runCommand('bd', [
|
|
241
|
+
'audit',
|
|
242
|
+
'label',
|
|
243
|
+
entryId,
|
|
244
|
+
'--json',
|
|
245
|
+
'--label',
|
|
246
|
+
label,
|
|
247
|
+
'--reason',
|
|
248
|
+
reason,
|
|
249
|
+
], { cwd: options.cwd || process.cwd() });
|
|
250
|
+
} catch (error) {
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
label,
|
|
254
|
+
error: error.message,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const labeledEntryId = parseRecordId(output, { requireJson: true });
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
success: labeledEntryId === entryId,
|
|
262
|
+
label,
|
|
263
|
+
entryId: labeledEntryId,
|
|
264
|
+
output,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function recordAndLabelSubagentAuditEvent(event, options = {}) {
|
|
269
|
+
const record = recordSubagentAuditEvent(event, options);
|
|
270
|
+
const label = labelSubagentAuditEvent(record.entryId, event, options);
|
|
271
|
+
return { record, label };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
module.exports = {
|
|
275
|
+
VERDICT_LABELS,
|
|
276
|
+
buildSubagentAuditPayload,
|
|
277
|
+
recordSubagentAuditEvent,
|
|
278
|
+
labelSubagentAuditEvent,
|
|
279
|
+
recordAndLabelSubagentAuditEvent,
|
|
280
|
+
hasAuditMetaJsonSupport,
|
|
281
|
+
redact,
|
|
282
|
+
};
|
package/lib/beads-setup.js
CHANGED
|
@@ -12,6 +12,13 @@ const path = require('node:path');
|
|
|
12
12
|
const { spawnSync } = require('node:child_process');
|
|
13
13
|
const { secureExecFileSync } = require('./shell-utils');
|
|
14
14
|
|
|
15
|
+
const BEADS_LOCAL_EXCLUDE_LINES = [
|
|
16
|
+
'# Forge local Beads state',
|
|
17
|
+
'.beads/',
|
|
18
|
+
'.dolt/',
|
|
19
|
+
'.beads-credential-key'
|
|
20
|
+
];
|
|
21
|
+
|
|
15
22
|
function isIgnorableFsError(error) {
|
|
16
23
|
return ['EACCES', 'ENOENT', 'ENOTDIR', 'EPERM'].includes(error?.code);
|
|
17
24
|
}
|
|
@@ -103,6 +110,10 @@ function writeBeadsGitignore(projectRoot) {
|
|
|
103
110
|
fs.mkdirSync(beadsDir, { recursive: true });
|
|
104
111
|
|
|
105
112
|
const gitignoreContent = [
|
|
113
|
+
'# Beads runtime, export, and backup state is local',
|
|
114
|
+
'*',
|
|
115
|
+
'!.gitignore',
|
|
116
|
+
'',
|
|
106
117
|
'# Dolt database files (binary, not suitable for git)',
|
|
107
118
|
'dolt/',
|
|
108
119
|
'*.db',
|
|
@@ -117,6 +128,108 @@ function writeBeadsGitignore(projectRoot) {
|
|
|
117
128
|
);
|
|
118
129
|
}
|
|
119
130
|
|
|
131
|
+
function resolveGitInfoExcludePath(projectRoot) {
|
|
132
|
+
try {
|
|
133
|
+
const gitExcludePath = secureExecFileSync(resolveCommand('git'), ['rev-parse', '--git-path', 'info/exclude'], {
|
|
134
|
+
cwd: projectRoot,
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
stdio: 'pipe'
|
|
137
|
+
}).trim();
|
|
138
|
+
|
|
139
|
+
if (gitExcludePath) {
|
|
140
|
+
return path.isAbsolute(gitExcludePath)
|
|
141
|
+
? gitExcludePath
|
|
142
|
+
: path.resolve(projectRoot, gitExcludePath);
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (!isRecoverableCommandError(error)) {
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return path.join(projectRoot, '.git', 'info', 'exclude');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function untrackExistingBeadsState(projectRoot) {
|
|
154
|
+
let tracked;
|
|
155
|
+
try {
|
|
156
|
+
tracked = secureExecFileSync(resolveCommand('git'), [
|
|
157
|
+
'ls-files',
|
|
158
|
+
'-z',
|
|
159
|
+
'--',
|
|
160
|
+
'.beads',
|
|
161
|
+
'.dolt',
|
|
162
|
+
'.beads-credential-key'
|
|
163
|
+
], {
|
|
164
|
+
cwd: projectRoot,
|
|
165
|
+
encoding: 'utf8',
|
|
166
|
+
stdio: 'pipe'
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (!isRecoverableCommandError(error)) {
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!tracked) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
secureExecFileSync(resolveCommand('git'), [
|
|
181
|
+
'rm',
|
|
182
|
+
'--cached',
|
|
183
|
+
'-r',
|
|
184
|
+
'-f',
|
|
185
|
+
'--ignore-unmatch',
|
|
186
|
+
'--',
|
|
187
|
+
'.beads',
|
|
188
|
+
'.dolt',
|
|
189
|
+
'.beads-credential-key'
|
|
190
|
+
], {
|
|
191
|
+
cwd: projectRoot,
|
|
192
|
+
encoding: 'utf8',
|
|
193
|
+
stdio: 'pipe'
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (!isRecoverableCommandError(error)) {
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Add local-only Git exclude rules for Beads state.
|
|
204
|
+
*
|
|
205
|
+
* Uses `.git/info/exclude` instead of the tracked `.gitignore` so `forge setup`
|
|
206
|
+
* does not dirty downstream repositories just to keep Beads runtime files local.
|
|
207
|
+
* Existing tracked Beads state is removed from the Git index only, leaving local
|
|
208
|
+
* files in place for the Beads runtime.
|
|
209
|
+
*
|
|
210
|
+
* @param {string} projectRoot - Absolute path to the project root
|
|
211
|
+
*/
|
|
212
|
+
function ensureBeadsGitExclude(projectRoot) {
|
|
213
|
+
const excludePath = resolveGitInfoExcludePath(projectRoot);
|
|
214
|
+
fs.mkdirSync(path.dirname(excludePath), { recursive: true });
|
|
215
|
+
|
|
216
|
+
const existing = fs.existsSync(excludePath)
|
|
217
|
+
? fs.readFileSync(excludePath, 'utf8')
|
|
218
|
+
: '';
|
|
219
|
+
const existingLines = new Set(existing.split(/\r?\n/));
|
|
220
|
+
const missingLines = BEADS_LOCAL_EXCLUDE_LINES.filter((line) => !existingLines.has(line));
|
|
221
|
+
|
|
222
|
+
if (missingLines.length === 0) {
|
|
223
|
+
untrackExistingBeadsState(projectRoot);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const prefix = existing && !existing.endsWith('\n') ? '\n' : '';
|
|
228
|
+
const spacer = existing.trim() ? '\n' : '';
|
|
229
|
+
fs.appendFileSync(excludePath, `${prefix}${spacer}${missingLines.join('\n')}\n`, 'utf8');
|
|
230
|
+
untrackExistingBeadsState(projectRoot);
|
|
231
|
+
}
|
|
232
|
+
|
|
120
233
|
/**
|
|
121
234
|
* Check whether Beads is properly initialized in a project.
|
|
122
235
|
*
|
|
@@ -334,6 +447,13 @@ function safeBeadsInit(projectRoot, options = {}) {
|
|
|
334
447
|
const warnings = [];
|
|
335
448
|
const errors = [];
|
|
336
449
|
|
|
450
|
+
// Keep Beads runtime/export state local even when the tracker already exists.
|
|
451
|
+
try {
|
|
452
|
+
ensureBeadsGitExclude(projectRoot);
|
|
453
|
+
} catch (err) {
|
|
454
|
+
warnings.push(`Failed to write local Beads git exclude rules: ${err.message}`);
|
|
455
|
+
}
|
|
456
|
+
|
|
337
457
|
// (a) Check if already initialized — idempotent skip
|
|
338
458
|
if (isBeadsInitialized(projectRoot)) {
|
|
339
459
|
return {
|
|
@@ -410,6 +530,7 @@ module.exports = {
|
|
|
410
530
|
sanitizePrefix,
|
|
411
531
|
writeBeadsConfig,
|
|
412
532
|
writeBeadsGitignore,
|
|
533
|
+
ensureBeadsGitExclude,
|
|
413
534
|
isBeadsInitialized,
|
|
414
535
|
readBeadsDatabaseName,
|
|
415
536
|
preSeedJsonl,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { execFileSync } = require('node:child_process');
|
|
6
|
+
const { cleanupDeprecatedSyncFiles } = require('./deprecated-sync-cleanup');
|
|
6
7
|
|
|
7
8
|
const DEFAULT_BEADS_VERSION = '1.0.0';
|
|
8
9
|
|
|
@@ -141,119 +142,41 @@ function templateWorkflows(workflowDir, branch, beadsVersion, createdFiles = [])
|
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
* @property {string[]} filesSkipped - Relative paths of files that already existed and were preserved
|
|
148
|
-
*/
|
|
145
|
+
function cleanupDeprecatedBeadsSync(projectRoot, options = {}) {
|
|
146
|
+
return cleanupDeprecatedSyncFiles(projectRoot, options);
|
|
147
|
+
}
|
|
149
148
|
|
|
150
149
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* @
|
|
154
|
-
* @
|
|
155
|
-
* @
|
|
150
|
+
* @typedef {Object} ScaffoldResult
|
|
151
|
+
* @property {string[]} filesCreated - Always empty; retained for deprecated API compatibility.
|
|
152
|
+
* @property {string[]} filesSkipped - Always empty; retained for deprecated API compatibility.
|
|
153
|
+
* @property {string[]} filesRemoved - Relative paths of deprecated generated sync files removed.
|
|
154
|
+
* @property {boolean} deprecated - Indicates the legacy scaffold path is deprecated.
|
|
155
|
+
* @property {string} message - Deprecation guidance for future sync direction.
|
|
156
156
|
*/
|
|
157
|
-
function copyFileWithDirs(src, dest) {
|
|
158
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
159
|
-
fs.copyFileSync(src, dest);
|
|
160
|
-
}
|
|
161
157
|
|
|
162
158
|
/**
|
|
163
|
-
*
|
|
159
|
+
* Deprecated compatibility shim for removed Beads/GitHub sync scaffolding.
|
|
164
160
|
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
161
|
+
* Performs cleanup of generated legacy sync artifacts only. User-owned files
|
|
162
|
+
* at matching paths are preserved unless their content matches known generated
|
|
163
|
+
* templates.
|
|
167
164
|
*
|
|
168
165
|
* @param {string} projectRoot - Absolute path to the user's project root
|
|
169
166
|
* @param {string} packageDir - Absolute path to the forge package install location
|
|
170
167
|
* @param {Object} [_options={}] - Reserved for future options
|
|
171
|
-
* @returns {ScaffoldResult}
|
|
168
|
+
* @returns {ScaffoldResult} Deprecated cleanup result.
|
|
172
169
|
*/
|
|
173
170
|
function scaffoldBeadsSync(projectRoot, packageDir, _options = {}) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const srcWorkflowsDir = path.join(packageDir, '.github', 'workflows');
|
|
184
|
-
|
|
185
|
-
for (const name of workflowNames) {
|
|
186
|
-
const src = path.join(srcWorkflowsDir, name);
|
|
187
|
-
const relPath = `.github/workflows/${name}`;
|
|
188
|
-
const dest = path.join(projectRoot, '.github', 'workflows', name);
|
|
189
|
-
|
|
190
|
-
if (!fs.existsSync(src)) continue;
|
|
191
|
-
if (fs.existsSync(dest)) {
|
|
192
|
-
filesSkipped.push(relPath);
|
|
193
|
-
} else {
|
|
194
|
-
copyFileWithDirs(src, dest);
|
|
195
|
-
filesCreated.push(relPath);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// --- Step b: Copy sync modules ---
|
|
200
|
-
const srcSyncDir = path.join(packageDir, 'scripts', 'github-beads-sync');
|
|
201
|
-
const destSyncDir = path.join(githubDir, 'scripts', 'beads-sync');
|
|
202
|
-
|
|
203
|
-
if (fs.existsSync(srcSyncDir)) {
|
|
204
|
-
const entries = fs.readdirSync(srcSyncDir);
|
|
205
|
-
for (const entry of entries) {
|
|
206
|
-
const srcFile = path.join(srcSyncDir, entry);
|
|
207
|
-
const stat = fs.statSync(srcFile);
|
|
208
|
-
if (stat.isFile()) {
|
|
209
|
-
const relPath = `.github/scripts/beads-sync/${entry}`;
|
|
210
|
-
const dest = path.join(destSyncDir, entry);
|
|
211
|
-
if (fs.existsSync(dest)) {
|
|
212
|
-
filesSkipped.push(relPath);
|
|
213
|
-
} else {
|
|
214
|
-
copyFileWithDirs(srcFile, dest);
|
|
215
|
-
filesCreated.push(relPath);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// --- Step c: Create beads-sync-config.json ---
|
|
222
|
-
const configRelPath = '.github/beads-sync-config.json';
|
|
223
|
-
const configDest = path.join(githubDir, 'beads-sync-config.json');
|
|
224
|
-
const srcConfig = path.join(packageDir, 'scripts', 'github-beads-sync.config.json');
|
|
225
|
-
|
|
226
|
-
if (fs.existsSync(configDest)) {
|
|
227
|
-
filesSkipped.push(configRelPath);
|
|
228
|
-
} else if (fs.existsSync(srcConfig)) {
|
|
229
|
-
copyFileWithDirs(srcConfig, configDest);
|
|
230
|
-
filesCreated.push(configRelPath);
|
|
231
|
-
} else {
|
|
232
|
-
// Create a sensible default config
|
|
233
|
-
const defaultConfig = {
|
|
234
|
-
defaultType: 'task',
|
|
235
|
-
defaultPriority: 2,
|
|
236
|
-
mapAssignee: true,
|
|
237
|
-
publicRepoGate: 'none'
|
|
238
|
-
};
|
|
239
|
-
fs.mkdirSync(path.dirname(configDest), { recursive: true });
|
|
240
|
-
fs.writeFileSync(configDest, JSON.stringify(defaultConfig, null, 2) + '\n');
|
|
241
|
-
filesCreated.push(configRelPath);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// --- Step d: Create beads-mapping.json if not exists ---
|
|
245
|
-
const mappingRelPath = '.github/beads-mapping.json';
|
|
246
|
-
const mappingDest = path.join(githubDir, 'beads-mapping.json');
|
|
247
|
-
|
|
248
|
-
if (fs.existsSync(mappingDest)) {
|
|
249
|
-
filesSkipped.push(mappingRelPath);
|
|
250
|
-
} else {
|
|
251
|
-
fs.mkdirSync(path.dirname(mappingDest), { recursive: true });
|
|
252
|
-
fs.writeFileSync(mappingDest, '{}');
|
|
253
|
-
filesCreated.push(mappingRelPath);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return { filesCreated, filesSkipped };
|
|
171
|
+
const cleanup = cleanupDeprecatedBeadsSync(projectRoot, { packageDir });
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
filesCreated: [],
|
|
175
|
+
filesSkipped: [],
|
|
176
|
+
filesRemoved: cleanup.removed,
|
|
177
|
+
deprecated: true,
|
|
178
|
+
message: 'Beads GitHub sync scaffolding is deprecated; future GitHub issue sync must use Forge Kernel/server authority.'
|
|
179
|
+
};
|
|
257
180
|
}
|
|
258
181
|
|
|
259
182
|
module.exports = {
|
|
@@ -261,5 +184,6 @@ module.exports = {
|
|
|
261
184
|
detectDefaultBranch,
|
|
262
185
|
detectBeadsVersion,
|
|
263
186
|
templateWorkflows,
|
|
187
|
+
cleanupDeprecatedBeadsSync,
|
|
264
188
|
scaffoldBeadsSync,
|
|
265
189
|
};
|
package/lib/codex-skills.js
CHANGED
|
@@ -2,6 +2,19 @@ const fs = require('node:fs');
|
|
|
2
2
|
const os = require('node:os');
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
|
|
5
|
+
const { populateAgentSkills } = require('./skills-sync');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Codex's documented repo-local skill discovery directory, relative to a project
|
|
9
|
+
* root. Codex scans `.agents/skills/<name>/SKILL.md` from the current working
|
|
10
|
+
* directory up to the repo root (developers.openai.com/codex/skills). Unlike the
|
|
11
|
+
* GLOBAL `$CODEX_HOME/skills` install, this surface is COMMITTED to the repo (kept
|
|
12
|
+
* byte-identical to the canonical `skills/` source by a pre-commit sync hook and
|
|
13
|
+
* the drift gate), so a teammate who clones the repo WITHOUT running `forge setup`
|
|
14
|
+
* still gets the Forge skills/stages auto-discovered.
|
|
15
|
+
*/
|
|
16
|
+
const CODEX_REPO_SKILLS_DIR = '.agents/skills';
|
|
17
|
+
|
|
5
18
|
function injectForgeAdapter(content, commandName) {
|
|
6
19
|
const adapter = [
|
|
7
20
|
'> Forge stage adapter',
|
|
@@ -20,7 +33,10 @@ function injectForgeAdapter(content, commandName) {
|
|
|
20
33
|
}
|
|
21
34
|
|
|
22
35
|
function listCodexSkillEntries(sourceRoot) {
|
|
23
|
-
|
|
36
|
+
// Read from the canonical `skills/` source (single source of truth) rather than
|
|
37
|
+
// a committed `.codex/skills` mirror. The GLOBAL `$CODEX_HOME/skills` install is
|
|
38
|
+
// generated from these entries with the stage adapter injected below.
|
|
39
|
+
const skillsDir = path.join(sourceRoot, 'skills');
|
|
24
40
|
|
|
25
41
|
if (!fs.existsSync(skillsDir)) {
|
|
26
42
|
return [];
|
|
@@ -94,10 +110,44 @@ function buildCodexSkillInstallPlan(sourceRoot, options = {}) {
|
|
|
94
110
|
}));
|
|
95
111
|
}
|
|
96
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Absolute path to a project's repo-local Codex skill discovery dir.
|
|
115
|
+
*
|
|
116
|
+
* @param {string} projectRoot - The project/repo root.
|
|
117
|
+
* @returns {string} `<projectRoot>/.agents/skills`.
|
|
118
|
+
*/
|
|
119
|
+
function resolveCodexRepoSkillsDir(projectRoot) {
|
|
120
|
+
return path.join(projectRoot, '.agents', 'skills');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Generate the repo-local Codex skill discovery mirror (`.agents/skills`) from
|
|
125
|
+
* the canonical `skills/` source.
|
|
126
|
+
*
|
|
127
|
+
* Content matches the other harness mirrors byte-for-byte (raw canonical — the
|
|
128
|
+
* `$CODEX_HOME` stage adapter is NOT injected here) so the skills-sync drift gate
|
|
129
|
+
* can enforce it, and so repo-local discovery reaches parity with the
|
|
130
|
+
* `.claude/skills` / `.cursor/skills` discovery surfaces.
|
|
131
|
+
*
|
|
132
|
+
* @param {object} params
|
|
133
|
+
* @param {string} params.sourceRoot - Root containing the canonical `skills/` dir.
|
|
134
|
+
* @param {string} params.projectRoot - Target project/repo root.
|
|
135
|
+
* @param {boolean} [params.clean=false] - Remove stale canonical-managed dirs first.
|
|
136
|
+
* @returns {{targetSkillsDir: string, written: string[]}}
|
|
137
|
+
*/
|
|
138
|
+
function populateCodexRepoSkills({ sourceRoot, projectRoot, clean = false }) {
|
|
139
|
+
const targetSkillsDir = resolveCodexRepoSkillsDir(projectRoot);
|
|
140
|
+
const { written } = populateAgentSkills({ sourceRoot, targetSkillsDir, clean });
|
|
141
|
+
return { targetSkillsDir, written };
|
|
142
|
+
}
|
|
143
|
+
|
|
97
144
|
module.exports = {
|
|
145
|
+
CODEX_REPO_SKILLS_DIR,
|
|
98
146
|
buildCodexSkillInstallPlan,
|
|
99
147
|
formatCodexSkillsInstallDir,
|
|
100
148
|
listCodexSkillEntries,
|
|
149
|
+
populateCodexRepoSkills,
|
|
101
150
|
resolveCodexHome,
|
|
151
|
+
resolveCodexRepoSkillsDir,
|
|
102
152
|
resolveCodexSkillsInstallDir,
|
|
103
153
|
};
|