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,237 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gate composition + real sub-runners for `forge preflight`.
|
|
5
|
+
*
|
|
6
|
+
* Each real runner takes an injectable `spawn` (defaults to spawnSync) so the
|
|
7
|
+
* composition and the runners are unit-testable without touching the shell.
|
|
8
|
+
* The four gates give DETERMINISTIC-GATE PARITY with CI on the blast radius of
|
|
9
|
+
* a change, without the slow full matrix:
|
|
10
|
+
*
|
|
11
|
+
* 1. lint — eslint --max-warnings 0 on changed files (or all with --all)
|
|
12
|
+
* 2. drift — skills-mirror + registry-sync + agentic-workflow structural asserts
|
|
13
|
+
* 3. sonar — eslint-plugin-sonarjs (cognitive-complexity=15 + parity rules)
|
|
14
|
+
* 4. affected — only the tests mapped from the changed files
|
|
15
|
+
*
|
|
16
|
+
* @module preflight/gates
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
const { spawnSync } = require('node:child_process');
|
|
21
|
+
const { execFileSync } = require('node:child_process');
|
|
22
|
+
const nodeFs = require('node:fs');
|
|
23
|
+
const { getAffectedTestFiles } = require('../commands/test');
|
|
24
|
+
|
|
25
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
26
|
+
|
|
27
|
+
/** Filter a changed-file list down to the JS files eslint should look at. */
|
|
28
|
+
function lintableFiles(files) {
|
|
29
|
+
if (!Array.isArray(files)) return [];
|
|
30
|
+
return files.filter((f) => /\.(mjs|cjs|js)$/.test(f) && !f.includes('node_modules'));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Short, bounded summary for a spawn result. */
|
|
34
|
+
function statusSummary(result, okMsg, failMsg) {
|
|
35
|
+
return result && result.status === 0
|
|
36
|
+
? { ok: true, summary: okMsg }
|
|
37
|
+
: { ok: false, summary: failMsg };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Gate 1 — ESLint on the change blast radius (or the whole tree with --all).
|
|
42
|
+
* `files === null` means "lint everything" (`eslint .`).
|
|
43
|
+
*/
|
|
44
|
+
function runEslint(files, { projectRoot, spawn = spawnSync } = {}) {
|
|
45
|
+
const lintAll = files === null;
|
|
46
|
+
const targets = lintAll ? ['.'] : lintableFiles(files);
|
|
47
|
+
if (targets.length === 0) {
|
|
48
|
+
return { ok: true, summary: 'no changed JS files' };
|
|
49
|
+
}
|
|
50
|
+
const result = spawn(
|
|
51
|
+
'npx',
|
|
52
|
+
['eslint', '--max-warnings', '0', ...targets],
|
|
53
|
+
{ cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
|
|
54
|
+
);
|
|
55
|
+
return statusSummary(
|
|
56
|
+
result,
|
|
57
|
+
lintAll ? 'whole tree clean' : `${targets.length} changed file(s) clean`,
|
|
58
|
+
'eslint reported errors/warnings',
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Gate 2 — deterministic structural asserts that CI enforces:
|
|
64
|
+
* - skills mirror drift (test/structural/skills-sync-drift.test.js)
|
|
65
|
+
* - SUBCOMMANDS<->ISSUE_COMMANDS registry sync (test/commands/_resolve-command-opts.test.js)
|
|
66
|
+
* - committed agentic-workflow mirror (scripts/sync-agentic-workflow.js --check)
|
|
67
|
+
*/
|
|
68
|
+
function runStructural({ projectRoot, spawn = spawnSync, fs = nodeFs } = {}) {
|
|
69
|
+
const exists = (rel) => {
|
|
70
|
+
try {
|
|
71
|
+
return fs.existsSync(path.join(projectRoot, rel));
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// These are Forge's OWN internal structural asserts. A consumer repo does not
|
|
78
|
+
// contain them and can NEVER satisfy them, so only run the ones that actually
|
|
79
|
+
// exist in this project. If none do, the gate is not applicable here — report
|
|
80
|
+
// an explicit SKIP rather than a false pass or an impossible failure.
|
|
81
|
+
const structuralTests = [
|
|
82
|
+
'test/structural/skills-sync-drift.test.js',
|
|
83
|
+
'test/commands/_resolve-command-opts.test.js',
|
|
84
|
+
].filter(exists);
|
|
85
|
+
const agenticScript = 'scripts/sync-agentic-workflow.js';
|
|
86
|
+
const hasAgentic = exists(agenticScript);
|
|
87
|
+
|
|
88
|
+
if (structuralTests.length === 0 && !hasAgentic) {
|
|
89
|
+
return {
|
|
90
|
+
ok: true,
|
|
91
|
+
skipped: true,
|
|
92
|
+
summary: 'no Forge-internal structural checks in this repo (consumer context)',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (structuralTests.length > 0) {
|
|
97
|
+
const bun = spawn(
|
|
98
|
+
'bun',
|
|
99
|
+
['test', '--timeout', '15000', ...structuralTests],
|
|
100
|
+
{ cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
|
|
101
|
+
);
|
|
102
|
+
if (!bun || bun.status !== 0) {
|
|
103
|
+
return { ok: false, summary: 'skills-mirror / registry-sync asserts failed' };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!hasAgentic) {
|
|
108
|
+
return { ok: true, summary: 'skills + registry in sync (no agentic-workflow mirror here)' };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const agentic = spawn(
|
|
112
|
+
'node',
|
|
113
|
+
[agenticScript, '--check'],
|
|
114
|
+
{ cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
|
|
115
|
+
);
|
|
116
|
+
return statusSummary(
|
|
117
|
+
agentic,
|
|
118
|
+
'skills + registry + agentic-workflow in sync',
|
|
119
|
+
'agentic-workflow mirror drift (run: node scripts/sync-agentic-workflow.js)',
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Gate 3 — SonarCloud parity via eslint-plugin-sonarjs, using an isolated flat
|
|
125
|
+
* config (cognitive-complexity pinned to 15 + the parity rules) so it never
|
|
126
|
+
* merges with the repo's main eslint config.
|
|
127
|
+
* `files === null` means "scan everything" (whole-tree `--all` mode) — mirrors
|
|
128
|
+
* runEslint so `--all` never leaves sonar as a vacuous "no changed files" pass.
|
|
129
|
+
*/
|
|
130
|
+
function runSonar(files, { projectRoot, spawn = spawnSync } = {}) {
|
|
131
|
+
const scanAll = files === null;
|
|
132
|
+
const targets = scanAll ? ['.'] : lintableFiles(files);
|
|
133
|
+
if (targets.length === 0) {
|
|
134
|
+
return { ok: true, summary: 'no changed JS files' };
|
|
135
|
+
}
|
|
136
|
+
const config = path.join(projectRoot, 'scripts', 'preflight-sonar.eslint.config.mjs');
|
|
137
|
+
const result = spawn(
|
|
138
|
+
'npx',
|
|
139
|
+
['eslint', '--no-config-lookup', '--config', config, '--max-warnings', '0', ...targets],
|
|
140
|
+
{ cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
|
|
141
|
+
);
|
|
142
|
+
return statusSummary(
|
|
143
|
+
result,
|
|
144
|
+
scanAll ? 'sonarjs clean (whole tree)' : `sonarjs clean (${targets.length} file(s))`,
|
|
145
|
+
'sonarjs parity violations (e.g. cognitive-complexity > 15)',
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Gate 4 — run ONLY the tests mapped from the changed files (reuses the
|
|
151
|
+
* pre-push affected-test mapping). No affected tests → fast-lane pass.
|
|
152
|
+
*/
|
|
153
|
+
function runAffectedTests({
|
|
154
|
+
projectRoot,
|
|
155
|
+
changedFiles: _changedFiles,
|
|
156
|
+
spawn = spawnSync,
|
|
157
|
+
resolveTests,
|
|
158
|
+
} = {}) {
|
|
159
|
+
const resolver = typeof resolveTests === 'function'
|
|
160
|
+
? resolveTests
|
|
161
|
+
// strict:true so a failed `git diff` THROWS here instead of returning [] —
|
|
162
|
+
// otherwise a git error would masquerade as "no affected tests" (fast-lane
|
|
163
|
+
// green), a fail-OPEN. The catch below turns that throw into a closed gate.
|
|
164
|
+
: () => getAffectedTestFiles(projectRoot, execFileSync, nodeFs, { strict: true });
|
|
165
|
+
let targets;
|
|
166
|
+
try {
|
|
167
|
+
targets = resolver() || [];
|
|
168
|
+
} catch (err) {
|
|
169
|
+
// A resolver ERROR must NOT masquerade as "no affected tests" (green).
|
|
170
|
+
// We could not determine what to run, so fail closed — never a vacuous pass.
|
|
171
|
+
const reason = err && err.message ? err.message : String(err);
|
|
172
|
+
return { ok: false, summary: `affected-test resolution failed — fail-closed (${reason})` };
|
|
173
|
+
}
|
|
174
|
+
if (targets.length === 0) {
|
|
175
|
+
return { ok: true, summary: 'no affected tests resolved (fast lane)' };
|
|
176
|
+
}
|
|
177
|
+
const result = spawn(
|
|
178
|
+
'bun',
|
|
179
|
+
['test', '--timeout', '15000', ...targets],
|
|
180
|
+
{ cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
|
|
181
|
+
);
|
|
182
|
+
return statusSummary(
|
|
183
|
+
result,
|
|
184
|
+
`${targets.length} affected test file(s) passed`,
|
|
185
|
+
'affected tests failed',
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Compose the ordered gate list. Each gate delegates to a sub-runner that can
|
|
191
|
+
* be injected via `deps` for testing; defaults wire the real runners above.
|
|
192
|
+
*
|
|
193
|
+
* @param {Object} args
|
|
194
|
+
* @param {string} args.projectRoot
|
|
195
|
+
* @param {string[]} args.changedFiles
|
|
196
|
+
* @param {boolean} [args.runAll] - lint/scan whole tree instead of changed files
|
|
197
|
+
* @param {Object} [args.deps] - { eslint, structural, sonar, affected }
|
|
198
|
+
* @returns {{ name: string, run: () => Promise<{ok:boolean, summary?:string}> }[]}
|
|
199
|
+
*/
|
|
200
|
+
function buildGates({ projectRoot, changedFiles = [], runAll = false, deps = {} }) {
|
|
201
|
+
const eslint = deps.eslint || ((files) => runEslint(files, { projectRoot }));
|
|
202
|
+
const structural = deps.structural || (() => runStructural({ projectRoot }));
|
|
203
|
+
const sonar = deps.sonar || ((files) => runSonar(files, { projectRoot }));
|
|
204
|
+
const affected = deps.affected || (() => runAffectedTests({ projectRoot, changedFiles }));
|
|
205
|
+
|
|
206
|
+
// Under --all, scope BOTH lint and sonar to the whole tree (null). Otherwise
|
|
207
|
+
// sonar would receive changedFiles=[] and report a vacuous "no changed files"
|
|
208
|
+
// pass while lint scanned everything — a fail-open hole on the remedy path.
|
|
209
|
+
const scanTargets = runAll ? null : changedFiles;
|
|
210
|
+
|
|
211
|
+
// Affected-tests maps from the change set, which is empty under --all. Running
|
|
212
|
+
// the WHOLE suite here defeats preflight's fast purpose (and hangs on Windows),
|
|
213
|
+
// so mark it explicitly not-run — never a vacuous green. CI runs the full suite.
|
|
214
|
+
const affectedGate = runAll
|
|
215
|
+
? async () => ({
|
|
216
|
+
ok: true,
|
|
217
|
+
skipped: true,
|
|
218
|
+
summary: 'whole-tree mode (--all): affected-test mapping N/A — run the full suite (CI does)',
|
|
219
|
+
})
|
|
220
|
+
: async () => affected();
|
|
221
|
+
|
|
222
|
+
return [
|
|
223
|
+
{ name: 'lint', run: async () => eslint(scanTargets) },
|
|
224
|
+
{ name: 'drift/registry/mirror', run: async () => structural() },
|
|
225
|
+
{ name: 'sonar (cognitive-complexity=15)', run: async () => sonar(scanTargets) },
|
|
226
|
+
{ name: 'affected-tests', run: affectedGate },
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
module.exports = {
|
|
231
|
+
buildGates,
|
|
232
|
+
lintableFiles,
|
|
233
|
+
runEslint,
|
|
234
|
+
runStructural,
|
|
235
|
+
runSonar,
|
|
236
|
+
runAffectedTests,
|
|
237
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fast-fail gate runner for `forge preflight`.
|
|
5
|
+
*
|
|
6
|
+
* Executes an ordered list of gates one at a time. The first gate to fail
|
|
7
|
+
* short-circuits the run: every later gate is reported as `skipped` and never
|
|
8
|
+
* executed. This keeps preflight FAST — an agent fixes the first broken gate
|
|
9
|
+
* and re-runs rather than waiting for the whole matrix.
|
|
10
|
+
*
|
|
11
|
+
* A gate is `{ name: string, run: () => Promise<{ ok: boolean, summary?: string }> }`.
|
|
12
|
+
* The sub-runner behind each `run` is injected by the composer (see gates.js),
|
|
13
|
+
* so this module stays pure and trivially testable.
|
|
14
|
+
*
|
|
15
|
+
* @module preflight/runner
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} GateOutcome
|
|
20
|
+
* @property {boolean} ok - Whether the gate passed.
|
|
21
|
+
* @property {string} [summary] - Short human-readable result line.
|
|
22
|
+
*
|
|
23
|
+
* @typedef {Object} Gate
|
|
24
|
+
* @property {string} name
|
|
25
|
+
* @property {() => Promise<GateOutcome>} run
|
|
26
|
+
*
|
|
27
|
+
* @typedef {Object} GateResult
|
|
28
|
+
* @property {string} name
|
|
29
|
+
* @property {boolean|null} ok - true/false, or null when skipped.
|
|
30
|
+
* @property {boolean} skipped
|
|
31
|
+
* @property {string} summary
|
|
32
|
+
* @property {number} [durationMs]
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Execute a single gate, normalizing thrown errors into a failed outcome.
|
|
37
|
+
*
|
|
38
|
+
* @param {Gate} gate
|
|
39
|
+
* @returns {Promise<GateResult>}
|
|
40
|
+
*/
|
|
41
|
+
async function executeGate(gate) {
|
|
42
|
+
const started = Date.now();
|
|
43
|
+
let outcome;
|
|
44
|
+
try {
|
|
45
|
+
outcome = await gate.run();
|
|
46
|
+
} catch (err) {
|
|
47
|
+
const message = err && err.message ? err.message : String(err);
|
|
48
|
+
outcome = { ok: false, summary: `error: ${message}` };
|
|
49
|
+
}
|
|
50
|
+
const durationMs = Date.now() - started;
|
|
51
|
+
const skipped = !!outcome?.skipped;
|
|
52
|
+
// A skipped gate is a pass-through: it is neither a pass nor a failure, so it
|
|
53
|
+
// must never short-circuit later gates (see runGates fast-fail). BUT a skip
|
|
54
|
+
// must never MASK an explicit failure — a gate that reports {ok:false} has
|
|
55
|
+
// failed regardless of a skipped flag, so it stays failed and short-circuits.
|
|
56
|
+
const explicitOk = !!outcome?.ok;
|
|
57
|
+
const ok = skipped ? outcome?.ok !== false : explicitOk;
|
|
58
|
+
// Contract: an explicit ok:false can never normalize to a pass. Guards against
|
|
59
|
+
// a future edit reintroducing the skip-masks-failure bug (4b73b6bf).
|
|
60
|
+
if (outcome && outcome.ok === false && ok === true) {
|
|
61
|
+
throw new Error('executeGate contract violation: skipped must not override an explicit ok:false');
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
name: gate.name,
|
|
65
|
+
ok,
|
|
66
|
+
skipped,
|
|
67
|
+
summary: outcome?.summary || '',
|
|
68
|
+
durationMs,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Live-log label for a gate result — SKIP must never read as PASS. */
|
|
73
|
+
function gateLogMark(result) {
|
|
74
|
+
if (result.skipped) return 'SKIP';
|
|
75
|
+
return result.ok ? 'PASS' : 'FAIL';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Run gates in order, fast-failing at the first failure.
|
|
80
|
+
*
|
|
81
|
+
* @param {Gate[]} gates
|
|
82
|
+
* @param {{ log?: (line: string) => void }} [options]
|
|
83
|
+
* @returns {Promise<{ ok: boolean, results: GateResult[], failedIndex: number }>}
|
|
84
|
+
*/
|
|
85
|
+
async function runGates(gates, options = {}) {
|
|
86
|
+
const log = typeof options.log === 'function' ? options.log : () => {};
|
|
87
|
+
const results = [];
|
|
88
|
+
let ok = true;
|
|
89
|
+
let failedIndex = -1;
|
|
90
|
+
|
|
91
|
+
for (const gate of gates) {
|
|
92
|
+
if (!ok) {
|
|
93
|
+
results.push({
|
|
94
|
+
name: gate.name,
|
|
95
|
+
ok: null,
|
|
96
|
+
skipped: true,
|
|
97
|
+
summary: 'skipped (earlier gate failed)',
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const result = await executeGate(gate);
|
|
103
|
+
results.push(result);
|
|
104
|
+
const suffix = result.summary ? ` — ${result.summary}` : '';
|
|
105
|
+
log(`${gateLogMark(result)} ${gate.name}${suffix}`);
|
|
106
|
+
|
|
107
|
+
if (!result.ok) {
|
|
108
|
+
ok = false;
|
|
109
|
+
failedIndex = results.length - 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { ok, results, failedIndex };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { runGates };
|
package/lib/project-discovery.js
CHANGED
|
@@ -263,16 +263,6 @@ async function detectInstalledAgents(projectPath) {
|
|
|
263
263
|
}
|
|
264
264
|
]
|
|
265
265
|
},
|
|
266
|
-
{
|
|
267
|
-
name: 'copilot',
|
|
268
|
-
checks: [
|
|
269
|
-
// .github/copilot-instructions.md
|
|
270
|
-
async () => {
|
|
271
|
-
const copilotFile = path.join(projectPath, '.github', 'copilot-instructions.md');
|
|
272
|
-
return fs.existsSync(copilotFile);
|
|
273
|
-
}
|
|
274
|
-
]
|
|
275
|
-
},
|
|
276
266
|
{
|
|
277
267
|
name: 'cursor',
|
|
278
268
|
checks: [
|
|
@@ -283,39 +273,6 @@ async function detectInstalledAgents(projectPath) {
|
|
|
283
273
|
}
|
|
284
274
|
]
|
|
285
275
|
},
|
|
286
|
-
{
|
|
287
|
-
name: 'cline',
|
|
288
|
-
checks: [
|
|
289
|
-
async () => fs.existsSync(path.join(projectPath, '.clinerules')),
|
|
290
|
-
async () => {
|
|
291
|
-
const clineDir = path.join(projectPath, '.cline');
|
|
292
|
-
return fs.existsSync(clineDir) && (await fs.promises.stat(clineDir)).isDirectory();
|
|
293
|
-
}
|
|
294
|
-
]
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
name: 'kilocode',
|
|
298
|
-
checks: [
|
|
299
|
-
async () => {
|
|
300
|
-
const kilocodeDir = path.join(projectPath, '.kilocode');
|
|
301
|
-
return fs.existsSync(kilocodeDir) && (await fs.promises.stat(kilocodeDir)).isDirectory();
|
|
302
|
-
},
|
|
303
|
-
async () => fs.existsSync(path.join(projectPath, '.kilocode', 'workflows', 'forge-workflow.md')),
|
|
304
|
-
async () => fs.existsSync(path.join(projectPath, '.kilocode', 'rules', 'workflow.md')),
|
|
305
|
-
async () => fs.existsSync(path.join(projectPath, '.kilocode', 'skills', 'forge-workflow', 'SKILL.md'))
|
|
306
|
-
]
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
name: 'roo',
|
|
310
|
-
checks: [
|
|
311
|
-
async () => fs.existsSync(path.join(projectPath, '.roorules')),
|
|
312
|
-
async () => {
|
|
313
|
-
const rooDir = path.join(projectPath, '.roo');
|
|
314
|
-
return fs.existsSync(rooDir) && (await fs.promises.stat(rooDir)).isDirectory();
|
|
315
|
-
},
|
|
316
|
-
async () => fs.existsSync(path.join(projectPath, '.roo', 'rules'))
|
|
317
|
-
]
|
|
318
|
-
},
|
|
319
276
|
{
|
|
320
277
|
name: 'codex',
|
|
321
278
|
checks: [
|
|
@@ -325,16 +282,6 @@ async function detectInstalledAgents(projectPath) {
|
|
|
325
282
|
return fs.existsSync(codexDir) && (await fs.promises.stat(codexDir)).isDirectory();
|
|
326
283
|
}
|
|
327
284
|
]
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
name: 'opencode',
|
|
331
|
-
checks: [
|
|
332
|
-
// opencode.json file
|
|
333
|
-
async () => {
|
|
334
|
-
const opencodeJson = path.join(projectPath, 'opencode.json');
|
|
335
|
-
return fs.existsSync(opencodeJson);
|
|
336
|
-
}
|
|
337
|
-
]
|
|
338
285
|
}
|
|
339
286
|
];
|
|
340
287
|
|