beth-copilot 2.0.0 → 2.1.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 +12 -0
- package/README.md +78 -78
- package/bin/cli.js +351 -46
- package/dist/cli/commands/doctor.d.ts +11 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +57 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/doctor.test.js +94 -1
- package/dist/cli/commands/doctor.test.js.map +1 -1
- package/dist/cli/commands/init-logic.e2e.test.js +102 -0
- package/dist/cli/commands/init-logic.e2e.test.js.map +1 -1
- package/dist/cli/commands/mcp.e2e.test.js +25 -0
- package/dist/cli/commands/mcp.e2e.test.js.map +1 -1
- package/dist/cli/commands/uninstall.test.d.ts +5 -0
- package/dist/cli/commands/uninstall.test.d.ts.map +1 -0
- package/dist/cli/commands/uninstall.test.js +223 -0
- package/dist/cli/commands/uninstall.test.js.map +1 -0
- package/dist/cli/commands/update.e2e.test.js +1 -3
- package/dist/cli/commands/update.e2e.test.js.map +1 -1
- package/package.json +1 -1
- package/sbom.json +259 -259
- package/templates/mcp.json.example +8 -0
package/bin/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
|
-
import { dirname, join, relative } from 'path';
|
|
5
|
-
import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, chmodSync } from 'fs';
|
|
4
|
+
import { basename, dirname, join, relative } from 'path';
|
|
5
|
+
import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, chmodSync, rmSync } from 'fs';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
|
-
import { execSync, spawn } from 'child_process';
|
|
7
|
+
import { execSync, execFileSync, spawn } from 'child_process';
|
|
8
8
|
|
|
9
9
|
const require = createRequire(import.meta.url);
|
|
10
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -226,18 +226,12 @@ async function animateBethBanner() {
|
|
|
226
226
|
console.log('"' + COLORS.reset);
|
|
227
227
|
console.log('');
|
|
228
228
|
|
|
229
|
-
// Show version
|
|
229
|
+
// Show version line only — commands are shown after install completes
|
|
230
230
|
console.log(`${COLORS.dim}v${CURRENT_VERSION}${COLORS.reset} ${COLORS.dim}AI Orchestrator for GitHub Copilot${COLORS.reset}`);
|
|
231
231
|
console.log('');
|
|
232
|
-
console.log(`${COLORS.bright}Commands:${COLORS.reset}`);
|
|
233
|
-
console.log(` ${COLORS.cyan}npx beth-copilot init${COLORS.reset} Install Beth in your project`);
|
|
234
|
-
console.log(` ${COLORS.cyan}npx beth-copilot help${COLORS.reset} Show full documentation`);
|
|
235
|
-
console.log('');
|
|
236
|
-
console.log(`${COLORS.bright}After install:${COLORS.reset} Open VS Code → Copilot Chat → ${COLORS.cyan}@Beth${COLORS.reset}`);
|
|
237
|
-
console.log('');
|
|
238
232
|
}
|
|
239
233
|
|
|
240
|
-
function showBethBannerStatic(
|
|
234
|
+
function showBethBannerStatic() {
|
|
241
235
|
const bethColors = [
|
|
242
236
|
'\x1b[38;5;196m',
|
|
243
237
|
'\x1b[38;5;202m',
|
|
@@ -276,17 +270,9 @@ function showBethBannerStatic({ showQuickHelp = true } = {}) {
|
|
|
276
270
|
console.log(COLORS.cyan + COLORS.bright + '"' + tagline + '"' + COLORS.reset);
|
|
277
271
|
console.log('');
|
|
278
272
|
|
|
279
|
-
// Show version
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
console.log('');
|
|
283
|
-
console.log(`${COLORS.bright}Commands:${COLORS.reset}`);
|
|
284
|
-
console.log(` ${COLORS.cyan}npx beth-copilot init${COLORS.reset} Install Beth in your project`);
|
|
285
|
-
console.log(` ${COLORS.cyan}npx beth-copilot help${COLORS.reset} Show full documentation`);
|
|
286
|
-
console.log('');
|
|
287
|
-
console.log(`${COLORS.bright}After install:${COLORS.reset} Open VS Code → Copilot Chat → ${COLORS.cyan}@Beth${COLORS.reset}`);
|
|
288
|
-
console.log('');
|
|
289
|
-
}
|
|
273
|
+
// Show version (always)
|
|
274
|
+
console.log(`${COLORS.dim}v${CURRENT_VERSION}${COLORS.reset} ${COLORS.dim}AI Orchestrator for GitHub Copilot${COLORS.reset}`);
|
|
275
|
+
console.log('');
|
|
290
276
|
}
|
|
291
277
|
|
|
292
278
|
// Compact Beth portrait with colors
|
|
@@ -590,33 +576,47 @@ ${BETH_GUARD_END}
|
|
|
590
576
|
}
|
|
591
577
|
|
|
592
578
|
function showHelp() {
|
|
593
|
-
showBethBannerStatic(
|
|
579
|
+
showBethBannerStatic();
|
|
594
580
|
console.log(`${COLORS.bright}Beth${COLORS.reset} - AI Orchestrator for GitHub Copilot
|
|
595
581
|
|
|
596
|
-
${COLORS.bright}
|
|
597
|
-
npx beth-copilot init [options] Initialize Beth in current directory
|
|
598
|
-
npx beth-copilot
|
|
599
|
-
npx beth-copilot
|
|
600
|
-
npx beth-copilot
|
|
601
|
-
npx beth-copilot
|
|
602
|
-
npx beth-copilot
|
|
603
|
-
npx beth-copilot
|
|
604
|
-
|
|
605
|
-
|
|
582
|
+
${COLORS.bright}Commands:${COLORS.reset}
|
|
583
|
+
${COLORS.cyan}npx beth-copilot init${COLORS.reset} [options] Initialize Beth in current directory
|
|
584
|
+
${COLORS.cyan}npx beth-copilot update${COLORS.reset} [options] Update project files to latest templates
|
|
585
|
+
${COLORS.cyan}npx beth-copilot doctor${COLORS.reset} Check system health and dependencies
|
|
586
|
+
${COLORS.cyan}npx beth-copilot land${COLORS.reset} [options] Automated session completion (test, commit, push)
|
|
587
|
+
${COLORS.cyan}npx beth-copilot quickstart${COLORS.reset} Run init + doctor
|
|
588
|
+
${COLORS.cyan}npx beth-copilot pre-push-guard${COLORS.reset} Run branch discipline checks (used by git hook)
|
|
589
|
+
${COLORS.cyan}npx beth-copilot uninstall${COLORS.reset} Remove all Beth files from current project
|
|
590
|
+
${COLORS.cyan}npx beth-copilot help${COLORS.reset} Show this help message
|
|
591
|
+
|
|
592
|
+
${COLORS.bright}Init Options:${COLORS.reset}
|
|
606
593
|
--force Overwrite existing files
|
|
607
594
|
--skip-backlog Don't create Backlog.md
|
|
608
595
|
--skip-mcp Don't create mcp.json.example
|
|
609
596
|
--verbose Show detailed diagnostics on errors
|
|
610
|
-
|
|
597
|
+
|
|
598
|
+
${COLORS.bright}Update Options:${COLORS.reset}
|
|
599
|
+
--check-only Report update status without modifying files
|
|
600
|
+
--force Overwrite user-modified files with templates
|
|
601
|
+
--verbose Show per-file detail
|
|
602
|
+
|
|
603
|
+
${COLORS.bright}Land Options:${COLORS.reset}
|
|
604
|
+
--message, -m <msg> Custom commit message
|
|
605
|
+
--skip-tests Skip test execution (not recommended)
|
|
606
|
+
--force, -f Push even if tests fail (dangerous)
|
|
607
|
+
--dry-run Show what would happen without executing
|
|
611
608
|
|
|
612
609
|
${COLORS.bright}Examples:${COLORS.reset}
|
|
613
610
|
npx beth-copilot init Set up Beth in current project
|
|
614
611
|
npx beth-copilot init --force Overwrite existing Beth files
|
|
612
|
+
npx beth-copilot update Update to latest templates
|
|
613
|
+
npx beth-copilot update --check-only See what changed without modifying
|
|
615
614
|
npx beth-copilot doctor Verify installation health
|
|
615
|
+
npx beth-copilot land -m "feat: new component" Commit and push session work
|
|
616
616
|
|
|
617
617
|
${COLORS.bright}What gets installed:${COLORS.reset}
|
|
618
618
|
.github/agents/ 7 specialized AI agents
|
|
619
|
-
.github/skills/
|
|
619
|
+
.github/skills/ Domain knowledge modules
|
|
620
620
|
.github/copilot-instructions.md Copilot configuration
|
|
621
621
|
.vscode/settings.json Recommended VS Code settings
|
|
622
622
|
AGENTS.md Workflow documentation
|
|
@@ -681,6 +681,44 @@ function copyDirRecursive(src, dest, options = {}) {
|
|
|
681
681
|
return copiedFiles;
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
+
/**
|
|
685
|
+
* Derive a task prefix from the project name.
|
|
686
|
+
* Uses package.json "name" field, falls back to directory name.
|
|
687
|
+
* Takes the first segment (split on - _ . space), lowercased, up to 6 letters.
|
|
688
|
+
*/
|
|
689
|
+
function deriveTaskPrefix(cwd) {
|
|
690
|
+
let projectName = '';
|
|
691
|
+
|
|
692
|
+
// Try package.json name field
|
|
693
|
+
const pkgPath = join(cwd, 'package.json');
|
|
694
|
+
if (existsSync(pkgPath)) {
|
|
695
|
+
try {
|
|
696
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
697
|
+
if (pkg.name && typeof pkg.name === 'string') {
|
|
698
|
+
projectName = pkg.name;
|
|
699
|
+
}
|
|
700
|
+
} catch {
|
|
701
|
+
// Ignore parse errors — fall through to directory name
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Fall back to directory name
|
|
706
|
+
if (!projectName) {
|
|
707
|
+
projectName = basename(cwd);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Strip npm scope (e.g. @scope/package -> package)
|
|
711
|
+
projectName = projectName.replace(/^@[^/]+\//, '');
|
|
712
|
+
|
|
713
|
+
// Split on common delimiters, take first segment
|
|
714
|
+
const firstSegment = projectName.split(/[-_. ]+/)[0] || '';
|
|
715
|
+
|
|
716
|
+
// Lowercase, keep only letters, take up to 6
|
|
717
|
+
const prefix = firstSegment.toLowerCase().replace(/[^a-z]/g, '').slice(0, 6);
|
|
718
|
+
|
|
719
|
+
return prefix || 'task'; // fallback to 'task' if nothing usable
|
|
720
|
+
}
|
|
721
|
+
|
|
684
722
|
async function init(options = {}) {
|
|
685
723
|
const { force = false, skipBacklog = false, skipMcp = false } = options;
|
|
686
724
|
const cwd = process.cwd();
|
|
@@ -700,11 +738,9 @@ ${COLORS.yellow}╔════════════════════
|
|
|
700
738
|
if (canAnimate()) {
|
|
701
739
|
await animateBethBanner();
|
|
702
740
|
} else {
|
|
703
|
-
showBethBannerStatic(
|
|
741
|
+
showBethBannerStatic();
|
|
704
742
|
}
|
|
705
743
|
|
|
706
|
-
log(`${COLORS.yellow}Tip: Run with --verbose for detailed diagnostics if you hit issues.${COLORS.reset}`);
|
|
707
|
-
|
|
708
744
|
// Check if templates exist
|
|
709
745
|
if (!existsSync(TEMPLATES_DIR)) {
|
|
710
746
|
logError('Templates directory not found. Package may be corrupted.');
|
|
@@ -787,6 +823,63 @@ ${COLORS.yellow}╔════════════════════
|
|
|
787
823
|
}
|
|
788
824
|
}
|
|
789
825
|
|
|
826
|
+
// Install .vscode/mcp.json with required MCP servers (unless --skip-mcp)
|
|
827
|
+
if (!skipMcp) {
|
|
828
|
+
const mcpJsonDest = join(cwd, '.vscode', 'mcp.json');
|
|
829
|
+
if (!existsSync(join(cwd, '.vscode'))) {
|
|
830
|
+
mkdirSync(join(cwd, '.vscode'), { recursive: true });
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
if (existsSync(mcpJsonDest) && !force) {
|
|
834
|
+
// Verify existing mcp.json has the required servers
|
|
835
|
+
try {
|
|
836
|
+
const existing = JSON.parse(readFileSync(mcpJsonDest, 'utf-8'));
|
|
837
|
+
const missing = [];
|
|
838
|
+
if (!existing.servers?.playwright) missing.push('playwright');
|
|
839
|
+
if (!existing.servers?.backlog) missing.push('backlog');
|
|
840
|
+
|
|
841
|
+
if (missing.length > 0) {
|
|
842
|
+
logWarning(`.vscode/mcp.json exists but missing required servers: ${missing.join(', ')}`);
|
|
843
|
+
logInfo('Add them manually or run with --force to overwrite');
|
|
844
|
+
} else {
|
|
845
|
+
logSuccess('.vscode/mcp.json already has required MCP servers');
|
|
846
|
+
}
|
|
847
|
+
} catch {
|
|
848
|
+
logWarning('.vscode/mcp.json exists but could not be parsed — verify it manually');
|
|
849
|
+
}
|
|
850
|
+
} else {
|
|
851
|
+
const mcpTemplateSrc = join(TEMPLATES_DIR, 'mcp.json.example');
|
|
852
|
+
if (existsSync(mcpTemplateSrc)) {
|
|
853
|
+
copyFileSync(mcpTemplateSrc, mcpJsonDest);
|
|
854
|
+
copiedFiles.push('.vscode/mcp.json');
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Initialize Backlog.md project with derived task prefix (unless skipped)
|
|
860
|
+
if (!skipBacklog) {
|
|
861
|
+
const backlogConfigPath = join(cwd, 'backlog', 'config.yml');
|
|
862
|
+
if (!existsSync(backlogConfigPath) || force) {
|
|
863
|
+
const taskPrefix = deriveTaskPrefix(cwd);
|
|
864
|
+
const dirName = basename(cwd);
|
|
865
|
+
try {
|
|
866
|
+
execFileSync(
|
|
867
|
+
'backlog',
|
|
868
|
+
['init', dirName, '--defaults', '--task-prefix', taskPrefix.toUpperCase(), '--integration-mode', 'mcp', '--auto-open-browser', 'false', '--bypass-git-hooks', 'true'],
|
|
869
|
+
{ cwd, stdio: 'pipe', encoding: 'utf-8' }
|
|
870
|
+
);
|
|
871
|
+
logSuccess(`Initialized Backlog.md with task prefix: ${taskPrefix.toUpperCase()}`);
|
|
872
|
+
copiedFiles.push('backlog/config.yml');
|
|
873
|
+
} catch (err) {
|
|
874
|
+
logWarning('Could not initialize Backlog.md — is the backlog CLI installed?');
|
|
875
|
+
logInfo('Install with: npm install -g backlog-md');
|
|
876
|
+
logDebug(err.message || String(err));
|
|
877
|
+
}
|
|
878
|
+
} else {
|
|
879
|
+
logSuccess('Backlog.md already initialized (backlog/config.yml exists)');
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
790
883
|
// Summary
|
|
791
884
|
console.log('');
|
|
792
885
|
if (copiedFiles.length > 0) {
|
|
@@ -808,17 +901,218 @@ ${COLORS.bright}Next steps:${COLORS.reset}
|
|
|
808
901
|
2. Open Copilot Chat (${COLORS.cyan}Ctrl+Alt+I${COLORS.reset} / ${COLORS.cyan}Cmd+Alt+I${COLORS.reset})
|
|
809
902
|
3. Type ${COLORS.cyan}@Beth${COLORS.reset} to start - she's your orchestrator
|
|
810
903
|
|
|
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
|
|
812
|
-
|
|
813
|
-
${COLORS.bright}Documentation:${COLORS.reset}
|
|
814
|
-
https://github.com/stephschofield/beth
|
|
904
|
+
${COLORS.bright}Pro tip:${COLORS.reset} Start every session with ${COLORS.cyan}@Beth${COLORS.reset} and let her route work to the right specialists.`);
|
|
815
905
|
|
|
816
|
-
|
|
906
|
+
// Commands at the bottom — easy to find and copy-paste
|
|
907
|
+
console.log(`
|
|
908
|
+
${COLORS.bright}Commands:${COLORS.reset}
|
|
909
|
+
${COLORS.cyan}npx beth-copilot update${COLORS.reset} Update Beth to the latest templates
|
|
910
|
+
${COLORS.cyan}npx beth-copilot doctor${COLORS.reset} Check system health and dependencies
|
|
911
|
+
${COLORS.cyan}npx beth-copilot land${COLORS.reset} Automated session completion (test, commit, push)
|
|
912
|
+
${COLORS.cyan}npx beth-copilot help${COLORS.reset} Show all commands, options, and documentation
|
|
817
913
|
`);
|
|
914
|
+
|
|
915
|
+
console.log(`${COLORS.dim}Tip: Run with --verbose for detailed diagnostics if you hit issues.${COLORS.reset}`);
|
|
916
|
+
console.log(`${COLORS.dim}Documentation: https://github.com/stephschofield/beth${COLORS.reset}`);
|
|
917
|
+
console.log(`${COLORS.cyan}"They broke my wings and forgot I had claws."${COLORS.reset}`);
|
|
918
|
+
console.log('');
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* Uninstall Beth from the current project.
|
|
923
|
+
* Removes all files/directories that init installed.
|
|
924
|
+
*/
|
|
925
|
+
async function uninstall() {
|
|
926
|
+
const cwd = process.cwd();
|
|
927
|
+
const args = process.argv.slice(3);
|
|
928
|
+
const forceFlag = args.includes('--force') || args.includes('-f');
|
|
929
|
+
|
|
930
|
+
showBethBannerStatic();
|
|
931
|
+
|
|
932
|
+
console.log(`${COLORS.bright}${COLORS.red}Uninstalling Beth...${COLORS.reset}\n`);
|
|
933
|
+
|
|
934
|
+
// Verify there's actually a Beth installation here
|
|
935
|
+
const githubDir = join(cwd, '.github');
|
|
936
|
+
const agentsDir = join(githubDir, 'agents');
|
|
937
|
+
const hasInstallation = existsSync(agentsDir) || existsSync(join(cwd, 'AGENTS.md'));
|
|
938
|
+
|
|
939
|
+
if (!hasInstallation) {
|
|
940
|
+
logWarning('No Beth installation detected in this directory.');
|
|
941
|
+
console.log('Are you in the right project? Beth installs into .github/agents/ and AGENTS.md.');
|
|
942
|
+
process.exit(0);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// --- Build the removal manifest ---
|
|
946
|
+
// Only remove files/dirs that Beth actually installs (from templates)
|
|
947
|
+
|
|
948
|
+
// Directories Beth owns entirely
|
|
949
|
+
const bethOwnedDirs = [
|
|
950
|
+
join(githubDir, 'agents'),
|
|
951
|
+
join(githubDir, 'skills'),
|
|
952
|
+
join(githubDir, 'hooks'),
|
|
953
|
+
];
|
|
954
|
+
|
|
955
|
+
// Individual files Beth installs
|
|
956
|
+
const bethOwnedFiles = [
|
|
957
|
+
join(githubDir, 'copilot-instructions.md'),
|
|
958
|
+
join(githubDir, 'copilot-mcp-config.json'),
|
|
959
|
+
join(githubDir, 'pull_request_template.md'),
|
|
960
|
+
join(githubDir, 'dependabot.yml'),
|
|
961
|
+
join(cwd, 'AGENTS.md'),
|
|
962
|
+
join(cwd, 'Backlog.md'),
|
|
963
|
+
join(cwd, 'mcp.json.example'),
|
|
964
|
+
join(cwd, '.vscode', 'settings.json'),
|
|
965
|
+
join(cwd, '.vscode', 'mcp.json'),
|
|
966
|
+
];
|
|
967
|
+
|
|
968
|
+
// Git pre-push hook (Beth appends a guard block)
|
|
969
|
+
const prePushHook = join(cwd, '.git', 'hooks', 'pre-push');
|
|
970
|
+
|
|
971
|
+
// Backlog.md directory (created by `backlog init`)
|
|
972
|
+
const backlogDir = join(cwd, 'backlog');
|
|
973
|
+
|
|
974
|
+
// --- Collect what actually exists ---
|
|
975
|
+
const dirsToRemove = bethOwnedDirs.filter(d => existsSync(d));
|
|
976
|
+
const filesToRemove = bethOwnedFiles.filter(f => existsSync(f));
|
|
977
|
+
const hasBacklogDir = existsSync(backlogDir);
|
|
978
|
+
const hasPrePushGuard = existsSync(prePushHook) && readFileSync(prePushHook, 'utf-8').includes(BETH_GUARD_BEGIN);
|
|
979
|
+
|
|
980
|
+
if (dirsToRemove.length === 0 && filesToRemove.length === 0 && !hasBacklogDir && !hasPrePushGuard) {
|
|
981
|
+
logWarning('Nothing to remove — Beth files have already been cleaned up.');
|
|
982
|
+
process.exit(0);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// --- Show what will be removed ---
|
|
986
|
+
console.log(`${COLORS.bright}The following will be removed:${COLORS.reset}\n`);
|
|
987
|
+
|
|
988
|
+
for (const dir of dirsToRemove) {
|
|
989
|
+
logInfo(`${relative(cwd, dir)}/ (directory)`);
|
|
990
|
+
}
|
|
991
|
+
for (const file of filesToRemove) {
|
|
992
|
+
logInfo(`${relative(cwd, file)}`);
|
|
993
|
+
}
|
|
994
|
+
if (hasBacklogDir) {
|
|
995
|
+
logInfo('backlog/ (directory — Backlog.md task data)');
|
|
996
|
+
}
|
|
997
|
+
if (hasPrePushGuard) {
|
|
998
|
+
logInfo('.git/hooks/pre-push (Beth guard block will be removed)');
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
console.log('');
|
|
1002
|
+
|
|
1003
|
+
// --- Confirm unless --force ---
|
|
1004
|
+
if (!forceFlag) {
|
|
1005
|
+
const confirmed = await promptYesNo(`${COLORS.yellow}Are you sure you want to remove Beth from this project?${COLORS.reset}`);
|
|
1006
|
+
if (!confirmed) {
|
|
1007
|
+
console.log('\nUninstall cancelled. Beth lives to fight another day.');
|
|
1008
|
+
process.exit(0);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
console.log('');
|
|
1013
|
+
const removed = [];
|
|
1014
|
+
|
|
1015
|
+
// --- Remove directories ---
|
|
1016
|
+
for (const dir of dirsToRemove) {
|
|
1017
|
+
try {
|
|
1018
|
+
rmSync(dir, { recursive: true, force: true });
|
|
1019
|
+
removed.push(relative(cwd, dir) + '/');
|
|
1020
|
+
logSuccess(`Removed ${relative(cwd, dir)}/`);
|
|
1021
|
+
} catch (err) {
|
|
1022
|
+
logError(`Failed to remove ${relative(cwd, dir)}/: ${err.message}`);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// --- Remove files ---
|
|
1027
|
+
for (const file of filesToRemove) {
|
|
1028
|
+
try {
|
|
1029
|
+
unlinkSync(file);
|
|
1030
|
+
removed.push(relative(cwd, file));
|
|
1031
|
+
logSuccess(`Removed ${relative(cwd, file)}`);
|
|
1032
|
+
} catch (err) {
|
|
1033
|
+
logError(`Failed to remove ${relative(cwd, file)}: ${err.message}`);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// --- Remove backlog directory ---
|
|
1038
|
+
if (hasBacklogDir) {
|
|
1039
|
+
try {
|
|
1040
|
+
rmSync(backlogDir, { recursive: true, force: true });
|
|
1041
|
+
removed.push('backlog/');
|
|
1042
|
+
logSuccess('Removed backlog/');
|
|
1043
|
+
} catch (err) {
|
|
1044
|
+
logError(`Failed to remove backlog/: ${err.message}`);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// --- Clean pre-push hook ---
|
|
1049
|
+
if (hasPrePushGuard) {
|
|
1050
|
+
try {
|
|
1051
|
+
const hookContent = readFileSync(prePushHook, 'utf-8');
|
|
1052
|
+
const beginIdx = hookContent.indexOf(BETH_GUARD_BEGIN);
|
|
1053
|
+
const endIdx = hookContent.indexOf(BETH_GUARD_END);
|
|
1054
|
+
|
|
1055
|
+
if (beginIdx !== -1 && endIdx !== -1) {
|
|
1056
|
+
const cleaned = hookContent.slice(0, beginIdx) + hookContent.slice(endIdx + BETH_GUARD_END.length + 1);
|
|
1057
|
+
const trimmed = cleaned.trim();
|
|
1058
|
+
|
|
1059
|
+
if (trimmed === '' || trimmed === '#!/bin/sh' || trimmed === '#!/bin/bash') {
|
|
1060
|
+
// Hook is now empty — remove the whole file
|
|
1061
|
+
unlinkSync(prePushHook);
|
|
1062
|
+
logSuccess('Removed .git/hooks/pre-push (was Beth-only)');
|
|
1063
|
+
} else {
|
|
1064
|
+
writeFileSync(prePushHook, cleaned);
|
|
1065
|
+
logSuccess('Removed Beth guard block from .git/hooks/pre-push');
|
|
1066
|
+
}
|
|
1067
|
+
removed.push('.git/hooks/pre-push (guard block)');
|
|
1068
|
+
}
|
|
1069
|
+
} catch (err) {
|
|
1070
|
+
logError(`Failed to clean pre-push hook: ${err.message}`);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// --- Clean up empty parent directories ---
|
|
1075
|
+
// If .github/ is now empty, remove it
|
|
1076
|
+
if (existsSync(githubDir)) {
|
|
1077
|
+
try {
|
|
1078
|
+
const remaining = readdirSync(githubDir);
|
|
1079
|
+
if (remaining.length === 0) {
|
|
1080
|
+
rmSync(githubDir, { recursive: true, force: true });
|
|
1081
|
+
logSuccess('Removed empty .github/');
|
|
1082
|
+
}
|
|
1083
|
+
} catch {
|
|
1084
|
+
// Not critical
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// If .vscode/ is now empty, remove it
|
|
1089
|
+
const vscodeDir = join(cwd, '.vscode');
|
|
1090
|
+
if (existsSync(vscodeDir)) {
|
|
1091
|
+
try {
|
|
1092
|
+
const remaining = readdirSync(vscodeDir);
|
|
1093
|
+
if (remaining.length === 0) {
|
|
1094
|
+
rmSync(vscodeDir, { recursive: true, force: true });
|
|
1095
|
+
logSuccess('Removed empty .vscode/');
|
|
1096
|
+
}
|
|
1097
|
+
} catch {
|
|
1098
|
+
// Not critical
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// --- Summary ---
|
|
1103
|
+
console.log('');
|
|
1104
|
+
if (removed.length > 0) {
|
|
1105
|
+
logSuccess(`Removed ${removed.length} items. Beth has left the building.`);
|
|
1106
|
+
console.log(`\n${COLORS.dim}To reinstall: npx beth-copilot init${COLORS.reset}`);
|
|
1107
|
+
} else {
|
|
1108
|
+
logWarning('No items were removed. Check file permissions.');
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
console.log(`\n${COLORS.cyan}"I'm not leaving. I'm choosing to go."${COLORS.reset}\n`);
|
|
818
1112
|
}
|
|
819
1113
|
|
|
820
1114
|
// Input validation constants
|
|
821
|
-
const ALLOWED_COMMANDS = ['init', 'help', '--help', '-h', 'doctor', 'quickstart', 'pre-push-guard', 'update', 'land'];
|
|
1115
|
+
const ALLOWED_COMMANDS = ['init', 'help', '--help', '-h', 'doctor', 'quickstart', 'pre-push-guard', 'update', 'land', 'uninstall'];
|
|
822
1116
|
const ALLOWED_FLAGS = ['--force', '--skip-backlog', '--skip-mcp', '--verbose', '--reason', '-r', '-f', '--skip-tests', '--message', '-m', '--dry-run', '--check-only'];
|
|
823
1117
|
const MAX_ARG_LENGTH = 50;
|
|
824
1118
|
|
|
@@ -826,7 +1120,7 @@ const MAX_ARG_LENGTH = 50;
|
|
|
826
1120
|
function validateArgs(args) {
|
|
827
1121
|
// The 'land' and 'update' commands handle their own arg validation
|
|
828
1122
|
const command = args[0]?.toLowerCase();
|
|
829
|
-
if (command === 'land' || command === 'update') return;
|
|
1123
|
+
if (command === 'land' || command === 'update' || command === 'uninstall') return;
|
|
830
1124
|
|
|
831
1125
|
for (const arg of args) {
|
|
832
1126
|
// Prevent excessively long arguments (log injection, DoS)
|
|
@@ -860,7 +1154,7 @@ globalThis.VERBOSE = options.verbose;
|
|
|
860
1154
|
|
|
861
1155
|
// Validate unknown flags (exclude --help which is handled as a command)
|
|
862
1156
|
// Skip for 'land' and 'update' commands which handle their own arg parsing
|
|
863
|
-
if (command !== 'land' && command !== 'update') {
|
|
1157
|
+
if (command !== 'land' && command !== 'update' && command !== 'uninstall') {
|
|
864
1158
|
const unknownFlags = args.filter(arg => arg.startsWith('--') && !ALLOWED_FLAGS.includes(arg) && arg !== '--help');
|
|
865
1159
|
if (unknownFlags.length > 0) {
|
|
866
1160
|
logError(`Unknown flag: ${unknownFlags[0].slice(0, MAX_ARG_LENGTH)}`);
|
|
@@ -914,6 +1208,17 @@ switch (command) {
|
|
|
914
1208
|
await prePushGuard();
|
|
915
1209
|
}
|
|
916
1210
|
break;
|
|
1211
|
+
case 'uninstall':
|
|
1212
|
+
try {
|
|
1213
|
+
await uninstall();
|
|
1214
|
+
} catch (error) {
|
|
1215
|
+
if (error instanceof UserError) {
|
|
1216
|
+
showUserError(error);
|
|
1217
|
+
process.exit(1);
|
|
1218
|
+
}
|
|
1219
|
+
throw error;
|
|
1220
|
+
}
|
|
1221
|
+
break;
|
|
917
1222
|
case 'help':
|
|
918
1223
|
case '--help':
|
|
919
1224
|
case '-h':
|
|
@@ -7,16 +7,27 @@
|
|
|
7
7
|
* - .github/agents/ exists with valid frontmatter
|
|
8
8
|
* - .github/skills/ exists
|
|
9
9
|
* - backlog.md initialization
|
|
10
|
+
* - Required MCP servers configured (.vscode/mcp.json)
|
|
10
11
|
*/
|
|
11
12
|
interface DoctorOptions {
|
|
12
13
|
verbose?: boolean;
|
|
13
14
|
}
|
|
15
|
+
interface CheckResult {
|
|
16
|
+
name: string;
|
|
17
|
+
status: 'pass' | 'warn' | 'fail';
|
|
18
|
+
message: string;
|
|
19
|
+
details?: string;
|
|
20
|
+
}
|
|
14
21
|
/**
|
|
15
22
|
* Parse the minimum major Node.js version from package.json engines.node.
|
|
16
23
|
* Supports formats like ">=18", "^18", ">=18.0.0", etc.
|
|
17
24
|
* Returns the parsed major version, or a fallback if parsing fails.
|
|
18
25
|
*/
|
|
19
26
|
export declare function getMinNodeVersion(cwd: string): number;
|
|
27
|
+
/**
|
|
28
|
+
* Check .vscode/mcp.json for required MCP servers
|
|
29
|
+
*/
|
|
30
|
+
export declare function checkMcpServers(cwd: string): CheckResult;
|
|
20
31
|
/**
|
|
21
32
|
* Main doctor command
|
|
22
33
|
* @param options - Command options
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkBH,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAiBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWrD;AA8LD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAmDxD;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,EAAE,aAAa,UAAO,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA6C3I"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - .github/agents/ exists with valid frontmatter
|
|
8
8
|
* - .github/skills/ exists
|
|
9
9
|
* - backlog.md initialization
|
|
10
|
+
* - Required MCP servers configured (.vscode/mcp.json)
|
|
10
11
|
*/
|
|
11
12
|
import { execSync } from 'child_process';
|
|
12
13
|
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
@@ -211,6 +212,61 @@ function checkBacklogInit(cwd) {
|
|
|
211
212
|
details: 'Run: backlog init',
|
|
212
213
|
};
|
|
213
214
|
}
|
|
215
|
+
/** Required MCP servers that agents depend on */
|
|
216
|
+
const REQUIRED_MCP_SERVERS = [
|
|
217
|
+
{ key: 'playwright', label: 'Playwright', hint: '"playwright": { "command": "npx", "args": ["@playwright/mcp@0.0.68"] }' },
|
|
218
|
+
{ key: 'backlog', label: 'Backlog.md', hint: '"backlog": { "command": "backlog", "args": ["mcp", "start"] }' },
|
|
219
|
+
];
|
|
220
|
+
/**
|
|
221
|
+
* Check .vscode/mcp.json for required MCP servers
|
|
222
|
+
*/
|
|
223
|
+
export function checkMcpServers(cwd) {
|
|
224
|
+
const mcpPath = join(cwd, '.vscode', 'mcp.json');
|
|
225
|
+
if (!existsSync(mcpPath)) {
|
|
226
|
+
return {
|
|
227
|
+
name: 'MCP Servers',
|
|
228
|
+
status: 'fail',
|
|
229
|
+
message: '.vscode/mcp.json not found',
|
|
230
|
+
details: 'Run: npx beth-copilot init (or npx beth-copilot init --force to regenerate)',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
let config;
|
|
234
|
+
try {
|
|
235
|
+
config = JSON.parse(readFileSync(mcpPath, 'utf-8'));
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return {
|
|
239
|
+
name: 'MCP Servers',
|
|
240
|
+
status: 'fail',
|
|
241
|
+
message: '.vscode/mcp.json is not valid JSON',
|
|
242
|
+
details: 'Fix the JSON syntax or run: npx beth-copilot init --force',
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const servers = config.servers;
|
|
246
|
+
if (!servers || typeof servers !== 'object') {
|
|
247
|
+
return {
|
|
248
|
+
name: 'MCP Servers',
|
|
249
|
+
status: 'fail',
|
|
250
|
+
message: '.vscode/mcp.json missing "servers" object',
|
|
251
|
+
details: 'Run: npx beth-copilot init --force',
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
const missing = REQUIRED_MCP_SERVERS.filter(s => !servers[s.key]);
|
|
255
|
+
if (missing.length > 0) {
|
|
256
|
+
return {
|
|
257
|
+
name: 'MCP Servers',
|
|
258
|
+
status: 'fail',
|
|
259
|
+
message: `missing required server(s): ${missing.map(m => m.label).join(', ')}`,
|
|
260
|
+
details: missing.map(m => `Add to .vscode/mcp.json servers: ${m.hint}`).join('\n '),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
const totalServers = Object.keys(servers).length;
|
|
264
|
+
return {
|
|
265
|
+
name: 'MCP Servers',
|
|
266
|
+
status: 'pass',
|
|
267
|
+
message: `${totalServers} servers configured (playwright ✓, backlog ✓)`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
214
270
|
/**
|
|
215
271
|
* Main doctor command
|
|
216
272
|
* @param options - Command options
|
|
@@ -229,6 +285,7 @@ export async function doctor(options = {}, exitOnFailure = true) {
|
|
|
229
285
|
checkAgents(cwd),
|
|
230
286
|
checkSkills(cwd),
|
|
231
287
|
checkBacklogInit(cwd),
|
|
288
|
+
checkMcpServers(cwd),
|
|
232
289
|
];
|
|
233
290
|
// Display results
|
|
234
291
|
for (const result of results) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
|