beth-copilot 1.0.18 → 2.0.0
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/CHANGELOG.md +79 -28
- package/README.md +127 -298
- package/assets/beth-questioning.png +0 -0
- package/assets/yellowstone-beth.png +0 -0
- package/bin/cli.js +124 -715
- package/dist/__tests__/inject-skills.test.d.ts +9 -0
- package/dist/__tests__/inject-skills.test.d.ts.map +1 -0
- package/dist/__tests__/inject-skills.test.js +143 -0
- package/dist/__tests__/inject-skills.test.js.map +1 -0
- package/dist/__tests__/skills/disambiguation.test.d.ts +10 -0
- package/dist/__tests__/skills/disambiguation.test.d.ts.map +1 -0
- package/dist/__tests__/skills/disambiguation.test.js +192 -0
- package/dist/__tests__/skills/disambiguation.test.js.map +1 -0
- package/dist/__tests__/skills/hook-injection.test.d.ts +11 -0
- package/dist/__tests__/skills/hook-injection.test.d.ts.map +1 -0
- package/dist/__tests__/skills/hook-injection.test.js +173 -0
- package/dist/__tests__/skills/hook-injection.test.js.map +1 -0
- package/dist/__tests__/skills/mapping-completeness.test.d.ts +17 -0
- package/dist/__tests__/skills/mapping-completeness.test.d.ts.map +1 -0
- package/dist/__tests__/skills/mapping-completeness.test.js +281 -0
- package/dist/__tests__/skills/mapping-completeness.test.js.map +1 -0
- package/dist/__tests__/skills/pipeline-integration.test.d.ts +18 -0
- package/dist/__tests__/skills/pipeline-integration.test.d.ts.map +1 -0
- package/dist/__tests__/skills/pipeline-integration.test.js +234 -0
- package/dist/__tests__/skills/pipeline-integration.test.js.map +1 -0
- package/dist/__tests__/skills/skill-routing.test.d.ts +15 -0
- package/dist/__tests__/skills/skill-routing.test.d.ts.map +1 -0
- package/dist/__tests__/skills/skill-routing.test.js +723 -0
- package/dist/__tests__/skills/skill-routing.test.js.map +1 -0
- package/dist/__tests__/skills/trigger-coverage.test.d.ts +24 -0
- package/dist/__tests__/skills/trigger-coverage.test.d.ts.map +1 -0
- package/dist/__tests__/skills/trigger-coverage.test.js +746 -0
- package/dist/__tests__/skills/trigger-coverage.test.js.map +1 -0
- package/dist/__tests__/smoke.test.d.ts +8 -0
- package/dist/__tests__/smoke.test.d.ts.map +1 -0
- package/dist/__tests__/smoke.test.js +62 -0
- package/dist/__tests__/smoke.test.js.map +1 -0
- package/dist/__tests__/verify-skills.test.d.ts +9 -0
- package/dist/__tests__/verify-skills.test.d.ts.map +1 -0
- package/dist/__tests__/verify-skills.test.js +78 -0
- package/dist/__tests__/verify-skills.test.js.map +1 -0
- package/dist/cli/commands/beads.e2e.test.d.ts +15 -0
- package/dist/cli/commands/beads.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/beads.e2e.test.js +585 -0
- package/dist/cli/commands/beads.e2e.test.js.map +1 -0
- package/dist/cli/commands/cli-edge-cases.e2e.test.d.ts +32 -0
- package/dist/cli/commands/cli-edge-cases.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/cli-edge-cases.e2e.test.js +162 -0
- package/dist/cli/commands/cli-edge-cases.e2e.test.js.map +1 -0
- package/dist/cli/commands/close.d.ts +54 -0
- package/dist/cli/commands/close.d.ts.map +1 -0
- package/dist/cli/commands/close.e2e.test.d.ts +11 -0
- package/dist/cli/commands/close.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/close.e2e.test.js +71 -0
- package/dist/cli/commands/close.e2e.test.js.map +1 -0
- package/dist/cli/commands/close.js +95 -0
- package/dist/cli/commands/close.js.map +1 -0
- package/dist/cli/commands/close.test.d.ts +13 -0
- package/dist/cli/commands/close.test.d.ts.map +1 -0
- package/dist/cli/commands/close.test.js +254 -0
- package/dist/cli/commands/close.test.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +7 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.e2e.test.js +3 -59
- package/dist/cli/commands/doctor.e2e.test.js.map +1 -1
- package/dist/cli/commands/doctor.js +38 -18
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/doctor.test.js +32 -25
- package/dist/cli/commands/doctor.test.js.map +1 -1
- package/dist/cli/commands/framework-isolation.test.d.ts +30 -0
- package/dist/cli/commands/framework-isolation.test.d.ts.map +1 -0
- package/dist/cli/commands/framework-isolation.test.js +118 -0
- package/dist/cli/commands/framework-isolation.test.js.map +1 -0
- package/dist/cli/commands/help.e2e.test.js +5 -9
- package/dist/cli/commands/help.e2e.test.js.map +1 -1
- package/dist/cli/commands/init-logic.e2e.test.d.ts +37 -0
- package/dist/cli/commands/init-logic.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/init-logic.e2e.test.js +315 -0
- package/dist/cli/commands/init-logic.e2e.test.js.map +1 -0
- package/dist/cli/commands/init.test.js +4 -21
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/commands/land.d.ts +130 -0
- package/dist/cli/commands/land.d.ts.map +1 -0
- package/dist/cli/commands/land.js +592 -0
- package/dist/cli/commands/land.js.map +1 -0
- package/dist/cli/commands/land.test.d.ts +19 -0
- package/dist/cli/commands/land.test.d.ts.map +1 -0
- package/dist/cli/commands/land.test.js +567 -0
- package/dist/cli/commands/land.test.js.map +1 -0
- package/dist/cli/commands/mcp.e2e.test.js +24 -31
- package/dist/cli/commands/mcp.e2e.test.js.map +1 -1
- package/dist/cli/commands/pipeline.e2e.test.js +28 -31
- package/dist/cli/commands/pipeline.e2e.test.js.map +1 -1
- package/dist/cli/commands/pre-push-guard.d.ts +74 -0
- package/dist/cli/commands/pre-push-guard.d.ts.map +1 -0
- package/dist/cli/commands/pre-push-guard.e2e.test.d.ts +24 -0
- package/dist/cli/commands/pre-push-guard.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/pre-push-guard.e2e.test.js +171 -0
- package/dist/cli/commands/pre-push-guard.e2e.test.js.map +1 -0
- package/dist/cli/commands/pre-push-guard.js +212 -0
- package/dist/cli/commands/pre-push-guard.js.map +1 -0
- package/dist/cli/commands/pre-push-guard.test.d.ts +14 -0
- package/dist/cli/commands/pre-push-guard.test.d.ts.map +1 -0
- package/dist/cli/commands/pre-push-guard.test.js +314 -0
- package/dist/cli/commands/pre-push-guard.test.js.map +1 -0
- package/dist/cli/commands/quickstart-expanded.e2e.test.d.ts +23 -0
- package/dist/cli/commands/quickstart-expanded.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/quickstart-expanded.e2e.test.js +152 -0
- package/dist/cli/commands/quickstart-expanded.e2e.test.js.map +1 -0
- package/dist/cli/commands/quickstart.d.ts +0 -1
- package/dist/cli/commands/quickstart.d.ts.map +1 -1
- package/dist/cli/commands/quickstart.js +9 -83
- package/dist/cli/commands/quickstart.js.map +1 -1
- package/dist/cli/commands/quickstart.test.js +8 -129
- package/dist/cli/commands/quickstart.test.js.map +1 -1
- package/dist/cli/commands/update.d.ts +35 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.e2e.test.d.ts +24 -0
- package/dist/cli/commands/update.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/update.e2e.test.js +240 -0
- package/dist/cli/commands/update.e2e.test.js.map +1 -0
- package/dist/cli/commands/update.js +255 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/core/agents/frontmatter.test.js +1 -1
- package/dist/core/agents/frontmatter.test.js.map +1 -1
- package/dist/core/agents/handoffs.test.js +1 -1
- package/dist/core/agents/handoffs.test.js.map +1 -1
- package/dist/core/agents/loader.d.ts +4 -2
- package/dist/core/agents/loader.d.ts.map +1 -1
- package/dist/core/agents/loader.js +5 -3
- package/dist/core/agents/loader.js.map +1 -1
- package/dist/core/agents/loader.test.js +42 -4
- package/dist/core/agents/loader.test.js.map +1 -1
- package/dist/core/agents/suite.test.js +12 -9
- package/dist/core/agents/suite.test.js.map +1 -1
- package/dist/core/agents/tools.test.js +15 -9
- package/dist/core/agents/tools.test.js.map +1 -1
- package/dist/core/agents/types.test.js +1 -1
- package/dist/core/agents/types.test.js.map +1 -1
- package/dist/core/skills/loader.test.js +1 -1
- package/dist/core/skills/loader.test.js.map +1 -1
- package/dist/index.d.ts +3 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -12
- package/dist/index.js.map +1 -1
- package/dist/lib/pathValidation.d.ts +0 -5
- package/dist/lib/pathValidation.d.ts.map +1 -1
- package/dist/lib/pathValidation.js +0 -11
- package/dist/lib/pathValidation.js.map +1 -1
- package/dist/lib/pathValidation.test.js +2 -14
- package/dist/lib/pathValidation.test.js.map +1 -1
- package/package.json +13 -10
- package/sbom.json +1927 -847
- package/templates/.github/agents/beth.agent.md +331 -105
- package/templates/.github/agents/developer.agent.md +73 -102
- package/templates/.github/agents/product-manager.agent.md +24 -68
- package/templates/.github/agents/researcher.agent.md +21 -69
- package/templates/.github/agents/security-reviewer.agent.md +39 -82
- package/templates/.github/agents/tester.agent.md +44 -65
- package/templates/.github/agents/ux-designer.agent.md +25 -76
- package/templates/.github/copilot-instructions.md +246 -225
- package/templates/.github/copilot-mcp-config.json +12 -0
- package/templates/.github/dependabot.yml +68 -0
- package/templates/.github/hooks/scripts/inject-skills.mjs +139 -0
- package/templates/.github/hooks/scripts/verify-skills.mjs +47 -0
- package/templates/.github/hooks/skill-enforcement.json +18 -0
- package/templates/.github/pull_request_template.md +48 -0
- package/templates/.github/skills/framer-components/SKILL.md +0 -0
- package/templates/.github/skills/prd/SKILL.md +0 -0
- package/templates/.github/skills/security-analysis/SKILL.md +798 -798
- package/templates/.github/skills/shadcn-ui/SKILL.md +561 -561
- package/templates/.github/skills/vercel-react-best-practices/AGENTS.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/SKILL.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-parallel.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-auth-actions.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-dedup-props.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-serialization.md +0 -0
- package/templates/.github/skills/web-design-guidelines/SKILL.md +0 -0
- package/templates/.vscode/settings.json +16 -16
- package/templates/AGENTS.md +103 -54
- package/templates/Backlog.md +80 -80
- package/templates/mcp.json.example +0 -3
- package/assets/beth-portrait-small.txt +0 -13
- package/assets/beth-portrait.txt +0 -60
- package/bin/beth-animation.sh +0 -155
- package/bin/lib/animation.js +0 -189
- package/bin/lib/pathValidation.js +0 -233
- package/bin/lib/pathValidation.test.js +0 -280
- package/dist/cli/commands/client-config.d.ts +0 -31
- package/dist/cli/commands/client-config.d.ts.map +0 -1
- package/dist/cli/commands/client-config.e2e.test.d.ts +0 -15
- package/dist/cli/commands/client-config.e2e.test.d.ts.map +0 -1
- package/dist/cli/commands/client-config.e2e.test.js +0 -556
- package/dist/cli/commands/client-config.e2e.test.js.map +0 -1
- package/dist/cli/commands/client-config.js +0 -73
- package/dist/cli/commands/client-config.js.map +0 -1
- package/dist/cli/commands/client-config.test.d.ts +0 -6
- package/dist/cli/commands/client-config.test.d.ts.map +0 -1
- package/dist/cli/commands/client-config.test.js +0 -133
- package/dist/cli/commands/client-config.test.js.map +0 -1
- package/dist/cli/commands/init-quickstart.e2e.test.d.ts +0 -11
- package/dist/cli/commands/init-quickstart.e2e.test.d.ts.map +0 -1
- package/dist/cli/commands/init-quickstart.e2e.test.js +0 -221
- package/dist/cli/commands/init-quickstart.e2e.test.js.map +0 -1
- package/dist/core/context.d.ts +0 -171
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/context.js +0 -353
- package/dist/core/context.js.map +0 -1
- package/dist/core/context.test.d.ts +0 -8
- package/dist/core/context.test.d.ts.map +0 -1
- package/dist/core/context.test.js +0 -253
- package/dist/core/context.test.js.map +0 -1
- package/dist/core/handoffs.d.ts +0 -151
- package/dist/core/handoffs.d.ts.map +0 -1
- package/dist/core/handoffs.js +0 -220
- package/dist/core/handoffs.js.map +0 -1
- package/dist/core/handoffs.test.d.ts +0 -8
- package/dist/core/handoffs.test.d.ts.map +0 -1
- package/dist/core/handoffs.test.js +0 -231
- package/dist/core/handoffs.test.js.map +0 -1
- package/dist/core/orchestrator.d.ts +0 -246
- package/dist/core/orchestrator.d.ts.map +0 -1
- package/dist/core/orchestrator.js +0 -514
- package/dist/core/orchestrator.js.map +0 -1
- package/dist/core/orchestrator.test.d.ts +0 -8
- package/dist/core/orchestrator.test.d.ts.map +0 -1
- package/dist/core/orchestrator.test.js +0 -517
- package/dist/core/orchestrator.test.js.map +0 -1
- package/dist/core/router.d.ts +0 -102
- package/dist/core/router.d.ts.map +0 -1
- package/dist/core/router.js +0 -178
- package/dist/core/router.js.map +0 -1
- package/dist/core/router.test.d.ts +0 -8
- package/dist/core/router.test.d.ts.map +0 -1
- package/dist/core/router.test.js +0 -215
- package/dist/core/router.test.js.map +0 -1
- package/dist/init.test.js +0 -288
- package/dist/providers/azure.d.ts +0 -147
- package/dist/providers/azure.d.ts.map +0 -1
- package/dist/providers/azure.js +0 -491
- package/dist/providers/azure.js.map +0 -1
- package/dist/providers/azure.test.d.ts +0 -11
- package/dist/providers/azure.test.d.ts.map +0 -1
- package/dist/providers/azure.test.js +0 -330
- package/dist/providers/azure.test.js.map +0 -1
- package/dist/providers/config.d.ts +0 -87
- package/dist/providers/config.d.ts.map +0 -1
- package/dist/providers/config.js +0 -193
- package/dist/providers/config.js.map +0 -1
- package/dist/providers/config.test.d.ts +0 -7
- package/dist/providers/config.test.d.ts.map +0 -1
- package/dist/providers/config.test.js +0 -370
- package/dist/providers/config.test.js.map +0 -1
- package/dist/providers/index.d.ts +0 -18
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js +0 -14
- package/dist/providers/index.js.map +0 -1
- package/dist/providers/interface.d.ts +0 -191
- package/dist/providers/interface.d.ts.map +0 -1
- package/dist/providers/interface.js +0 -94
- package/dist/providers/interface.js.map +0 -1
- package/dist/providers/retry.d.ts +0 -128
- package/dist/providers/retry.d.ts.map +0 -1
- package/dist/providers/retry.js +0 -205
- package/dist/providers/retry.js.map +0 -1
- package/dist/providers/retry.test.d.ts +0 -7
- package/dist/providers/retry.test.d.ts.map +0 -1
- package/dist/providers/retry.test.js +0 -439
- package/dist/providers/retry.test.js.map +0 -1
- package/dist/providers/streaming.d.ts +0 -157
- package/dist/providers/streaming.d.ts.map +0 -1
- package/dist/providers/streaming.js +0 -233
- package/dist/providers/streaming.js.map +0 -1
- package/dist/providers/streaming.test.d.ts +0 -7
- package/dist/providers/streaming.test.d.ts.map +0 -1
- package/dist/providers/streaming.test.js +0 -372
- package/dist/providers/streaming.test.js.map +0 -1
- package/dist/providers/types.d.ts +0 -209
- package/dist/providers/types.d.ts.map +0 -1
- package/dist/providers/types.js +0 -53
- package/dist/providers/types.js.map +0 -1
- package/dist/providers/types.test.d.ts +0 -7
- package/dist/providers/types.test.d.ts.map +0 -1
- package/dist/providers/types.test.js +0 -141
- package/dist/providers/types.test.js.map +0 -1
- package/dist/tools/cli/beads.d.ts +0 -27
- package/dist/tools/cli/beads.d.ts.map +0 -1
- package/dist/tools/cli/beads.js +0 -172
- package/dist/tools/cli/beads.js.map +0 -1
- package/dist/tools/cli/beads.test.d.ts +0 -8
- package/dist/tools/cli/beads.test.d.ts.map +0 -1
- package/dist/tools/cli/beads.test.js +0 -264
- package/dist/tools/cli/beads.test.js.map +0 -1
- package/dist/tools/cli/editFile.d.ts +0 -17
- package/dist/tools/cli/editFile.d.ts.map +0 -1
- package/dist/tools/cli/editFile.js +0 -125
- package/dist/tools/cli/editFile.js.map +0 -1
- package/dist/tools/cli/editFile.test.d.ts +0 -8
- package/dist/tools/cli/editFile.test.d.ts.map +0 -1
- package/dist/tools/cli/editFile.test.js +0 -177
- package/dist/tools/cli/editFile.test.js.map +0 -1
- package/dist/tools/cli/readFile.d.ts +0 -25
- package/dist/tools/cli/readFile.d.ts.map +0 -1
- package/dist/tools/cli/readFile.js +0 -118
- package/dist/tools/cli/readFile.js.map +0 -1
- package/dist/tools/cli/readFile.test.d.ts +0 -8
- package/dist/tools/cli/readFile.test.d.ts.map +0 -1
- package/dist/tools/cli/readFile.test.js +0 -194
- package/dist/tools/cli/readFile.test.js.map +0 -1
- package/dist/tools/cli/search.d.ts +0 -16
- package/dist/tools/cli/search.d.ts.map +0 -1
- package/dist/tools/cli/search.js +0 -261
- package/dist/tools/cli/search.js.map +0 -1
- package/dist/tools/cli/search.test.d.ts +0 -8
- package/dist/tools/cli/search.test.d.ts.map +0 -1
- package/dist/tools/cli/search.test.js +0 -172
- package/dist/tools/cli/search.test.js.map +0 -1
- package/dist/tools/cli/subagent.d.ts +0 -43
- package/dist/tools/cli/subagent.d.ts.map +0 -1
- package/dist/tools/cli/subagent.js +0 -99
- package/dist/tools/cli/subagent.js.map +0 -1
- package/dist/tools/cli/subagent.test.d.ts +0 -8
- package/dist/tools/cli/subagent.test.d.ts.map +0 -1
- package/dist/tools/cli/subagent.test.js +0 -190
- package/dist/tools/cli/subagent.test.js.map +0 -1
- package/dist/tools/cli/terminal.d.ts +0 -19
- package/dist/tools/cli/terminal.d.ts.map +0 -1
- package/dist/tools/cli/terminal.js +0 -164
- package/dist/tools/cli/terminal.js.map +0 -1
- package/dist/tools/cli/terminal.test.d.ts +0 -8
- package/dist/tools/cli/terminal.test.d.ts.map +0 -1
- package/dist/tools/cli/terminal.test.js +0 -161
- package/dist/tools/cli/terminal.test.js.map +0 -1
- package/dist/tools/index.d.ts +0 -25
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js +0 -41
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/interface.d.ts +0 -64
- package/dist/tools/interface.d.ts.map +0 -1
- package/dist/tools/interface.js +0 -37
- package/dist/tools/interface.js.map +0 -1
- package/dist/tools/interface.test.d.ts +0 -7
- package/dist/tools/interface.test.d.ts.map +0 -1
- package/dist/tools/interface.test.js +0 -179
- package/dist/tools/interface.test.js.map +0 -1
- package/dist/tools/mcp/bridge.d.ts +0 -48
- package/dist/tools/mcp/bridge.d.ts.map +0 -1
- package/dist/tools/mcp/bridge.js +0 -128
- package/dist/tools/mcp/bridge.js.map +0 -1
- package/dist/tools/mcp/bridge.test.d.ts +0 -8
- package/dist/tools/mcp/bridge.test.d.ts.map +0 -1
- package/dist/tools/mcp/bridge.test.js +0 -300
- package/dist/tools/mcp/bridge.test.js.map +0 -1
- package/dist/tools/mcp/client.d.ts +0 -135
- package/dist/tools/mcp/client.d.ts.map +0 -1
- package/dist/tools/mcp/client.js +0 -263
- package/dist/tools/mcp/client.js.map +0 -1
- package/dist/tools/mcp/client.test.d.ts +0 -8
- package/dist/tools/mcp/client.test.d.ts.map +0 -1
- package/dist/tools/mcp/client.test.js +0 -390
- package/dist/tools/mcp/client.test.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -82
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/registry.js +0 -99
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/registry.test.d.ts +0 -7
- package/dist/tools/registry.test.d.ts.map +0 -1
- package/dist/tools/registry.test.js +0 -199
- package/dist/tools/registry.test.js.map +0 -1
- package/dist/tools/suite.test.d.ts +0 -11
- package/dist/tools/suite.test.d.ts.map +0 -1
- package/dist/tools/suite.test.js +0 -119
- package/dist/tools/suite.test.js.map +0 -1
- package/dist/tools/types.d.ts +0 -75
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/types.js +0 -30
- package/dist/tools/types.js.map +0 -1
- package/dist/tools/types.test.d.ts +0 -7
- package/dist/tools/types.test.d.ts.map +0 -1
- package/dist/tools/types.test.js +0 -178
- package/dist/tools/types.test.js.map +0 -1
- package/templates/.vscode/mcp.json +0 -20
- package/templates/CLAUDE.md +0 -129
package/bin/cli.js
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { dirname, join, relative } from 'path';
|
|
5
|
-
import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
|
|
5
|
+
import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, chmodSync } from 'fs';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
7
|
import { execSync, spawn } from 'child_process';
|
|
8
|
-
import { validateBeadsPath, validateBinaryPath } from './lib/pathValidation.js';
|
|
9
8
|
|
|
10
9
|
const require = createRequire(import.meta.url);
|
|
11
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -518,59 +517,6 @@ async function checkForUpdates() {
|
|
|
518
517
|
}
|
|
519
518
|
}
|
|
520
519
|
|
|
521
|
-
function getBeadsPath() {
|
|
522
|
-
// Check if bd is available in PATH
|
|
523
|
-
try {
|
|
524
|
-
logDebug('Checking if bd is in PATH...');
|
|
525
|
-
execSync('bd --version', { stdio: 'ignore' });
|
|
526
|
-
logDebug('Found bd in PATH');
|
|
527
|
-
return 'bd';
|
|
528
|
-
} catch {
|
|
529
|
-
logDebug('bd not in PATH, checking common locations...');
|
|
530
|
-
// Check common installation paths based on platform
|
|
531
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
532
|
-
const isWindows = process.platform === 'win32';
|
|
533
|
-
|
|
534
|
-
const commonPaths = isWindows ? [
|
|
535
|
-
// Windows: npm global, Go bin, local apps
|
|
536
|
-
join(process.env.APPDATA || '', 'npm', 'bd.cmd'),
|
|
537
|
-
join(homeDir, 'AppData', 'Roaming', 'npm', 'bd.cmd'),
|
|
538
|
-
join(homeDir, 'AppData', 'Local', 'Microsoft', 'WindowsApps', 'bd.exe'),
|
|
539
|
-
join(homeDir, 'go', 'bin', 'bd.exe'),
|
|
540
|
-
join(process.env.GOPATH || join(homeDir, 'go'), 'bin', 'bd.exe'),
|
|
541
|
-
] : [
|
|
542
|
-
// Unix: homebrew, npm global, go bin, local bin
|
|
543
|
-
'/opt/homebrew/bin/bd',
|
|
544
|
-
'/usr/local/bin/bd',
|
|
545
|
-
join(homeDir, '.local', 'bin', 'bd'),
|
|
546
|
-
join(homeDir, 'bin', 'bd'),
|
|
547
|
-
join(homeDir, '.npm-global', 'bin', 'bd'),
|
|
548
|
-
join(homeDir, 'go', 'bin', 'bd'),
|
|
549
|
-
join(process.env.GOPATH || join(homeDir, 'go'), 'bin', 'bd'),
|
|
550
|
-
];
|
|
551
|
-
|
|
552
|
-
for (const bdPath of commonPaths) {
|
|
553
|
-
logDebug(`Checking: ${bdPath}`);
|
|
554
|
-
if (existsSync(bdPath)) {
|
|
555
|
-
logDebug(`Found at: ${bdPath}`);
|
|
556
|
-
return bdPath;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
logDebug('bd not found in any common location');
|
|
561
|
-
return null;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
function isBeadsInstalled() {
|
|
566
|
-
return getBeadsPath() !== null;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function isBeadsInitialized(cwd) {
|
|
570
|
-
// Check if .beads directory exists in the project
|
|
571
|
-
return existsSync(join(cwd, '.beads'));
|
|
572
|
-
}
|
|
573
|
-
|
|
574
520
|
async function promptYesNo(question) {
|
|
575
521
|
const readline = await import('readline');
|
|
576
522
|
const rl = readline.createInterface({
|
|
@@ -601,178 +547,46 @@ async function promptForInput(question) {
|
|
|
601
547
|
});
|
|
602
548
|
}
|
|
603
549
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
*
|
|
607
|
-
* SECURITY NOTE - shell:true usage:
|
|
608
|
-
* - Required for cross-platform npm execution (npm.cmd on Windows, npm on Unix)
|
|
609
|
-
* - Arguments are HARDCODED - no user input is passed to the shell
|
|
610
|
-
* - Command injection risk: NONE (no dynamic/user-supplied values)
|
|
611
|
-
*
|
|
612
|
-
* Alternative considered: Using platform-specific binary names (npm.cmd vs npm)
|
|
613
|
-
* would eliminate shell:true but adds complexity and edge cases for non-standard installs.
|
|
614
|
-
*
|
|
615
|
-
* @returns {Promise<boolean>} True if installation succeeded and was verified
|
|
616
|
-
*/
|
|
617
|
-
async function installBeads() {
|
|
618
|
-
const isWindows = process.platform === 'win32';
|
|
619
|
-
const isMac = process.platform === 'darwin';
|
|
620
|
-
|
|
621
|
-
log('\nInstalling beads CLI via npm...', COLORS.cyan);
|
|
622
|
-
logInfo('npm install -g @beads/bd');
|
|
623
|
-
|
|
624
|
-
// SECURITY: shell:true is required for cross-platform npm execution.
|
|
625
|
-
// All arguments are hardcoded constants - no user input reaches the shell.
|
|
626
|
-
return new Promise((resolve) => {
|
|
627
|
-
const child = spawn('npm', ['install', '-g', '@beads/bd'], {
|
|
628
|
-
stdio: 'inherit',
|
|
629
|
-
shell: true
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
child.on('close', (code) => {
|
|
633
|
-
if (code === 0) {
|
|
634
|
-
// CRITICAL: Verify installation actually worked before claiming success
|
|
635
|
-
// npm can exit 0 even when the package isn't properly installed
|
|
636
|
-
const verifiedPath = getBeadsPath();
|
|
637
|
-
if (verifiedPath) {
|
|
638
|
-
logSuccess('beads CLI installed and verified!');
|
|
639
|
-
resolve(true);
|
|
640
|
-
} else {
|
|
641
|
-
logWarning('npm reported success but beads CLI not found in PATH.');
|
|
642
|
-
logInfo('This can happen if npm global bin is not in your PATH.');
|
|
643
|
-
if (globalThis.VERBOSE) {
|
|
644
|
-
showPathDiagnostics();
|
|
645
|
-
} else {
|
|
646
|
-
logInfo('Run with --verbose for PATH diagnostics.');
|
|
647
|
-
}
|
|
648
|
-
console.log('');
|
|
649
|
-
showBeadsAlternatives(isWindows, isMac);
|
|
650
|
-
resolve(false);
|
|
651
|
-
}
|
|
652
|
-
} else {
|
|
653
|
-
logError('npm install failed.');
|
|
654
|
-
console.log('');
|
|
655
|
-
showBeadsAlternatives(isWindows, isMac);
|
|
656
|
-
resolve(false);
|
|
657
|
-
}
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
child.on('error', () => {
|
|
661
|
-
logError('Failed to run npm.');
|
|
662
|
-
logInfo('Make sure npm is installed and in your PATH.');
|
|
663
|
-
resolve(false);
|
|
664
|
-
});
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
function showBeadsAlternatives(isWindows, isMac) {
|
|
669
|
-
logInfo('Alternative installation methods:');
|
|
670
|
-
if (isWindows) {
|
|
671
|
-
logInfo(' PowerShell: irm https://raw.githubusercontent.com/steveyegge/beads/main/install.ps1 | iex');
|
|
672
|
-
logInfo(' Go: go install github.com/steveyegge/beads/cmd/bd@latest');
|
|
673
|
-
} else {
|
|
674
|
-
if (isMac) {
|
|
675
|
-
logInfo(' Homebrew: brew install beads');
|
|
676
|
-
}
|
|
677
|
-
logInfo(' Script: curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash');
|
|
678
|
-
logInfo(' Go: go install github.com/steveyegge/beads/cmd/bd@latest');
|
|
679
|
-
}
|
|
680
|
-
logInfo('');
|
|
681
|
-
logInfo('Learn more: https://github.com/steveyegge/beads');
|
|
682
|
-
}
|
|
550
|
+
const BETH_GUARD_BEGIN = '# --- BEGIN BETH GUARD ---';
|
|
551
|
+
const BETH_GUARD_END = '# --- END BETH GUARD ---';
|
|
683
552
|
|
|
684
553
|
/**
|
|
685
|
-
*
|
|
686
|
-
*
|
|
687
|
-
* SECURITY NOTE - shell:true usage:
|
|
688
|
-
* - bdPath is validated via getBeadsPath() which only returns paths that:
|
|
689
|
-
* 1. Pass execSync('bd --version') verification, OR
|
|
690
|
-
* 2. Exist on disk (verified via existsSync) from a HARDCODED list of paths
|
|
691
|
-
* - Arguments are HARDCODED ('init') - no user input is passed to the shell
|
|
692
|
-
* - Command injection risk: LOW (bdPath is validated, no user input in args)
|
|
693
|
-
*
|
|
694
|
-
* The shell:true is used for PATH resolution consistency, though it could be
|
|
695
|
-
* eliminated since we have an absolute path. Kept for consistency with other
|
|
696
|
-
* spawn calls and to handle edge cases in shell script wrappers.
|
|
697
|
-
*
|
|
698
|
-
* @param {string} cwd - Current working directory (validated by caller)
|
|
699
|
-
* @returns {Promise<boolean>} True if initialization succeeded
|
|
554
|
+
* Generate the shell script to append to the pre-push hook.
|
|
555
|
+
* Pure shell — no Node dependency at hook time for speed.
|
|
700
556
|
*/
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
/**
|
|
737
|
-
* Runs `bd doctor` to verify beads configuration health.
|
|
738
|
-
*
|
|
739
|
-
* SECURITY NOTE - shell:true usage:
|
|
740
|
-
* - bdPath is validated via getBeadsPath() (same as initializeBeads)
|
|
741
|
-
* - Arguments are HARDCODED ('doctor') - no user input is passed to the shell
|
|
742
|
-
* - Command injection risk: LOW (bdPath is validated, no user input in args)
|
|
743
|
-
*
|
|
744
|
-
* @returns {Promise<boolean>} True if bd doctor passed
|
|
745
|
-
*/
|
|
746
|
-
async function runBeadsDoctor() {
|
|
747
|
-
log('\nRunning beads doctor to verify configuration...', COLORS.cyan);
|
|
748
|
-
|
|
749
|
-
const bdPath = getBeadsPath();
|
|
750
|
-
if (!bdPath) {
|
|
751
|
-
logWarning('Cannot run beads doctor: bd not found.');
|
|
752
|
-
return false;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
return new Promise((resolve) => {
|
|
756
|
-
const child = spawn(bdPath, ['doctor'], {
|
|
757
|
-
stdio: 'inherit',
|
|
758
|
-
shell: true,
|
|
759
|
-
});
|
|
760
|
-
|
|
761
|
-
child.on('close', (code) => {
|
|
762
|
-
if (code === 0) {
|
|
763
|
-
logSuccess('beads doctor passed!');
|
|
764
|
-
resolve(true);
|
|
765
|
-
} else {
|
|
766
|
-
logWarning('beads doctor reported issues. Run "bd doctor" manually to investigate.');
|
|
767
|
-
resolve(false);
|
|
768
|
-
}
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
child.on('error', () => {
|
|
772
|
-
logWarning('Failed to run beads doctor. Run "bd doctor" manually.');
|
|
773
|
-
resolve(false);
|
|
774
|
-
});
|
|
775
|
-
});
|
|
557
|
+
function generateGuardScript() {
|
|
558
|
+
return `
|
|
559
|
+
${BETH_GUARD_BEGIN}
|
|
560
|
+
# Branch discipline enforcement — installed by beth-copilot
|
|
561
|
+
# Bypass: BETH_SKIP_PUSH_GUARD=1 git push
|
|
562
|
+
if [ "\$BETH_SKIP_PUSH_GUARD" = "1" ]; then
|
|
563
|
+
echo "⚠ Pre-push guard bypassed (BETH_SKIP_PUSH_GUARD=1)" >&2
|
|
564
|
+
else
|
|
565
|
+
_beth_branch=\$(git branch --show-current 2>/dev/null)
|
|
566
|
+
|
|
567
|
+
# Block pushes from protected branches
|
|
568
|
+
case "\$_beth_branch" in
|
|
569
|
+
main|master)
|
|
570
|
+
echo "✗ Pushing from '\$_beth_branch' is blocked. Work on an epic branch." >&2
|
|
571
|
+
echo " Set BETH_SKIP_PUSH_GUARD=1 to bypass." >&2
|
|
572
|
+
exit 1
|
|
573
|
+
;;
|
|
574
|
+
esac
|
|
575
|
+
|
|
576
|
+
# Warn if not on an epic or release branch
|
|
577
|
+
case "\$_beth_branch" in
|
|
578
|
+
epic/*) ;;
|
|
579
|
+
release/*) ;;
|
|
580
|
+
"")
|
|
581
|
+
echo "⚠ Detached HEAD — no branch name. Proceeding anyway." >&2
|
|
582
|
+
;;
|
|
583
|
+
*)
|
|
584
|
+
echo "⚠ Branch '\$_beth_branch' doesn't follow the epic/<id> convention." >&2
|
|
585
|
+
;;
|
|
586
|
+
esac
|
|
587
|
+
fi
|
|
588
|
+
${BETH_GUARD_END}
|
|
589
|
+
`;
|
|
776
590
|
}
|
|
777
591
|
|
|
778
592
|
function showHelp() {
|
|
@@ -782,65 +596,43 @@ function showHelp() {
|
|
|
782
596
|
${COLORS.bright}Usage:${COLORS.reset}
|
|
783
597
|
npx beth-copilot init [options] Initialize Beth in current directory
|
|
784
598
|
npx beth-copilot doctor Check system health and dependencies
|
|
785
|
-
npx beth-copilot
|
|
599
|
+
npx beth-copilot land [opts] Automated session completion (test, commit, push)
|
|
600
|
+
npx beth-copilot pre-push-guard Run branch discipline checks (used by git hook)
|
|
601
|
+
npx beth-copilot update [options] Update project files to latest templates
|
|
602
|
+
npx beth-copilot quickstart Run init + doctor
|
|
786
603
|
npx beth-copilot help Show this help message
|
|
787
604
|
|
|
788
605
|
${COLORS.bright}Options:${COLORS.reset}
|
|
789
606
|
--force Overwrite existing files
|
|
790
607
|
--skip-backlog Don't create Backlog.md
|
|
791
|
-
--skip-mcp Don't
|
|
792
|
-
--skip-beads Skip beads check (not recommended)
|
|
608
|
+
--skip-mcp Don't create mcp.json.example
|
|
793
609
|
--verbose Show detailed diagnostics on errors
|
|
794
|
-
--
|
|
795
|
-
vscode, copilot-cli, claude-code, all
|
|
610
|
+
--check-only Check for updates without modifying files
|
|
796
611
|
|
|
797
612
|
${COLORS.bright}Examples:${COLORS.reset}
|
|
798
|
-
npx beth-copilot init Set up Beth
|
|
799
|
-
npx beth-copilot init --client vscode VS Code + Copilot only
|
|
800
|
-
npx beth-copilot init --client claude-code Claude Code only
|
|
801
|
-
npx beth-copilot init --client all All clients
|
|
613
|
+
npx beth-copilot init Set up Beth in current project
|
|
802
614
|
npx beth-copilot init --force Overwrite existing Beth files
|
|
803
615
|
npx beth-copilot doctor Verify installation health
|
|
804
616
|
|
|
805
617
|
${COLORS.bright}What gets installed:${COLORS.reset}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
.github/copilot-instructions.md Copilot configuration
|
|
819
|
-
|
|
820
|
-
${COLORS.dim}Claude Code:${COLORS.reset}
|
|
821
|
-
CLAUDE.md Claude Code instructions
|
|
822
|
-
|
|
823
|
-
${COLORS.bright}Supported clients:${COLORS.reset}
|
|
824
|
-
VS Code with GitHub Copilot Full agent orchestration with MCP
|
|
825
|
-
GitHub Copilot CLI Terminal-based with bd CLI
|
|
826
|
-
Claude Code CLAUDE.md + bd setup claude hooks
|
|
618
|
+
.github/agents/ 7 specialized AI agents
|
|
619
|
+
.github/skills/ 8 domain knowledge modules
|
|
620
|
+
.github/copilot-instructions.md Copilot configuration
|
|
621
|
+
.vscode/settings.json Recommended VS Code settings
|
|
622
|
+
AGENTS.md Workflow documentation
|
|
623
|
+
Backlog.md Task tracking file
|
|
624
|
+
mcp.json.example Optional MCP server config
|
|
625
|
+
|
|
626
|
+
${COLORS.bright}After installation:${COLORS.reset}
|
|
627
|
+
1. Open project in VS Code
|
|
628
|
+
2. Open Copilot Chat (Ctrl+Alt+I / Cmd+Alt+I)
|
|
629
|
+
3. Type @Beth to start working
|
|
827
630
|
|
|
828
631
|
${COLORS.bright}Documentation:${COLORS.reset}
|
|
829
632
|
https://github.com/stephschofield/beth
|
|
830
633
|
`);
|
|
831
634
|
}
|
|
832
635
|
|
|
833
|
-
/**
|
|
834
|
-
* Persist client selection to .github/.beth-client.json
|
|
835
|
-
* So quickstart and other commands know which client was configured.
|
|
836
|
-
*/
|
|
837
|
-
function persistClientConfig(cwd, clients) {
|
|
838
|
-
const configDir = join(cwd, '.github');
|
|
839
|
-
const configPath = join(configDir, '.beth-client.json');
|
|
840
|
-
mkdirSync(configDir, { recursive: true });
|
|
841
|
-
writeFileSync(configPath, JSON.stringify(clients, null, 2) + '\n');
|
|
842
|
-
}
|
|
843
|
-
|
|
844
636
|
function copyDirRecursive(src, dest, options = {}) {
|
|
845
637
|
const { force = false, copiedFiles = [] } = options;
|
|
846
638
|
|
|
@@ -889,149 +681,8 @@ function copyDirRecursive(src, dest, options = {}) {
|
|
|
889
681
|
return copiedFiles;
|
|
890
682
|
}
|
|
891
683
|
|
|
892
|
-
/**
|
|
893
|
-
* Prompt the user to select their AI coding client(s).
|
|
894
|
-
* Returns an object with boolean flags for each client.
|
|
895
|
-
*/
|
|
896
|
-
async function promptForClient() {
|
|
897
|
-
console.log('');
|
|
898
|
-
log('Which AI coding tool are you using?', COLORS.bright);
|
|
899
|
-
console.log('');
|
|
900
|
-
console.log(` ${COLORS.cyan}[1]${COLORS.reset} VS Code with GitHub Copilot`);
|
|
901
|
-
console.log(` ${COLORS.cyan}[2]${COLORS.reset} GitHub Copilot CLI (terminal)`);
|
|
902
|
-
console.log(` ${COLORS.cyan}[3]${COLORS.reset} Claude Code`);
|
|
903
|
-
console.log(` ${COLORS.cyan}[a]${COLORS.reset} All of the above`);
|
|
904
|
-
console.log('');
|
|
905
|
-
|
|
906
|
-
const answer = await promptForInput('Enter selection (1/2/3/a, or comma-separated e.g. 1,3):');
|
|
907
|
-
|
|
908
|
-
if (!answer || answer.toLowerCase() === 'a') {
|
|
909
|
-
return { vscode: true, copilotCli: true, claudeCode: true };
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
const selections = answer.split(',').map(s => s.trim());
|
|
913
|
-
return {
|
|
914
|
-
vscode: selections.includes('1'),
|
|
915
|
-
copilotCli: selections.includes('2'),
|
|
916
|
-
claudeCode: selections.includes('3'),
|
|
917
|
-
};
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
/**
|
|
921
|
-
* Parse --client flag value into client selection object.
|
|
922
|
-
*/
|
|
923
|
-
function parseClientFlag(clientArg) {
|
|
924
|
-
if (!clientArg || clientArg === 'all') {
|
|
925
|
-
return { vscode: true, copilotCli: true, claudeCode: true };
|
|
926
|
-
}
|
|
927
|
-
return {
|
|
928
|
-
vscode: clientArg === 'vscode',
|
|
929
|
-
copilotCli: clientArg === 'copilot-cli',
|
|
930
|
-
claudeCode: clientArg === 'claude-code',
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
/**
|
|
935
|
-
* Install beads-mcp (MCP server) for VS Code integration.
|
|
936
|
-
*
|
|
937
|
-
* SECURITY NOTE - shell:true usage:
|
|
938
|
-
* - Required for cross-platform uv/pip execution
|
|
939
|
-
* - Arguments are HARDCODED - no user input is passed to the shell
|
|
940
|
-
* - Command injection risk: NONE (no dynamic/user-supplied values)
|
|
941
|
-
*/
|
|
942
|
-
async function installBeadsMcp() {
|
|
943
|
-
log('\nInstalling beads-mcp (MCP server for VS Code)...', COLORS.cyan);
|
|
944
|
-
|
|
945
|
-
// Try uv first, then pip
|
|
946
|
-
const installers = [
|
|
947
|
-
{ cmd: 'uv', args: ['tool', 'install', 'beads-mcp'], label: 'uv tool install beads-mcp' },
|
|
948
|
-
{ cmd: 'pip', args: ['install', 'beads-mcp'], label: 'pip install beads-mcp' },
|
|
949
|
-
];
|
|
950
|
-
|
|
951
|
-
for (const installer of installers) {
|
|
952
|
-
try {
|
|
953
|
-
execSync(`${installer.cmd} --version`, { stdio: 'ignore' });
|
|
954
|
-
} catch {
|
|
955
|
-
logDebug(`${installer.cmd} not found, trying next installer...`);
|
|
956
|
-
continue;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
logInfo(installer.label);
|
|
960
|
-
|
|
961
|
-
// SECURITY: All arguments are hardcoded constants.
|
|
962
|
-
return new Promise((resolve) => {
|
|
963
|
-
const child = spawn(installer.cmd, installer.args, {
|
|
964
|
-
stdio: 'inherit',
|
|
965
|
-
shell: true,
|
|
966
|
-
});
|
|
967
|
-
|
|
968
|
-
child.on('close', (code) => {
|
|
969
|
-
if (code === 0) {
|
|
970
|
-
logSuccess('beads-mcp installed!');
|
|
971
|
-
resolve(true);
|
|
972
|
-
} else {
|
|
973
|
-
logWarning(`${installer.label} failed.`);
|
|
974
|
-
resolve(false);
|
|
975
|
-
}
|
|
976
|
-
});
|
|
977
|
-
|
|
978
|
-
child.on('error', () => {
|
|
979
|
-
logWarning(`Failed to run ${installer.cmd}.`);
|
|
980
|
-
resolve(false);
|
|
981
|
-
});
|
|
982
|
-
});
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
logWarning('Neither uv nor pip found. Install beads-mcp manually:');
|
|
986
|
-
logInfo(' uv tool install beads-mcp');
|
|
987
|
-
logInfo(' OR: pip install beads-mcp');
|
|
988
|
-
return false;
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
/**
|
|
992
|
-
* Run `bd setup claude` to configure Claude Code integration.
|
|
993
|
-
*
|
|
994
|
-
* SECURITY NOTE - shell:true usage:
|
|
995
|
-
* - bdPath is validated via getBeadsPath()
|
|
996
|
-
* - Arguments are HARDCODED ('setup', 'claude')
|
|
997
|
-
* - Command injection risk: LOW (bdPath validated, no user input in args)
|
|
998
|
-
*/
|
|
999
|
-
async function runBdSetupClaude() {
|
|
1000
|
-
log('\nConfiguring beads for Claude Code...', COLORS.cyan);
|
|
1001
|
-
|
|
1002
|
-
const bdPath = getBeadsPath();
|
|
1003
|
-
if (!bdPath) {
|
|
1004
|
-
logWarning('Cannot run bd setup claude: bd not found.');
|
|
1005
|
-
logInfo('Run manually after installing beads: bd setup claude');
|
|
1006
|
-
return false;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
// SECURITY: bdPath is validated, only hardcoded args.
|
|
1010
|
-
return new Promise((resolve) => {
|
|
1011
|
-
const child = spawn(bdPath, ['setup', 'claude'], {
|
|
1012
|
-
stdio: 'inherit',
|
|
1013
|
-
shell: true,
|
|
1014
|
-
});
|
|
1015
|
-
|
|
1016
|
-
child.on('close', (code) => {
|
|
1017
|
-
if (code === 0) {
|
|
1018
|
-
logSuccess('Claude Code integration configured!');
|
|
1019
|
-
resolve(true);
|
|
1020
|
-
} else {
|
|
1021
|
-
logWarning('bd setup claude failed. Run manually: bd setup claude');
|
|
1022
|
-
resolve(false);
|
|
1023
|
-
}
|
|
1024
|
-
});
|
|
1025
|
-
|
|
1026
|
-
child.on('error', () => {
|
|
1027
|
-
logWarning('Failed to run bd setup claude. Run manually: bd setup claude');
|
|
1028
|
-
resolve(false);
|
|
1029
|
-
});
|
|
1030
|
-
});
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
684
|
async function init(options = {}) {
|
|
1034
|
-
const { force = false, skipBacklog = false, skipMcp = false
|
|
685
|
+
const { force = false, skipBacklog = false, skipMcp = false } = options;
|
|
1035
686
|
const cwd = process.cwd();
|
|
1036
687
|
|
|
1037
688
|
// Check for updates
|
|
@@ -1054,27 +705,6 @@ ${COLORS.yellow}╔════════════════════
|
|
|
1054
705
|
|
|
1055
706
|
log(`${COLORS.yellow}Tip: Run with --verbose for detailed diagnostics if you hit issues.${COLORS.reset}`);
|
|
1056
707
|
|
|
1057
|
-
// Determine which client(s) to configure
|
|
1058
|
-
let clients;
|
|
1059
|
-
if (clientArg) {
|
|
1060
|
-
clients = parseClientFlag(clientArg);
|
|
1061
|
-
} else {
|
|
1062
|
-
clients = await promptForClient();
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
// Validate at least one client selected
|
|
1066
|
-
if (!clients.vscode && !clients.copilotCli && !clients.claudeCode) {
|
|
1067
|
-
logWarning('No client selected. Defaulting to VS Code with GitHub Copilot.');
|
|
1068
|
-
clients.vscode = true;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
const selectedNames = [];
|
|
1072
|
-
if (clients.vscode) selectedNames.push('VS Code + Copilot');
|
|
1073
|
-
if (clients.copilotCli) selectedNames.push('Copilot CLI');
|
|
1074
|
-
if (clients.claudeCode) selectedNames.push('Claude Code');
|
|
1075
|
-
log(`\nConfiguring for: ${COLORS.cyan}${selectedNames.join(', ')}${COLORS.reset}`);
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
708
|
// Check if templates exist
|
|
1079
709
|
if (!existsSync(TEMPLATES_DIR)) {
|
|
1080
710
|
logError('Templates directory not found. Package may be corrupted.');
|
|
@@ -1083,15 +713,13 @@ ${COLORS.yellow}╔════════════════════
|
|
|
1083
713
|
|
|
1084
714
|
const copiedFiles = [];
|
|
1085
715
|
|
|
1086
|
-
//
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
const skillsSrc = join(TEMPLATES_DIR, '.github', 'skills');
|
|
1090
|
-
const skillsDest = join(cwd, '.github', 'skills');
|
|
716
|
+
// Copy .github directory (agents, skills, copilot-instructions.md)
|
|
717
|
+
const githubSrc = join(TEMPLATES_DIR, '.github');
|
|
718
|
+
const githubDest = join(cwd, '.github');
|
|
1091
719
|
|
|
1092
|
-
if (existsSync(
|
|
1093
|
-
log('\nInstalling
|
|
1094
|
-
copyDirRecursive(
|
|
720
|
+
if (existsSync(githubSrc)) {
|
|
721
|
+
log('\nInstalling agents and skills...');
|
|
722
|
+
copyDirRecursive(githubSrc, githubDest, { force, copiedFiles });
|
|
1095
723
|
}
|
|
1096
724
|
|
|
1097
725
|
// Copy AGENTS.md
|
|
@@ -1122,38 +750,33 @@ ${COLORS.yellow}╔════════════════════
|
|
|
1122
750
|
}
|
|
1123
751
|
}
|
|
1124
752
|
|
|
1125
|
-
//
|
|
1126
|
-
if (
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
// .github/agents/ (agent definitions with frontmatter)
|
|
1130
|
-
const agentsSrc = join(TEMPLATES_DIR, '.github', 'agents');
|
|
1131
|
-
const agentsDest = join(cwd, '.github', 'agents');
|
|
1132
|
-
if (existsSync(agentsSrc)) {
|
|
1133
|
-
copyDirRecursive(agentsSrc, agentsDest, { force, copiedFiles });
|
|
1134
|
-
}
|
|
753
|
+
// Copy mcp.json.example (unless skipped)
|
|
754
|
+
if (!skipMcp) {
|
|
755
|
+
const mcpSrc = join(TEMPLATES_DIR, 'mcp.json.example');
|
|
756
|
+
const mcpDest = join(cwd, 'mcp.json.example');
|
|
1135
757
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
if (existsSync(copilotInstructionsSrc)) {
|
|
1140
|
-
if (existsSync(copilotInstructionsDest) && !force) {
|
|
1141
|
-
logWarning('Skipped (exists): .github/copilot-instructions.md');
|
|
758
|
+
if (existsSync(mcpSrc)) {
|
|
759
|
+
if (existsSync(mcpDest) && !force) {
|
|
760
|
+
logWarning('Skipped (exists): mcp.json.example');
|
|
1142
761
|
} else {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
copiedFiles.push('.github/copilot-instructions.md');
|
|
762
|
+
copyFileSync(mcpSrc, mcpDest);
|
|
763
|
+
copiedFiles.push('mcp.json.example');
|
|
1146
764
|
}
|
|
1147
765
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Copy .vscode/settings.json (recommended settings for agent mode)
|
|
769
|
+
const vscodeSrc = join(TEMPLATES_DIR, '.vscode');
|
|
770
|
+
const vscodeDest = join(cwd, '.vscode');
|
|
771
|
+
|
|
772
|
+
if (existsSync(vscodeSrc)) {
|
|
1151
773
|
if (!existsSync(vscodeDest)) {
|
|
1152
774
|
mkdirSync(vscodeDest, { recursive: true });
|
|
1153
775
|
}
|
|
1154
776
|
|
|
1155
|
-
const settingsSrc = join(
|
|
777
|
+
const settingsSrc = join(vscodeSrc, 'settings.json');
|
|
1156
778
|
const settingsDest = join(vscodeDest, 'settings.json');
|
|
779
|
+
|
|
1157
780
|
if (existsSync(settingsSrc)) {
|
|
1158
781
|
if (existsSync(settingsDest) && !force) {
|
|
1159
782
|
logWarning('Skipped (exists): .vscode/settings.json');
|
|
@@ -1162,61 +785,8 @@ ${COLORS.yellow}╔════════════════════
|
|
|
1162
785
|
copiedFiles.push('.vscode/settings.json');
|
|
1163
786
|
}
|
|
1164
787
|
}
|
|
1165
|
-
|
|
1166
|
-
// .vscode/mcp.json (beads + shadcn + playwright + deepwiki)
|
|
1167
|
-
if (!skipMcp) {
|
|
1168
|
-
const mcpJsonSrc = join(TEMPLATES_DIR, '.vscode', 'mcp.json');
|
|
1169
|
-
const mcpJsonDest = join(vscodeDest, 'mcp.json');
|
|
1170
|
-
if (existsSync(mcpJsonSrc)) {
|
|
1171
|
-
if (existsSync(mcpJsonDest) && !force) {
|
|
1172
|
-
logWarning('Skipped (exists): .vscode/mcp.json');
|
|
1173
|
-
} else {
|
|
1174
|
-
copyFileSync(mcpJsonSrc, mcpJsonDest);
|
|
1175
|
-
copiedFiles.push('.vscode/mcp.json');
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
788
|
}
|
|
1180
789
|
|
|
1181
|
-
// === COPILOT CLI FILES ===
|
|
1182
|
-
if (clients.copilotCli && !clients.vscode) {
|
|
1183
|
-
// Only install copilot-instructions.md if VS Code didn't already do it
|
|
1184
|
-
log('\nInstalling Copilot CLI configuration...');
|
|
1185
|
-
|
|
1186
|
-
const copilotInstructionsSrc = join(TEMPLATES_DIR, '.github', 'copilot-instructions.md');
|
|
1187
|
-
const copilotInstructionsDest = join(cwd, '.github', 'copilot-instructions.md');
|
|
1188
|
-
if (existsSync(copilotInstructionsSrc)) {
|
|
1189
|
-
if (existsSync(copilotInstructionsDest) && !force) {
|
|
1190
|
-
logWarning('Skipped (exists): .github/copilot-instructions.md');
|
|
1191
|
-
} else {
|
|
1192
|
-
mkdirSync(join(cwd, '.github'), { recursive: true });
|
|
1193
|
-
copyFileSync(copilotInstructionsSrc, copilotInstructionsDest);
|
|
1194
|
-
copiedFiles.push('.github/copilot-instructions.md');
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
// === CLAUDE CODE FILES ===
|
|
1200
|
-
if (clients.claudeCode) {
|
|
1201
|
-
log('\nInstalling Claude Code configuration...');
|
|
1202
|
-
|
|
1203
|
-
// CLAUDE.md
|
|
1204
|
-
const claudeMdSrc = join(TEMPLATES_DIR, 'CLAUDE.md');
|
|
1205
|
-
const claudeMdDest = join(cwd, 'CLAUDE.md');
|
|
1206
|
-
if (existsSync(claudeMdSrc)) {
|
|
1207
|
-
if (existsSync(claudeMdDest) && !force) {
|
|
1208
|
-
logWarning('Skipped (exists): CLAUDE.md');
|
|
1209
|
-
} else {
|
|
1210
|
-
copyFileSync(claudeMdSrc, claudeMdDest);
|
|
1211
|
-
copiedFiles.push('CLAUDE.md');
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
// Persist client selection for quickstart and other commands
|
|
1217
|
-
persistClientConfig(cwd, clients);
|
|
1218
|
-
copiedFiles.push('.github/.beth-client.json');
|
|
1219
|
-
|
|
1220
790
|
// Summary
|
|
1221
791
|
console.log('');
|
|
1222
792
|
if (copiedFiles.length > 0) {
|
|
@@ -1226,190 +796,20 @@ ${COLORS.yellow}╔════════════════════
|
|
|
1226
796
|
logWarning('No files were copied. Use --force to overwrite existing files.');
|
|
1227
797
|
}
|
|
1228
798
|
|
|
1229
|
-
// Check for beads CLI (REQUIRED for Beth)
|
|
1230
|
-
if (!skipBeads) {
|
|
1231
|
-
console.log('');
|
|
1232
|
-
log('Checking beads (required for task tracking)...', COLORS.cyan);
|
|
1233
|
-
|
|
1234
|
-
let bdPath = getBeadsPath();
|
|
1235
|
-
|
|
1236
|
-
// Loop until beads is installed
|
|
1237
|
-
while (!bdPath) {
|
|
1238
|
-
logWarning('beads CLI is not installed.');
|
|
1239
|
-
logInfo('Beth requires beads for task tracking. Agents use it to coordinate work.');
|
|
1240
|
-
logInfo('Learn more: https://github.com/steveyegge/beads');
|
|
1241
|
-
console.log('');
|
|
1242
|
-
|
|
1243
|
-
const shouldInstallBeads = await promptYesNo('Install beads CLI now? (required)');
|
|
1244
|
-
if (shouldInstallBeads) {
|
|
1245
|
-
const installed = await installBeads();
|
|
1246
|
-
if (installed) {
|
|
1247
|
-
// Re-check for beads after installation
|
|
1248
|
-
bdPath = getBeadsPath();
|
|
1249
|
-
if (!bdPath) {
|
|
1250
|
-
console.log('');
|
|
1251
|
-
logWarning('beads installed but not found in common paths.');
|
|
1252
|
-
logInfo('The installer may have placed it in a custom location.');
|
|
1253
|
-
console.log('');
|
|
1254
|
-
logInfo('Please try one of these options:');
|
|
1255
|
-
logInfo(' 1. Open a NEW terminal and run: npx beth-copilot init');
|
|
1256
|
-
logInfo(' 2. Add ~/.local/bin to your PATH and retry');
|
|
1257
|
-
logInfo(' 3. Run: source ~/.bashrc (or ~/.zshrc) then retry');
|
|
1258
|
-
console.log('');
|
|
1259
|
-
|
|
1260
|
-
const retryCheck = await promptYesNo('Retry detection? (select No to enter path manually)');
|
|
1261
|
-
if (retryCheck) {
|
|
1262
|
-
bdPath = getBeadsPath();
|
|
1263
|
-
continue;
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
// Allow manual path entry
|
|
1267
|
-
const customPath = await promptForInput('Enter full path to bd binary (or press Enter to retry installation):');
|
|
1268
|
-
if (customPath) {
|
|
1269
|
-
const validation = validateBeadsPath(customPath);
|
|
1270
|
-
if (validation.valid) {
|
|
1271
|
-
bdPath = validation.normalizedPath;
|
|
1272
|
-
logSuccess(`Found beads at: ${bdPath}`);
|
|
1273
|
-
} else {
|
|
1274
|
-
logError(`Invalid path: ${validation.error}`);
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
} else {
|
|
1279
|
-
console.log('');
|
|
1280
|
-
logError('Installation script failed.');
|
|
1281
|
-
logInfo('You can try installing manually:');
|
|
1282
|
-
logInfo(' curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash');
|
|
1283
|
-
console.log('');
|
|
1284
|
-
}
|
|
1285
|
-
} else {
|
|
1286
|
-
console.log('');
|
|
1287
|
-
logError('beads is REQUIRED for Beth to function.');
|
|
1288
|
-
logInfo('Beth agents use beads to track tasks, dependencies, and coordinate work.');
|
|
1289
|
-
logInfo('Without beads, the multi-agent workflow will not work correctly.');
|
|
1290
|
-
console.log('');
|
|
1291
|
-
|
|
1292
|
-
const tryAgain = await promptYesNo('Would you like to try installing beads?');
|
|
1293
|
-
if (!tryAgain) {
|
|
1294
|
-
logError('Cannot continue without beads. Exiting.');
|
|
1295
|
-
logInfo('Install beads manually and run "npx beth-copilot init" again:');
|
|
1296
|
-
logInfo(' npm install -g @beads/bd');
|
|
1297
|
-
process.exit(1);
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
// Show path info if not in standard PATH
|
|
1303
|
-
if (bdPath && bdPath !== 'bd') {
|
|
1304
|
-
logSuccess(`beads CLI found at: ${bdPath}`);
|
|
1305
|
-
const isWindows = process.platform === 'win32';
|
|
1306
|
-
if (isWindows) {
|
|
1307
|
-
logInfo('Tip: Ensure npm global bin is in your PATH to use "bd" directly.');
|
|
1308
|
-
} else {
|
|
1309
|
-
logInfo('Tip: Add ~/.local/bin or npm global bin to your PATH to use "bd" directly.');
|
|
1310
|
-
}
|
|
1311
|
-
} else {
|
|
1312
|
-
logSuccess('beads CLI is installed');
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
// Initialize beads in the project if not already done
|
|
1316
|
-
if (!isBeadsInitialized(cwd)) {
|
|
1317
|
-
logInfo('beads not initialized in this project.');
|
|
1318
|
-
let initialized = false;
|
|
1319
|
-
|
|
1320
|
-
while (!initialized) {
|
|
1321
|
-
const shouldInitBeads = await promptYesNo('Initialize beads now? (required)');
|
|
1322
|
-
if (shouldInitBeads) {
|
|
1323
|
-
initialized = await initializeBeads(cwd);
|
|
1324
|
-
if (!initialized) {
|
|
1325
|
-
logWarning('Initialization failed. Let\'s try again.');
|
|
1326
|
-
}
|
|
1327
|
-
} else {
|
|
1328
|
-
logError('beads must be initialized for Beth to work correctly.');
|
|
1329
|
-
logInfo('The .beads directory stores task tracking data used by all agents.');
|
|
1330
|
-
console.log('');
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
} else {
|
|
1334
|
-
logSuccess('beads is initialized in this project');
|
|
1335
|
-
}
|
|
1336
|
-
} else {
|
|
1337
|
-
logWarning('Skipped beads check (--skip-beads). Beth may not function correctly.');
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
// Run bd doctor to verify beads configuration
|
|
1341
|
-
if (!skipBeads && getBeadsPath() && isBeadsInitialized(cwd)) {
|
|
1342
|
-
await runBeadsDoctor();
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
// === CLIENT-SPECIFIC BEADS INTEGRATION ===
|
|
1346
|
-
if (!skipBeads && getBeadsPath() && isBeadsInitialized(cwd)) {
|
|
1347
|
-
// VS Code: install beads-mcp (MCP server)
|
|
1348
|
-
if (clients.vscode) {
|
|
1349
|
-
const shouldInstallMcp = await promptYesNo('Install beads-mcp for VS Code MCP integration?');
|
|
1350
|
-
if (shouldInstallMcp) {
|
|
1351
|
-
await installBeadsMcp();
|
|
1352
|
-
} else {
|
|
1353
|
-
logInfo('Skipped beads-mcp. Install later with: uv tool install beads-mcp');
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
// Claude Code: run bd setup claude
|
|
1358
|
-
if (clients.claudeCode) {
|
|
1359
|
-
const shouldSetupClaude = await promptYesNo('Configure beads for Claude Code? (bd setup claude)');
|
|
1360
|
-
if (shouldSetupClaude) {
|
|
1361
|
-
await runBdSetupClaude();
|
|
1362
|
-
} else {
|
|
1363
|
-
logInfo('Skipped Claude Code setup. Run later: bd setup claude');
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
799
|
// Final verification
|
|
1369
800
|
console.log('');
|
|
1370
801
|
log('Verifying installation...', COLORS.cyan);
|
|
1371
|
-
|
|
1372
|
-
const finalBeadsOk = skipBeads || getBeadsPath();
|
|
1373
|
-
const finalBeadsInit = skipBeads || isBeadsInitialized(cwd);
|
|
1374
|
-
|
|
1375
|
-
if (finalBeadsOk && finalBeadsInit) {
|
|
1376
|
-
logSuccess('All dependencies installed and configured!');
|
|
1377
|
-
} else {
|
|
1378
|
-
if (!finalBeadsOk) logError('beads CLI not found');
|
|
1379
|
-
if (!finalBeadsInit) logError('beads not initialized in project');
|
|
1380
|
-
logError('Setup incomplete. Please resolve issues above and run init again.');
|
|
1381
|
-
process.exit(1);
|
|
1382
|
-
}
|
|
802
|
+
logSuccess('All files installed and configured!');
|
|
1383
803
|
|
|
1384
|
-
// Next steps
|
|
1385
|
-
console.log(
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
if (clients.vscode) {
|
|
1389
|
-
console.log(`
|
|
1390
|
-
${COLORS.bright}VS Code + Copilot:${COLORS.reset}
|
|
804
|
+
// Next steps
|
|
805
|
+
console.log(`
|
|
806
|
+
${COLORS.bright}Next steps:${COLORS.reset}
|
|
1391
807
|
1. Open this project in VS Code
|
|
1392
808
|
2. Open Copilot Chat (${COLORS.cyan}Ctrl+Alt+I${COLORS.reset} / ${COLORS.cyan}Cmd+Alt+I${COLORS.reset})
|
|
1393
|
-
3. Type ${COLORS.cyan}@Beth${COLORS.reset} to start
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
if (clients.copilotCli) {
|
|
1397
|
-
console.log(`
|
|
1398
|
-
${COLORS.bright}Copilot CLI:${COLORS.reset}
|
|
1399
|
-
1. Run ${COLORS.cyan}copilot${COLORS.reset} in your project directory
|
|
1400
|
-
2. Beth's instructions are in ${COLORS.cyan}.github/copilot-instructions.md${COLORS.reset}
|
|
1401
|
-
3. Use ${COLORS.cyan}bd ready${COLORS.reset} to find work, ${COLORS.cyan}bd create${COLORS.reset} to track tasks`);
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
if (clients.claudeCode) {
|
|
1405
|
-
console.log(`
|
|
1406
|
-
${COLORS.bright}Claude Code:${COLORS.reset}
|
|
1407
|
-
1. Run ${COLORS.cyan}claude${COLORS.reset} in your project directory
|
|
1408
|
-
2. Beth's instructions are in ${COLORS.cyan}CLAUDE.md${COLORS.reset}
|
|
1409
|
-
3. Use ${COLORS.cyan}bd ready${COLORS.reset} to find work, ${COLORS.cyan}bd prime${COLORS.reset} for session context`);
|
|
1410
|
-
}
|
|
809
|
+
3. Type ${COLORS.cyan}@Beth${COLORS.reset} to start - she's your orchestrator
|
|
810
|
+
|
|
811
|
+
${COLORS.bright}Pro tip:${COLORS.reset} Start every session with ${COLORS.cyan}@Beth${COLORS.reset} and let her route work to the right specialists.
|
|
1411
812
|
|
|
1412
|
-
console.log(`
|
|
1413
813
|
${COLORS.bright}Documentation:${COLORS.reset}
|
|
1414
814
|
https://github.com/stephschofield/beth
|
|
1415
815
|
|
|
@@ -1418,13 +818,16 @@ ${COLORS.cyan}"They broke my wings and forgot I had claws."${COLORS.reset}
|
|
|
1418
818
|
}
|
|
1419
819
|
|
|
1420
820
|
// Input validation constants
|
|
1421
|
-
const ALLOWED_COMMANDS = ['init', 'help', '--help', '-h', 'doctor', 'quickstart'];
|
|
1422
|
-
const ALLOWED_FLAGS = ['--force', '--skip-backlog', '--skip-mcp', '--skip-
|
|
1423
|
-
const ALLOWED_CLIENTS = ['vscode', 'copilot-cli', 'claude-code', 'all'];
|
|
821
|
+
const ALLOWED_COMMANDS = ['init', 'help', '--help', '-h', 'doctor', 'quickstart', 'pre-push-guard', 'update', 'land'];
|
|
822
|
+
const ALLOWED_FLAGS = ['--force', '--skip-backlog', '--skip-mcp', '--verbose', '--reason', '-r', '-f', '--skip-tests', '--message', '-m', '--dry-run', '--check-only'];
|
|
1424
823
|
const MAX_ARG_LENGTH = 50;
|
|
1425
824
|
|
|
1426
825
|
// Validate and sanitize input
|
|
1427
826
|
function validateArgs(args) {
|
|
827
|
+
// The 'land' and 'update' commands handle their own arg validation
|
|
828
|
+
const command = args[0]?.toLowerCase();
|
|
829
|
+
if (command === 'land' || command === 'update') return;
|
|
830
|
+
|
|
1428
831
|
for (const arg of args) {
|
|
1429
832
|
// Prevent excessively long arguments (log injection, DoS)
|
|
1430
833
|
if (arg.length > MAX_ARG_LENGTH) {
|
|
@@ -1445,40 +848,25 @@ validateArgs(args);
|
|
|
1445
848
|
|
|
1446
849
|
const command = args[0]?.toLowerCase();
|
|
1447
850
|
|
|
1448
|
-
// Parse --client flag value (e.g. --client vscode)
|
|
1449
|
-
let clientArg = null;
|
|
1450
|
-
const clientFlagIndex = args.indexOf('--client');
|
|
1451
|
-
if (clientFlagIndex !== -1 && clientFlagIndex + 1 < args.length) {
|
|
1452
|
-
clientArg = args[clientFlagIndex + 1].toLowerCase();
|
|
1453
|
-
if (!ALLOWED_CLIENTS.includes(clientArg)) {
|
|
1454
|
-
logError(`Invalid client: ${clientArg.slice(0, MAX_ARG_LENGTH)}`);
|
|
1455
|
-
console.log(`Valid clients: ${ALLOWED_CLIENTS.join(', ')}`);
|
|
1456
|
-
process.exit(1);
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
851
|
const options = {
|
|
1461
852
|
force: args.includes('--force'),
|
|
1462
853
|
skipBacklog: args.includes('--skip-backlog'),
|
|
1463
854
|
skipMcp: args.includes('--skip-mcp'),
|
|
1464
|
-
skipBeads: args.includes('--skip-beads'),
|
|
1465
855
|
verbose: args.includes('--verbose'),
|
|
1466
|
-
client: clientArg,
|
|
1467
856
|
};
|
|
1468
857
|
|
|
1469
858
|
// Set global verbose flag for logDebug
|
|
1470
859
|
globalThis.VERBOSE = options.verbose;
|
|
1471
860
|
|
|
1472
861
|
// Validate unknown flags (exclude --help which is handled as a command)
|
|
1473
|
-
//
|
|
1474
|
-
|
|
1475
|
-
const unknownFlags = args.filter(
|
|
1476
|
-
|
|
1477
|
-
);
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
process.exit(1);
|
|
862
|
+
// Skip for 'land' and 'update' commands which handle their own arg parsing
|
|
863
|
+
if (command !== 'land' && command !== 'update') {
|
|
864
|
+
const unknownFlags = args.filter(arg => arg.startsWith('--') && !ALLOWED_FLAGS.includes(arg) && arg !== '--help');
|
|
865
|
+
if (unknownFlags.length > 0) {
|
|
866
|
+
logError(`Unknown flag: ${unknownFlags[0].slice(0, MAX_ARG_LENGTH)}`);
|
|
867
|
+
console.log('Run "npx beth-copilot help" for usage information.');
|
|
868
|
+
process.exit(1);
|
|
869
|
+
}
|
|
1482
870
|
}
|
|
1483
871
|
|
|
1484
872
|
switch (command) {
|
|
@@ -1505,6 +893,27 @@ switch (command) {
|
|
|
1505
893
|
await quickstart(options);
|
|
1506
894
|
}
|
|
1507
895
|
break;
|
|
896
|
+
case 'land':
|
|
897
|
+
{
|
|
898
|
+
const { land } = await loadTsCommand('land');
|
|
899
|
+
// Pass raw args after 'land' — the command handles its own parsing
|
|
900
|
+
const landArgs = process.argv.slice(3);
|
|
901
|
+
await land(landArgs);
|
|
902
|
+
}
|
|
903
|
+
break;
|
|
904
|
+
case 'update':
|
|
905
|
+
{
|
|
906
|
+
const { update } = await loadTsCommand('update');
|
|
907
|
+
const updateArgs = process.argv.slice(3);
|
|
908
|
+
await update(updateArgs);
|
|
909
|
+
}
|
|
910
|
+
break;
|
|
911
|
+
case 'pre-push-guard':
|
|
912
|
+
{
|
|
913
|
+
const { prePushGuard } = await loadTsCommand('pre-push-guard');
|
|
914
|
+
await prePushGuard();
|
|
915
|
+
}
|
|
916
|
+
break;
|
|
1508
917
|
case 'help':
|
|
1509
918
|
case '--help':
|
|
1510
919
|
case '-h':
|