beth-copilot 1.0.16 → 1.0.18
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/bin/cli.js +355 -47
- package/dist/cli/commands/client-config.d.ts +31 -0
- package/dist/cli/commands/client-config.d.ts.map +1 -0
- package/dist/cli/commands/client-config.e2e.test.d.ts +15 -0
- package/dist/cli/commands/client-config.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/client-config.e2e.test.js +556 -0
- package/dist/cli/commands/client-config.e2e.test.js.map +1 -0
- package/dist/cli/commands/client-config.js +73 -0
- package/dist/cli/commands/client-config.js.map +1 -0
- package/dist/cli/commands/client-config.test.d.ts +6 -0
- package/dist/cli/commands/client-config.test.d.ts.map +1 -0
- package/dist/cli/commands/client-config.test.js +133 -0
- package/dist/cli/commands/client-config.test.js.map +1 -0
- package/dist/cli/commands/help.e2e.test.js +4 -4
- package/dist/cli/commands/help.e2e.test.js.map +1 -1
- package/dist/cli/commands/init-quickstart.e2e.test.d.ts +11 -0
- package/dist/cli/commands/init-quickstart.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/init-quickstart.e2e.test.js +221 -0
- package/dist/cli/commands/init-quickstart.e2e.test.js.map +1 -0
- package/dist/cli/commands/mcp.e2e.test.js +29 -22
- package/dist/cli/commands/mcp.e2e.test.js.map +1 -1
- package/dist/cli/commands/pipeline.e2e.test.js +19 -19
- package/dist/cli/commands/pipeline.e2e.test.js.map +1 -1
- package/dist/cli/commands/quickstart.d.ts.map +1 -1
- package/dist/cli/commands/quickstart.js +23 -7
- package/dist/cli/commands/quickstart.js.map +1 -1
- package/dist/cli/commands/quickstart.test.js +65 -0
- package/dist/cli/commands/quickstart.test.js.map +1 -1
- package/package.json +1 -1
- package/sbom.json +209 -209
- package/templates/.github/agents/beth.agent.md +24 -1
- package/templates/.github/agents/developer.agent.md +25 -18
- package/templates/.github/agents/product-manager.agent.md +11 -0
- package/templates/.github/agents/researcher.agent.md +11 -0
- package/templates/.github/agents/security-reviewer.agent.md +11 -0
- package/templates/.github/agents/tester.agent.md +11 -0
- package/templates/.github/agents/ux-designer.agent.md +11 -0
- package/templates/.github/copilot-instructions.md +21 -0
- package/templates/.vscode/mcp.json +20 -0
- package/templates/CLAUDE.md +129 -0
- package/templates/mcp.json.example +3 -0
package/bin/cli.js
CHANGED
|
@@ -788,34 +788,59 @@ ${COLORS.bright}Usage:${COLORS.reset}
|
|
|
788
788
|
${COLORS.bright}Options:${COLORS.reset}
|
|
789
789
|
--force Overwrite existing files
|
|
790
790
|
--skip-backlog Don't create Backlog.md
|
|
791
|
-
--skip-mcp Don't
|
|
791
|
+
--skip-mcp Don't install MCP server configs
|
|
792
792
|
--skip-beads Skip beads check (not recommended)
|
|
793
793
|
--verbose Show detailed diagnostics on errors
|
|
794
|
+
--client <type> Skip interactive prompt. Values:
|
|
795
|
+
vscode, copilot-cli, claude-code, all
|
|
794
796
|
|
|
795
797
|
${COLORS.bright}Examples:${COLORS.reset}
|
|
796
|
-
npx beth-copilot init Set up Beth
|
|
798
|
+
npx beth-copilot init Set up Beth (interactive client selection)
|
|
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
|
|
797
802
|
npx beth-copilot init --force Overwrite existing Beth files
|
|
798
803
|
npx beth-copilot doctor Verify installation health
|
|
799
804
|
|
|
800
805
|
${COLORS.bright}What gets installed:${COLORS.reset}
|
|
801
|
-
.
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
806
|
+
${COLORS.dim}Shared (all clients):${COLORS.reset}
|
|
807
|
+
.github/skills/ Domain knowledge modules
|
|
808
|
+
AGENTS.md Workflow documentation
|
|
809
|
+
Backlog.md Task tracking file
|
|
810
|
+
|
|
811
|
+
${COLORS.dim}VS Code + GitHub Copilot:${COLORS.reset}
|
|
812
|
+
.github/agents/ 7 specialized AI agents
|
|
813
|
+
.github/copilot-instructions.md Copilot configuration
|
|
814
|
+
.vscode/settings.json Recommended VS Code settings
|
|
815
|
+
.vscode/mcp.json MCP servers (beads, shadcn, playwright, deepwiki)
|
|
816
|
+
|
|
817
|
+
${COLORS.dim}GitHub Copilot CLI:${COLORS.reset}
|
|
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
|
|
813
827
|
|
|
814
828
|
${COLORS.bright}Documentation:${COLORS.reset}
|
|
815
829
|
https://github.com/stephschofield/beth
|
|
816
830
|
`);
|
|
817
831
|
}
|
|
818
832
|
|
|
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
|
+
|
|
819
844
|
function copyDirRecursive(src, dest, options = {}) {
|
|
820
845
|
const { force = false, copiedFiles = [] } = options;
|
|
821
846
|
|
|
@@ -864,8 +889,149 @@ function copyDirRecursive(src, dest, options = {}) {
|
|
|
864
889
|
return copiedFiles;
|
|
865
890
|
}
|
|
866
891
|
|
|
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
|
+
|
|
867
1033
|
async function init(options = {}) {
|
|
868
|
-
const { force = false, skipBacklog = false, skipMcp = false, skipBeads = false } = options;
|
|
1034
|
+
const { force = false, skipBacklog = false, skipMcp = false, skipBeads = false, client: clientArg } = options;
|
|
869
1035
|
const cwd = process.cwd();
|
|
870
1036
|
|
|
871
1037
|
// Check for updates
|
|
@@ -888,6 +1054,27 @@ ${COLORS.yellow}╔════════════════════
|
|
|
888
1054
|
|
|
889
1055
|
log(`${COLORS.yellow}Tip: Run with --verbose for detailed diagnostics if you hit issues.${COLORS.reset}`);
|
|
890
1056
|
|
|
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
|
+
|
|
891
1078
|
// Check if templates exist
|
|
892
1079
|
if (!existsSync(TEMPLATES_DIR)) {
|
|
893
1080
|
logError('Templates directory not found. Package may be corrupted.');
|
|
@@ -896,13 +1083,15 @@ ${COLORS.yellow}╔════════════════════
|
|
|
896
1083
|
|
|
897
1084
|
const copiedFiles = [];
|
|
898
1085
|
|
|
899
|
-
//
|
|
900
|
-
const githubSrc = join(TEMPLATES_DIR, '.github');
|
|
901
|
-
const githubDest = join(cwd, '.github');
|
|
1086
|
+
// === SHARED FILES (all clients) ===
|
|
902
1087
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1088
|
+
// Copy .github/skills/ (domain knowledge - useful for all clients)
|
|
1089
|
+
const skillsSrc = join(TEMPLATES_DIR, '.github', 'skills');
|
|
1090
|
+
const skillsDest = join(cwd, '.github', 'skills');
|
|
1091
|
+
|
|
1092
|
+
if (existsSync(skillsSrc)) {
|
|
1093
|
+
log('\nInstalling skills (domain knowledge)...');
|
|
1094
|
+
copyDirRecursive(skillsSrc, skillsDest, { force, copiedFiles });
|
|
906
1095
|
}
|
|
907
1096
|
|
|
908
1097
|
// Copy AGENTS.md
|
|
@@ -933,33 +1122,38 @@ ${COLORS.yellow}╔════════════════════
|
|
|
933
1122
|
}
|
|
934
1123
|
}
|
|
935
1124
|
|
|
936
|
-
//
|
|
937
|
-
if (
|
|
938
|
-
|
|
939
|
-
const mcpDest = join(cwd, 'mcp.json.example');
|
|
1125
|
+
// === VS CODE + COPILOT FILES ===
|
|
1126
|
+
if (clients.vscode) {
|
|
1127
|
+
log('\nInstalling VS Code + Copilot configuration...');
|
|
940
1128
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// .github/copilot-instructions.md
|
|
1137
|
+
const copilotInstructionsSrc = join(TEMPLATES_DIR, '.github', 'copilot-instructions.md');
|
|
1138
|
+
const copilotInstructionsDest = join(cwd, '.github', 'copilot-instructions.md');
|
|
1139
|
+
if (existsSync(copilotInstructionsSrc)) {
|
|
1140
|
+
if (existsSync(copilotInstructionsDest) && !force) {
|
|
1141
|
+
logWarning('Skipped (exists): .github/copilot-instructions.md');
|
|
944
1142
|
} else {
|
|
945
|
-
|
|
946
|
-
|
|
1143
|
+
mkdirSync(join(cwd, '.github'), { recursive: true });
|
|
1144
|
+
copyFileSync(copilotInstructionsSrc, copilotInstructionsDest);
|
|
1145
|
+
copiedFiles.push('.github/copilot-instructions.md');
|
|
947
1146
|
}
|
|
948
1147
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
const vscodeSrc = join(TEMPLATES_DIR, '.vscode');
|
|
953
|
-
const vscodeDest = join(cwd, '.vscode');
|
|
954
|
-
|
|
955
|
-
if (existsSync(vscodeSrc)) {
|
|
1148
|
+
|
|
1149
|
+
// .vscode/settings.json
|
|
1150
|
+
const vscodeDest = join(cwd, '.vscode');
|
|
956
1151
|
if (!existsSync(vscodeDest)) {
|
|
957
1152
|
mkdirSync(vscodeDest, { recursive: true });
|
|
958
1153
|
}
|
|
959
1154
|
|
|
960
|
-
const settingsSrc = join(
|
|
1155
|
+
const settingsSrc = join(TEMPLATES_DIR, '.vscode', 'settings.json');
|
|
961
1156
|
const settingsDest = join(vscodeDest, 'settings.json');
|
|
962
|
-
|
|
963
1157
|
if (existsSync(settingsSrc)) {
|
|
964
1158
|
if (existsSync(settingsDest) && !force) {
|
|
965
1159
|
logWarning('Skipped (exists): .vscode/settings.json');
|
|
@@ -968,8 +1162,61 @@ ${COLORS.yellow}╔════════════════════
|
|
|
968
1162
|
copiedFiles.push('.vscode/settings.json');
|
|
969
1163
|
}
|
|
970
1164
|
}
|
|
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
|
+
}
|
|
1180
|
+
|
|
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
|
+
}
|
|
971
1197
|
}
|
|
972
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
|
+
|
|
973
1220
|
// Summary
|
|
974
1221
|
console.log('');
|
|
975
1222
|
if (copiedFiles.length > 0) {
|
|
@@ -1095,6 +1342,29 @@ ${COLORS.yellow}╔════════════════════
|
|
|
1095
1342
|
await runBeadsDoctor();
|
|
1096
1343
|
}
|
|
1097
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
|
+
|
|
1098
1368
|
// Final verification
|
|
1099
1369
|
console.log('');
|
|
1100
1370
|
log('Verifying installation...', COLORS.cyan);
|
|
@@ -1111,15 +1381,35 @@ ${COLORS.yellow}╔════════════════════
|
|
|
1111
1381
|
process.exit(1);
|
|
1112
1382
|
}
|
|
1113
1383
|
|
|
1114
|
-
// Next steps
|
|
1115
|
-
console.log(
|
|
1116
|
-
|
|
1384
|
+
// Next steps (client-specific)
|
|
1385
|
+
console.log('');
|
|
1386
|
+
log('Next steps:', COLORS.bright);
|
|
1387
|
+
|
|
1388
|
+
if (clients.vscode) {
|
|
1389
|
+
console.log(`
|
|
1390
|
+
${COLORS.bright}VS Code + Copilot:${COLORS.reset}
|
|
1117
1391
|
1. Open this project in VS Code
|
|
1118
1392
|
2. Open Copilot Chat (${COLORS.cyan}Ctrl+Alt+I${COLORS.reset} / ${COLORS.cyan}Cmd+Alt+I${COLORS.reset})
|
|
1119
|
-
3. Type ${COLORS.cyan}@Beth${COLORS.reset} to start
|
|
1120
|
-
|
|
1121
|
-
|
|
1393
|
+
3. Type ${COLORS.cyan}@Beth${COLORS.reset} to start — she's your orchestrator`);
|
|
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
|
+
}
|
|
1122
1411
|
|
|
1412
|
+
console.log(`
|
|
1123
1413
|
${COLORS.bright}Documentation:${COLORS.reset}
|
|
1124
1414
|
https://github.com/stephschofield/beth
|
|
1125
1415
|
|
|
@@ -1129,7 +1419,8 @@ ${COLORS.cyan}"They broke my wings and forgot I had claws."${COLORS.reset}
|
|
|
1129
1419
|
|
|
1130
1420
|
// Input validation constants
|
|
1131
1421
|
const ALLOWED_COMMANDS = ['init', 'help', '--help', '-h', 'doctor', 'quickstart'];
|
|
1132
|
-
const ALLOWED_FLAGS = ['--force', '--skip-backlog', '--skip-mcp', '--skip-beads', '--verbose'];
|
|
1422
|
+
const ALLOWED_FLAGS = ['--force', '--skip-backlog', '--skip-mcp', '--skip-beads', '--verbose', '--client'];
|
|
1423
|
+
const ALLOWED_CLIENTS = ['vscode', 'copilot-cli', 'claude-code', 'all'];
|
|
1133
1424
|
const MAX_ARG_LENGTH = 50;
|
|
1134
1425
|
|
|
1135
1426
|
// Validate and sanitize input
|
|
@@ -1154,19 +1445,36 @@ validateArgs(args);
|
|
|
1154
1445
|
|
|
1155
1446
|
const command = args[0]?.toLowerCase();
|
|
1156
1447
|
|
|
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
|
+
|
|
1157
1460
|
const options = {
|
|
1158
1461
|
force: args.includes('--force'),
|
|
1159
1462
|
skipBacklog: args.includes('--skip-backlog'),
|
|
1160
1463
|
skipMcp: args.includes('--skip-mcp'),
|
|
1161
1464
|
skipBeads: args.includes('--skip-beads'),
|
|
1162
1465
|
verbose: args.includes('--verbose'),
|
|
1466
|
+
client: clientArg,
|
|
1163
1467
|
};
|
|
1164
1468
|
|
|
1165
1469
|
// Set global verbose flag for logDebug
|
|
1166
1470
|
globalThis.VERBOSE = options.verbose;
|
|
1167
1471
|
|
|
1168
1472
|
// Validate unknown flags (exclude --help which is handled as a command)
|
|
1169
|
-
|
|
1473
|
+
// Also exclude the value after --client since it's not a flag
|
|
1474
|
+
const clientValueIndex = clientFlagIndex !== -1 ? clientFlagIndex + 1 : -1;
|
|
1475
|
+
const unknownFlags = args.filter((arg, i) =>
|
|
1476
|
+
arg.startsWith('--') && !ALLOWED_FLAGS.includes(arg) && arg !== '--help' && i !== clientValueIndex
|
|
1477
|
+
);
|
|
1170
1478
|
if (unknownFlags.length > 0) {
|
|
1171
1479
|
logError(`Unknown flag: ${unknownFlags[0].slice(0, MAX_ARG_LENGTH)}`);
|
|
1172
1480
|
console.log('Run "npx beth-copilot help" for usage information.');
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client Configuration Persistence and Detection
|
|
3
|
+
*
|
|
4
|
+
* Persists the user's AI client selection (VS Code, Copilot CLI, Claude Code)
|
|
5
|
+
* to `.github/.beth-client.json` so other commands can detect which client
|
|
6
|
+
* was chosen during `init`.
|
|
7
|
+
*
|
|
8
|
+
* Falls back to marker file detection when no config file exists.
|
|
9
|
+
*/
|
|
10
|
+
export interface ClientSelection {
|
|
11
|
+
vscode: boolean;
|
|
12
|
+
copilotCli: boolean;
|
|
13
|
+
claudeCode: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const CLIENT_CONFIG_FILE = ".beth-client.json";
|
|
16
|
+
export declare const CLIENT_CONFIG_DIR = ".github";
|
|
17
|
+
/**
|
|
18
|
+
* Persist the client selection to `.github/.beth-client.json`.
|
|
19
|
+
* Creates the `.github/` directory if it doesn't exist.
|
|
20
|
+
* Overwrites any existing config file.
|
|
21
|
+
*/
|
|
22
|
+
export declare function persistClientConfig(cwd: string, clients: ClientSelection): void;
|
|
23
|
+
/**
|
|
24
|
+
* Detect the client configuration.
|
|
25
|
+
*
|
|
26
|
+
* 1. Tries to read `.github/.beth-client.json`
|
|
27
|
+
* 2. Falls back to marker file detection if config is missing or invalid
|
|
28
|
+
* 3. Defaults to `{ vscode: true, copilotCli: false, claudeCode: false }` if nothing detected
|
|
29
|
+
*/
|
|
30
|
+
export declare function detectClientConfig(cwd: string): ClientSelection;
|
|
31
|
+
//# sourceMappingURL=client-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/client-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AACtD,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAO/E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAiB/D"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E tests for each Beth client configuration.
|
|
3
|
+
*
|
|
4
|
+
* Tests the three supported client modes:
|
|
5
|
+
* - VS Code GitHub Copilot Chat (--client vscode)
|
|
6
|
+
* - GitHub Copilot CLI (--client copilot-cli)
|
|
7
|
+
* - Claude Code (--client claude-code)
|
|
8
|
+
*
|
|
9
|
+
* Each configuration installs a different set of files. These tests verify
|
|
10
|
+
* that each mode installs exactly what it should — and nothing extra.
|
|
11
|
+
*
|
|
12
|
+
* Run with: node --test dist/cli/commands/client-config.e2e.test.js
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=client-config.e2e.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-config.e2e.test.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/client-config.e2e.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|