beth-copilot 1.0.17 → 1.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 +41 -28
- package/README.md +87 -247
- package/bin/cli.js +115 -7
- 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 +49 -0
- package/dist/__tests__/smoke.test.js.map +1 -0
- package/dist/cli/commands/beads.e2e.test.d.ts +13 -0
- package/dist/cli/commands/beads.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/beads.e2e.test.js +526 -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 +89 -0
- package/dist/cli/commands/close.d.ts.map +1 -0
- package/dist/cli/commands/close.e2e.test.d.ts +27 -0
- package/dist/cli/commands/close.e2e.test.d.ts.map +1 -0
- package/dist/cli/commands/close.e2e.test.js +252 -0
- package/dist/cli/commands/close.e2e.test.js.map +1 -0
- package/dist/cli/commands/close.js +309 -0
- package/dist/cli/commands/close.js.map +1 -0
- package/dist/cli/commands/close.test.d.ts +15 -0
- package/dist/cli/commands/close.test.d.ts.map +1 -0
- package/dist/cli/commands/close.test.js +634 -0
- package/dist/cli/commands/close.test.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +23 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +93 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/doctor.test.js +209 -0
- 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 +119 -0
- package/dist/cli/commands/framework-isolation.test.js.map +1 -0
- 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 +305 -0
- package/dist/cli/commands/init-logic.e2e.test.js.map +1 -0
- package/dist/cli/commands/land.d.ts +142 -0
- package/dist/cli/commands/land.d.ts.map +1 -0
- package/dist/cli/commands/land.js +647 -0
- package/dist/cli/commands/land.js.map +1 -0
- package/dist/cli/commands/land.test.d.ts +20 -0
- package/dist/cli/commands/land.test.d.ts.map +1 -0
- package/dist/cli/commands/land.test.js +622 -0
- package/dist/cli/commands/land.test.js.map +1 -0
- package/dist/cli/commands/pipeline.e2e.test.js +1 -1
- package/dist/cli/commands/pipeline.e2e.test.js.map +1 -1
- package/dist/cli/commands/pre-push-guard.d.ts +84 -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 +257 -0
- package/dist/cli/commands/pre-push-guard.js.map +1 -0
- package/dist/cli/commands/pre-push-guard.test.d.ts +15 -0
- package/dist/cli/commands/pre-push-guard.test.d.ts.map +1 -0
- package/dist/cli/commands/pre-push-guard.test.js +397 -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 +179 -0
- package/dist/cli/commands/quickstart-expanded.e2e.test.js.map +1 -0
- package/dist/cli/commands/quickstart.test.js +40 -2
- package/dist/cli/commands/quickstart.test.js.map +1 -1
- package/dist/core/agents/suite.test.js +4 -2
- package/dist/core/agents/suite.test.js.map +1 -1
- package/dist/core/agents/tools.test.js +5 -1
- package/dist/core/agents/tools.test.js.map +1 -1
- package/dist/index.d.ts +3 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -10
- package/dist/index.js.map +1 -1
- package/package.json +15 -9
- package/sbom.json +2011 -819
- package/templates/.github/agents/beth.agent.md +222 -45
- package/templates/.github/agents/developer.agent.md +37 -67
- package/templates/.github/agents/product-manager.agent.md +15 -57
- package/templates/.github/agents/researcher.agent.md +20 -60
- package/templates/.github/agents/security-reviewer.agent.md +29 -70
- package/templates/.github/agents/tester.agent.md +40 -58
- package/templates/.github/agents/ux-designer.agent.md +20 -63
- package/templates/.github/copilot-instructions.md +217 -204
- package/templates/AGENTS.md +108 -20
- 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
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-Push Guard — Branch discipline enforcement
|
|
3
|
+
*
|
|
4
|
+
* Validates before push:
|
|
5
|
+
* - No direct pushes to main/master (BLOCKS)
|
|
6
|
+
* - Current branch follows epic/<id> convention (WARNING)
|
|
7
|
+
* - Open in-progress beads issues reported (WARNING)
|
|
8
|
+
* - Bypassed with BETH_SKIP_PUSH_GUARD=1 environment variable
|
|
9
|
+
*/
|
|
10
|
+
import { execFileSync } from 'child_process';
|
|
11
|
+
const COLORS = {
|
|
12
|
+
reset: '\x1b[0m',
|
|
13
|
+
bright: '\x1b[1m',
|
|
14
|
+
red: '\x1b[31m',
|
|
15
|
+
yellow: '\x1b[33m',
|
|
16
|
+
cyan: '\x1b[36m',
|
|
17
|
+
};
|
|
18
|
+
/** Protected branch names that cannot receive direct pushes. */
|
|
19
|
+
const PROTECTED_BRANCHES = ['main', 'master'];
|
|
20
|
+
/** Epic branch naming convention: epic/<rig>-<hash> */
|
|
21
|
+
const EPIC_BRANCH_PATTERN = /^epic\/[a-z]+-[a-z0-9]+$/;
|
|
22
|
+
/** Release branches are also valid push targets. */
|
|
23
|
+
const RELEASE_BRANCH_PATTERN = /^release\/v?\d+/;
|
|
24
|
+
/**
|
|
25
|
+
* Parse pre-push stdin input into structured refs.
|
|
26
|
+
* Git sends: <local ref> <local SHA> <remote ref> <remote SHA>
|
|
27
|
+
* One line per ref being pushed.
|
|
28
|
+
*/
|
|
29
|
+
export function parsePushRefs(stdin) {
|
|
30
|
+
return stdin
|
|
31
|
+
.trim()
|
|
32
|
+
.split('\n')
|
|
33
|
+
.filter((line) => line.trim().length > 0)
|
|
34
|
+
.map((line) => {
|
|
35
|
+
const parts = line.trim().split(/\s+/);
|
|
36
|
+
return {
|
|
37
|
+
localRef: parts[0] || '',
|
|
38
|
+
localSha: parts[1] || '',
|
|
39
|
+
remoteRef: parts[2] || '',
|
|
40
|
+
remoteSha: parts[3] || '',
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extract branch name from a Git ref.
|
|
46
|
+
* refs/heads/main → main
|
|
47
|
+
* refs/heads/epic/beth-abc123 → epic/beth-abc123
|
|
48
|
+
*/
|
|
49
|
+
export function extractBranchName(ref) {
|
|
50
|
+
const prefix = 'refs/heads/';
|
|
51
|
+
if (ref.startsWith(prefix)) {
|
|
52
|
+
return ref.slice(prefix.length);
|
|
53
|
+
}
|
|
54
|
+
return ref;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if a branch name is protected (main, master).
|
|
58
|
+
*/
|
|
59
|
+
export function isProtectedBranch(branch) {
|
|
60
|
+
return PROTECTED_BRANCHES.includes(branch);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if the branch follows the epic/<id> convention.
|
|
64
|
+
*/
|
|
65
|
+
export function isEpicBranch(branch) {
|
|
66
|
+
return EPIC_BRANCH_PATTERN.test(branch);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if the branch is a release branch.
|
|
70
|
+
*/
|
|
71
|
+
export function isReleaseBranch(branch) {
|
|
72
|
+
return RELEASE_BRANCH_PATTERN.test(branch);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check if the branch follows any recognized naming convention.
|
|
76
|
+
*/
|
|
77
|
+
export function isRecognizedBranch(branch) {
|
|
78
|
+
return isEpicBranch(branch) || isReleaseBranch(branch) || isProtectedBranch(branch);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get the current Git branch name.
|
|
82
|
+
* Returns null if not in a git repo or in detached HEAD state.
|
|
83
|
+
*/
|
|
84
|
+
export function getCurrentBranch() {
|
|
85
|
+
try {
|
|
86
|
+
const result = execFileSync('git', ['branch', '--show-current'], {
|
|
87
|
+
encoding: 'utf-8',
|
|
88
|
+
timeout: 5000,
|
|
89
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
90
|
+
}).trim();
|
|
91
|
+
return result || null;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get beads issues with in_progress status.
|
|
99
|
+
* Returns empty array if bd is unavailable or errors.
|
|
100
|
+
*/
|
|
101
|
+
export function getInProgressIssues() {
|
|
102
|
+
try {
|
|
103
|
+
const output = execFileSync('bd', ['list', '--json'], {
|
|
104
|
+
encoding: 'utf-8',
|
|
105
|
+
timeout: 10000,
|
|
106
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
107
|
+
});
|
|
108
|
+
const parsed = JSON.parse(output);
|
|
109
|
+
if (!Array.isArray(parsed))
|
|
110
|
+
return [];
|
|
111
|
+
return parsed
|
|
112
|
+
.filter((item) => typeof item === 'object' &&
|
|
113
|
+
item !== null &&
|
|
114
|
+
'id' in item &&
|
|
115
|
+
'title' in item &&
|
|
116
|
+
'status' in item &&
|
|
117
|
+
item.status === 'in_progress')
|
|
118
|
+
.map((item) => ({ id: item.id, title: item.title }));
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Run all pre-push guard checks.
|
|
126
|
+
*
|
|
127
|
+
* @param currentBranch - Current Git branch name
|
|
128
|
+
* @param refs - Optional parsed push refs from Git stdin (for remote ref validation)
|
|
129
|
+
* @param checkBeads - Whether to check for in-progress beads issues
|
|
130
|
+
* @returns GuardResult with allowed status and diagnostics
|
|
131
|
+
*/
|
|
132
|
+
export function runGuard(currentBranch, refs, checkBeads = true) {
|
|
133
|
+
const errors = [];
|
|
134
|
+
const warnings = [];
|
|
135
|
+
// Check 1: No pushing to protected branches via remote refs
|
|
136
|
+
if (refs && refs.length > 0) {
|
|
137
|
+
for (const ref of refs) {
|
|
138
|
+
const targetBranch = extractBranchName(ref.remoteRef);
|
|
139
|
+
if (isProtectedBranch(targetBranch)) {
|
|
140
|
+
errors.push(`Direct push to '${targetBranch}' is blocked. Use a PR from your epic branch.`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Check 1b: Also block if current branch IS a protected branch
|
|
145
|
+
// (catches the common case without needing stdin refs)
|
|
146
|
+
if (currentBranch && isProtectedBranch(currentBranch)) {
|
|
147
|
+
const msg = `Pushing from '${currentBranch}' is blocked. Work on an epic branch.`;
|
|
148
|
+
if (!errors.some((e) => e.includes(currentBranch))) {
|
|
149
|
+
errors.push(msg);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Check 2: Current branch should follow epic convention
|
|
153
|
+
if (currentBranch && !isRecognizedBranch(currentBranch)) {
|
|
154
|
+
warnings.push(`Branch '${currentBranch}' doesn't follow the epic/<id> convention. Consider renaming.`);
|
|
155
|
+
}
|
|
156
|
+
// Check 3: Warn about in-progress beads issues (soft check)
|
|
157
|
+
if (checkBeads) {
|
|
158
|
+
try {
|
|
159
|
+
const inProgress = getInProgressIssues();
|
|
160
|
+
if (inProgress.length > 0) {
|
|
161
|
+
warnings.push(`${inProgress.length} issue${inProgress.length === 1 ? '' : 's'} still in_progress:`);
|
|
162
|
+
for (const issue of inProgress) {
|
|
163
|
+
warnings.push(` ◐ ${issue.id}: ${issue.title}`);
|
|
164
|
+
}
|
|
165
|
+
warnings.push('Consider closing completed work before pushing.');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// bd not available — skip silently
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
allowed: errors.length === 0,
|
|
174
|
+
errors,
|
|
175
|
+
warnings,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Main entry point for the pre-push guard command.
|
|
180
|
+
* Reads refs from stdin (if available) and runs all checks.
|
|
181
|
+
*/
|
|
182
|
+
export async function prePushGuard(stdinInput) {
|
|
183
|
+
// Check bypass
|
|
184
|
+
if (process.env.BETH_SKIP_PUSH_GUARD === '1') {
|
|
185
|
+
console.error(`${COLORS.yellow}⚠ Pre-push guard bypassed (BETH_SKIP_PUSH_GUARD=1)${COLORS.reset}`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// Read stdin if not provided (Git pre-push pipes refs via stdin)
|
|
189
|
+
let stdin = stdinInput;
|
|
190
|
+
if (stdin === undefined) {
|
|
191
|
+
const fs = await import('fs');
|
|
192
|
+
try {
|
|
193
|
+
stdin = fs.readFileSync(0, 'utf-8');
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
stdin = '';
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const refs = stdin.trim() ? parsePushRefs(stdin) : undefined;
|
|
200
|
+
const currentBranch = getCurrentBranch();
|
|
201
|
+
const result = runGuard(currentBranch, refs);
|
|
202
|
+
// Print warnings
|
|
203
|
+
for (const warning of result.warnings) {
|
|
204
|
+
console.error(`${COLORS.yellow}⚠ ${warning}${COLORS.reset}`);
|
|
205
|
+
}
|
|
206
|
+
// Print errors and exit non-zero to block push
|
|
207
|
+
if (!result.allowed) {
|
|
208
|
+
console.error('');
|
|
209
|
+
for (const error of result.errors) {
|
|
210
|
+
console.error(`${COLORS.red}✗ ${error}${COLORS.reset}`);
|
|
211
|
+
}
|
|
212
|
+
console.error(`\n${COLORS.yellow}Set BETH_SKIP_PUSH_GUARD=1 to bypass.${COLORS.reset}`);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Generate the shell script content to append to .beads/hooks/pre-push.
|
|
218
|
+
* Pure shell — no Node dependency at hook time for speed.
|
|
219
|
+
*/
|
|
220
|
+
export function generateHookScript() {
|
|
221
|
+
return `
|
|
222
|
+
# --- BEGIN BETH GUARD ---
|
|
223
|
+
# Branch discipline enforcement — installed by beth-copilot
|
|
224
|
+
# Bypass: BETH_SKIP_PUSH_GUARD=1 git push
|
|
225
|
+
if [ "\$BETH_SKIP_PUSH_GUARD" = "1" ]; then
|
|
226
|
+
echo "⚠ Pre-push guard bypassed (BETH_SKIP_PUSH_GUARD=1)" >&2
|
|
227
|
+
else
|
|
228
|
+
_beth_branch=\$(git branch --show-current 2>/dev/null)
|
|
229
|
+
|
|
230
|
+
# Block pushes from protected branches
|
|
231
|
+
case "\$_beth_branch" in
|
|
232
|
+
main|master)
|
|
233
|
+
echo "✗ Pushing from '\$_beth_branch' is blocked. Work on an epic branch." >&2
|
|
234
|
+
echo " Set BETH_SKIP_PUSH_GUARD=1 to bypass." >&2
|
|
235
|
+
exit 1
|
|
236
|
+
;;
|
|
237
|
+
esac
|
|
238
|
+
|
|
239
|
+
# Warn if not on an epic or release branch
|
|
240
|
+
case "\$_beth_branch" in
|
|
241
|
+
epic/*) ;;
|
|
242
|
+
release/*) ;;
|
|
243
|
+
"")
|
|
244
|
+
echo "⚠ Detached HEAD — no branch name. Proceeding anyway." >&2
|
|
245
|
+
;;
|
|
246
|
+
*)
|
|
247
|
+
echo "⚠ Branch '\$_beth_branch' doesn't follow the epic/<id> convention." >&2
|
|
248
|
+
;;
|
|
249
|
+
esac
|
|
250
|
+
fi
|
|
251
|
+
# --- END BETH GUARD ---
|
|
252
|
+
`;
|
|
253
|
+
}
|
|
254
|
+
/** Marker used to detect if bethguard is already installed in a hook file. */
|
|
255
|
+
export const BETH_GUARD_BEGIN = '# --- BEGIN BETH GUARD ---';
|
|
256
|
+
export const BETH_GUARD_END = '# --- END BETH GUARD ---';
|
|
257
|
+
//# sourceMappingURL=pre-push-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-push-guard.js","sourceRoot":"","sources":["../../../src/cli/commands/pre-push-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-Push Guard Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests branch discipline enforcement:
|
|
5
|
+
* - Push ref parsing from Git stdin
|
|
6
|
+
* - Branch name extraction from refs
|
|
7
|
+
* - Protected branch detection
|
|
8
|
+
* - Epic branch convention validation
|
|
9
|
+
* - Release branch recognition
|
|
10
|
+
* - Guard logic: errors vs warnings
|
|
11
|
+
* - Hook script generation
|
|
12
|
+
* - Beads in-progress issue detection
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=pre-push-guard.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-push-guard.test.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/pre-push-guard.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|