cluttry 1.0.3 → 1.5.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 +71 -0
- package/README.md +203 -300
- package/dist/commands/completions.d.ts +16 -0
- package/dist/commands/completions.d.ts.map +1 -0
- package/dist/commands/completions.js +46 -0
- package/dist/commands/completions.js.map +1 -0
- package/dist/commands/explain-copy.d.ts +11 -0
- package/dist/commands/explain-copy.d.ts.map +1 -0
- package/dist/commands/explain-copy.js +34 -0
- package/dist/commands/explain-copy.js.map +1 -0
- package/dist/commands/finish.d.ts +20 -0
- package/dist/commands/finish.d.ts.map +1 -0
- package/dist/commands/finish.js +817 -0
- package/dist/commands/finish.js.map +1 -0
- package/dist/commands/gc.d.ts +22 -0
- package/dist/commands/gc.d.ts.map +1 -0
- package/dist/commands/gc.js +163 -0
- package/dist/commands/gc.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/open.d.ts +15 -1
- package/dist/commands/open.d.ts.map +1 -1
- package/dist/commands/open.js +96 -17
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/resume.d.ts +21 -0
- package/dist/commands/resume.d.ts.map +1 -0
- package/dist/commands/resume.js +106 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +6 -14
- package/dist/commands/rm.js.map +1 -1
- package/dist/commands/spawn.d.ts +3 -0
- package/dist/commands/spawn.d.ts.map +1 -1
- package/dist/commands/spawn.js +182 -19
- package/dist/commands/spawn.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +198 -9
- package/dist/index.js.map +1 -1
- package/dist/lib/completions.d.ts +35 -0
- package/dist/lib/completions.d.ts.map +1 -0
- package/dist/lib/completions.js +368 -0
- package/dist/lib/completions.js.map +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +2 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +43 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +251 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/git.d.ts +17 -0
- package/dist/lib/git.d.ts.map +1 -1
- package/dist/lib/git.js +78 -10
- package/dist/lib/git.js.map +1 -1
- package/dist/lib/safety.d.ts +79 -0
- package/dist/lib/safety.d.ts.map +1 -0
- package/dist/lib/safety.js +133 -0
- package/dist/lib/safety.js.map +1 -0
- package/dist/lib/secrets.d.ts +29 -0
- package/dist/lib/secrets.d.ts.map +1 -1
- package/dist/lib/secrets.js +115 -0
- package/dist/lib/secrets.js.map +1 -1
- package/dist/lib/session.d.ts +93 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/session.js +254 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/types.d.ts +6 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -7
- package/src/commands/doctor.ts +0 -222
- package/src/commands/init.ts +0 -120
- package/src/commands/list.ts +0 -133
- package/src/commands/open.ts +0 -78
- package/src/commands/prune.ts +0 -36
- package/src/commands/rm.ts +0 -125
- package/src/commands/shell.ts +0 -99
- package/src/commands/spawn.ts +0 -169
- package/src/index.ts +0 -123
- package/src/lib/config.ts +0 -120
- package/src/lib/git.ts +0 -243
- package/src/lib/output.ts +0 -102
- package/src/lib/paths.ts +0 -108
- package/src/lib/secrets.ts +0 -193
- package/src/lib/types.ts +0 -69
- package/tests/config.test.ts +0 -102
- package/tests/paths.test.ts +0 -155
- package/tests/secrets.test.ts +0 -150
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -15
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured error handling for cry CLI
|
|
3
|
+
*
|
|
4
|
+
* Each error includes:
|
|
5
|
+
* - what: What happened
|
|
6
|
+
* - why: Why it likely happened
|
|
7
|
+
* - fix: Exact command(s) the user should run
|
|
8
|
+
*/
|
|
9
|
+
import * as out from './output.js';
|
|
10
|
+
/**
|
|
11
|
+
* Print a structured error message and exit
|
|
12
|
+
*/
|
|
13
|
+
export function fail(error) {
|
|
14
|
+
out.error(error.what);
|
|
15
|
+
out.newline();
|
|
16
|
+
out.log(`${out.fmt.bold('Why:')} ${error.why}`);
|
|
17
|
+
out.newline();
|
|
18
|
+
out.log(out.fmt.bold('Fix:'));
|
|
19
|
+
const fixes = Array.isArray(error.fix) ? error.fix : [error.fix];
|
|
20
|
+
for (const fix of fixes) {
|
|
21
|
+
out.log(` ${out.fmt.cyan(fix)}`);
|
|
22
|
+
}
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Print a structured error without exiting (for non-fatal errors)
|
|
27
|
+
*/
|
|
28
|
+
export function printError(error) {
|
|
29
|
+
out.error(error.what);
|
|
30
|
+
out.newline();
|
|
31
|
+
out.log(`${out.fmt.bold('Why:')} ${error.why}`);
|
|
32
|
+
out.newline();
|
|
33
|
+
out.log(out.fmt.bold('Fix:'));
|
|
34
|
+
const fixes = Array.isArray(error.fix) ? error.fix : [error.fix];
|
|
35
|
+
for (const fix of fixes) {
|
|
36
|
+
out.log(` ${out.fmt.cyan(fix)}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
|
+
// Common error factories
|
|
41
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
42
|
+
export const errors = {
|
|
43
|
+
notGitRepo() {
|
|
44
|
+
return {
|
|
45
|
+
what: 'Not a git repository.',
|
|
46
|
+
why: 'cry requires a git repository to manage worktrees. The current directory is not inside a git repo.',
|
|
47
|
+
fix: [
|
|
48
|
+
'cd /path/to/your/repo',
|
|
49
|
+
'git init # or clone an existing repo',
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
gitNotInstalled() {
|
|
54
|
+
return {
|
|
55
|
+
what: 'Git is not installed or not in PATH.',
|
|
56
|
+
why: 'cry requires git to manage worktrees. Git was not found on your system.',
|
|
57
|
+
fix: [
|
|
58
|
+
'brew install git # macOS',
|
|
59
|
+
'sudo apt install git # Ubuntu/Debian',
|
|
60
|
+
'https://git-scm.com/downloads',
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
branchAlreadyExists(branch) {
|
|
65
|
+
return {
|
|
66
|
+
what: `Branch '${branch}' already exists.`,
|
|
67
|
+
why: 'You used --new but the branch already exists locally or remotely.',
|
|
68
|
+
fix: [
|
|
69
|
+
`cry spawn ${branch} # use existing branch (without --new)`,
|
|
70
|
+
`cry spawn ${branch}-v2 --new # choose a different name`,
|
|
71
|
+
`git branch -d ${branch} # delete the existing branch first`,
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
worktreeAlreadyExists(branch, existingPath) {
|
|
76
|
+
return {
|
|
77
|
+
what: `A worktree already exists for branch '${branch}'.`,
|
|
78
|
+
why: `The branch is already checked out in another worktree at: ${existingPath}`,
|
|
79
|
+
fix: [
|
|
80
|
+
`cry rm ${branch} # remove the existing worktree`,
|
|
81
|
+
`cry open ${branch} # open the existing worktree instead`,
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
destinationExists(path) {
|
|
86
|
+
return {
|
|
87
|
+
what: `Destination already exists: ${path}`,
|
|
88
|
+
why: 'The target directory for the worktree already exists on disk.',
|
|
89
|
+
fix: [
|
|
90
|
+
`rm -rf "${path}" # remove the directory`,
|
|
91
|
+
`cry spawn <branch> --path /different/path # use a different path`,
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
dirtyWorkingTree(branch) {
|
|
96
|
+
return {
|
|
97
|
+
what: 'Worktree has uncommitted changes.',
|
|
98
|
+
why: 'There are modified or untracked files that would be lost.',
|
|
99
|
+
fix: [
|
|
100
|
+
'git status # see what changed',
|
|
101
|
+
'git add . && git commit -m "WIP" # commit changes',
|
|
102
|
+
`cry rm ${branch} --force # remove anyway (changes lost)`,
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
ghNotInstalled() {
|
|
107
|
+
return {
|
|
108
|
+
what: 'GitHub CLI (gh) is not installed.',
|
|
109
|
+
why: 'cry uses the gh CLI to create pull requests. It was not found on your system.',
|
|
110
|
+
fix: [
|
|
111
|
+
'brew install gh # macOS',
|
|
112
|
+
'sudo apt install gh # Ubuntu/Debian',
|
|
113
|
+
'https://cli.github.com/',
|
|
114
|
+
'gh auth login # after installing, authenticate',
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
ghNotAuthenticated() {
|
|
119
|
+
return {
|
|
120
|
+
what: 'GitHub CLI is not authenticated.',
|
|
121
|
+
why: 'You have gh installed but have not logged in yet.',
|
|
122
|
+
fix: [
|
|
123
|
+
'gh auth login',
|
|
124
|
+
'gh auth status # verify authentication',
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
noRemoteConfigured() {
|
|
129
|
+
return {
|
|
130
|
+
what: 'No remote repository configured.',
|
|
131
|
+
why: 'This repository has no origin remote, so there is nowhere to push or create PRs.',
|
|
132
|
+
fix: [
|
|
133
|
+
'git remote add origin https://github.com/user/repo.git',
|
|
134
|
+
'git remote add origin git@github.com:user/repo.git',
|
|
135
|
+
'git remote -v # list current remotes',
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
pushFailed(branch) {
|
|
140
|
+
return {
|
|
141
|
+
what: `Failed to push branch '${branch}'.`,
|
|
142
|
+
why: 'The push was rejected. This could be due to no remote, auth issues, or branch protection.',
|
|
143
|
+
fix: [
|
|
144
|
+
'git remote -v # check remote is configured',
|
|
145
|
+
'gh auth status # check GitHub authentication',
|
|
146
|
+
`git push -u origin ${branch} # try manually`,
|
|
147
|
+
'git fetch origin && git rebase origin/main # if behind remote',
|
|
148
|
+
],
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
permissionDenied(path) {
|
|
152
|
+
return {
|
|
153
|
+
what: `Permission denied: ${path}`,
|
|
154
|
+
why: 'You do not have permission to read or write to this path.',
|
|
155
|
+
fix: [
|
|
156
|
+
`ls -la "${path}" # check permissions`,
|
|
157
|
+
`sudo chown -R $(whoami) "${path}" # take ownership (if appropriate)`,
|
|
158
|
+
'Try a different directory with --path',
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
worktreeNotFound(nameOrPath, available) {
|
|
163
|
+
const availableList = available.slice(0, 5).map(a => ` • ${a}`).join('\n');
|
|
164
|
+
return {
|
|
165
|
+
what: `Worktree not found: ${nameOrPath}`,
|
|
166
|
+
why: 'No worktree matches that branch name or path.',
|
|
167
|
+
fix: [
|
|
168
|
+
'cry list # see all worktrees',
|
|
169
|
+
`Available:\n${availableList}`,
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
sessionNotFound(nameOrId) {
|
|
174
|
+
return {
|
|
175
|
+
what: `Session not found: ${nameOrId}`,
|
|
176
|
+
why: 'No session matches that branch name or session ID.',
|
|
177
|
+
fix: [
|
|
178
|
+
'cry list # see all worktrees and sessions',
|
|
179
|
+
'cry spawn <branch> --new # create a new session',
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
detachedHead() {
|
|
184
|
+
return {
|
|
185
|
+
what: 'Cannot determine base branch: HEAD is detached.',
|
|
186
|
+
why: 'You are not on a branch, so cry cannot determine the PR target.',
|
|
187
|
+
fix: [
|
|
188
|
+
'git checkout main # switch to a branch first',
|
|
189
|
+
'cry spawn <branch> --new --base-branch main # specify base explicitly',
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
},
|
|
193
|
+
configNotFound() {
|
|
194
|
+
return {
|
|
195
|
+
what: 'No .cry.json found.',
|
|
196
|
+
why: 'This repository has not been initialized with cry.',
|
|
197
|
+
fix: [
|
|
198
|
+
'cry init # initialize cry in this repo',
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
agentNotFound(agent) {
|
|
203
|
+
return {
|
|
204
|
+
what: `Agent command not found: ${agent}`,
|
|
205
|
+
why: `The '${agent}' command is not installed or not in your PATH.`,
|
|
206
|
+
fix: agent === 'claude' ? [
|
|
207
|
+
'npm install -g @anthropic-ai/claude-code',
|
|
208
|
+
'https://docs.anthropic.com/claude-code',
|
|
209
|
+
] : agent === 'cursor' ? [
|
|
210
|
+
'Download Cursor from https://cursor.sh',
|
|
211
|
+
'Ensure cursor CLI is in your PATH',
|
|
212
|
+
] : [
|
|
213
|
+
`Install ${agent} or update agentCommand in .cry.json`,
|
|
214
|
+
],
|
|
215
|
+
};
|
|
216
|
+
},
|
|
217
|
+
editorNotFound(editor) {
|
|
218
|
+
return {
|
|
219
|
+
what: `Editor command not found: ${editor}`,
|
|
220
|
+
why: `The '${editor}' command is not installed or not in your PATH.`,
|
|
221
|
+
fix: editor === 'code' ? [
|
|
222
|
+
'Install VS Code from https://code.visualstudio.com',
|
|
223
|
+
'Run "Shell Command: Install code in PATH" from VS Code',
|
|
224
|
+
] : [
|
|
225
|
+
`Install ${editor} or update editorCommand in .cry.json`,
|
|
226
|
+
],
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
cannotRemoveMainWorktree() {
|
|
230
|
+
return {
|
|
231
|
+
what: 'Cannot remove the main worktree.',
|
|
232
|
+
why: 'This is your primary repository checkout. Removing it would delete your repo.',
|
|
233
|
+
fix: [
|
|
234
|
+
'cry list # see worktrees - the main one cannot be removed',
|
|
235
|
+
'Use cry rm <branch> to remove other worktrees',
|
|
236
|
+
],
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
worktreeMissing(path, branch) {
|
|
240
|
+
return {
|
|
241
|
+
what: `Worktree no longer exists: ${path}`,
|
|
242
|
+
why: 'The worktree directory was deleted but the session manifest still exists.',
|
|
243
|
+
fix: [
|
|
244
|
+
`cry spawn ${branch} --new # recreate the worktree`,
|
|
245
|
+
'cry gc # clean up stale sessions',
|
|
246
|
+
'cry prune # clean up git worktree references',
|
|
247
|
+
],
|
|
248
|
+
};
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AAQnC;;GAEG;AACH,MAAM,UAAU,IAAI,CAAC,KAAe;IAClC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,GAAG,CAAC,OAAO,EAAE,CAAC;IACd,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IAChD,GAAG,CAAC,OAAO,EAAE,CAAC;IACd,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjE,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,GAAG,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,KAAe;IACxC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,GAAG,CAAC,OAAO,EAAE,CAAC;IACd,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IAChD,GAAG,CAAC,OAAO,EAAE,CAAC;IACd,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjE,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,GAAG,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,yBAAyB;AACzB,gFAAgF;AAEhF,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,UAAU;QACR,OAAO;YACL,IAAI,EAAE,uBAAuB;YAC7B,GAAG,EAAE,oGAAoG;YACzG,GAAG,EAAE;gBACH,uBAAuB;gBACvB,uCAAuC;aACxC;SACF,CAAC;IACJ,CAAC;IAED,eAAe;QACb,OAAO;YACL,IAAI,EAAE,sCAAsC;YAC5C,GAAG,EAAE,yEAAyE;YAC9E,GAAG,EAAE;gBACH,2BAA2B;gBAC3B,uCAAuC;gBACvC,+BAA+B;aAChC;SACF,CAAC;IACJ,CAAC;IAED,mBAAmB,CAAC,MAAc;QAChC,OAAO;YACL,IAAI,EAAE,WAAW,MAAM,mBAAmB;YAC1C,GAAG,EAAE,mEAAmE;YACxE,GAAG,EAAE;gBACH,aAAa,MAAM,yCAAyC;gBAC5D,aAAa,MAAM,sCAAsC;gBACzD,iBAAiB,MAAM,sCAAsC;aAC9D;SACF,CAAC;IACJ,CAAC;IAED,qBAAqB,CAAC,MAAc,EAAE,YAAoB;QACxD,OAAO;YACL,IAAI,EAAE,yCAAyC,MAAM,IAAI;YACzD,GAAG,EAAE,6DAA6D,YAAY,EAAE;YAChF,GAAG,EAAE;gBACH,UAAU,MAAM,kCAAkC;gBAClD,YAAY,MAAM,wCAAwC;aAC3D;SACF,CAAC;IACJ,CAAC;IAED,iBAAiB,CAAC,IAAY;QAC5B,OAAO;YACL,IAAI,EAAE,+BAA+B,IAAI,EAAE;YAC3C,GAAG,EAAE,+DAA+D;YACpE,GAAG,EAAE;gBACH,WAAW,IAAI,2BAA2B;gBAC1C,mEAAmE;aACpE;SACF,CAAC;IACJ,CAAC;IAED,gBAAgB,CAAC,MAAc;QAC7B,OAAO;YACL,IAAI,EAAE,mCAAmC;YACzC,GAAG,EAAE,2DAA2D;YAChE,GAAG,EAAE;gBACH,gCAAgC;gBAChC,oDAAoD;gBACpD,UAAU,MAAM,0CAA0C;aAC3D;SACF,CAAC;IACJ,CAAC;IAED,cAAc;QACZ,OAAO;YACL,IAAI,EAAE,mCAAmC;YACzC,GAAG,EAAE,+EAA+E;YACpF,GAAG,EAAE;gBACH,0BAA0B;gBAC1B,sCAAsC;gBACtC,yBAAyB;gBACzB,iDAAiD;aAClD;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB;QAChB,OAAO;YACL,IAAI,EAAE,kCAAkC;YACxC,GAAG,EAAE,mDAAmD;YACxD,GAAG,EAAE;gBACH,eAAe;gBACf,yCAAyC;aAC1C;SACF,CAAC;IACJ,CAAC;IAED,kBAAkB;QAChB,OAAO;YACL,IAAI,EAAE,kCAAkC;YACxC,GAAG,EAAE,kFAAkF;YACvF,GAAG,EAAE;gBACH,wDAAwD;gBACxD,oDAAoD;gBACpD,uCAAuC;aACxC;SACF,CAAC;IACJ,CAAC;IAED,UAAU,CAAC,MAAc;QACvB,OAAO;YACL,IAAI,EAAE,0BAA0B,MAAM,IAAI;YAC1C,GAAG,EAAE,2FAA2F;YAChG,GAAG,EAAE;gBACH,6CAA6C;gBAC7C,+CAA+C;gBAC/C,sBAAsB,MAAM,kBAAkB;gBAC9C,gEAAgE;aACjE;SACF,CAAC;IACJ,CAAC;IAED,gBAAgB,CAAC,IAAY;QAC3B,OAAO;YACL,IAAI,EAAE,sBAAsB,IAAI,EAAE;YAClC,GAAG,EAAE,2DAA2D;YAChE,GAAG,EAAE;gBACH,WAAW,IAAI,wBAAwB;gBACvC,4BAA4B,IAAI,sCAAsC;gBACtE,uCAAuC;aACxC;SACF,CAAC;IACJ,CAAC;IAED,gBAAgB,CAAC,UAAkB,EAAE,SAAmB;QACtD,MAAM,aAAa,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5E,OAAO;YACL,IAAI,EAAE,uBAAuB,UAAU,EAAE;YACzC,GAAG,EAAE,+CAA+C;YACpD,GAAG,EAAE;gBACH,+BAA+B;gBAC/B,eAAe,aAAa,EAAE;aAC/B;SACF,CAAC;IACJ,CAAC;IAED,eAAe,CAAC,QAAgB;QAC9B,OAAO;YACL,IAAI,EAAE,sBAAsB,QAAQ,EAAE;YACtC,GAAG,EAAE,oDAAoD;YACzD,GAAG,EAAE;gBACH,4CAA4C;gBAC5C,kDAAkD;aACnD;SACF,CAAC;IACJ,CAAC;IAED,YAAY;QACV,OAAO;YACL,IAAI,EAAE,iDAAiD;YACvD,GAAG,EAAE,iEAAiE;YACtE,GAAG,EAAE;gBACH,+CAA+C;gBAC/C,wEAAwE;aACzE;SACF,CAAC;IACJ,CAAC;IAED,cAAc;QACZ,OAAO;YACL,IAAI,EAAE,qBAAqB;YAC3B,GAAG,EAAE,oDAAoD;YACzD,GAAG,EAAE;gBACH,yCAAyC;aAC1C;SACF,CAAC;IACJ,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,OAAO;YACL,IAAI,EAAE,4BAA4B,KAAK,EAAE;YACzC,GAAG,EAAE,QAAQ,KAAK,iDAAiD;YACnE,GAAG,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACxB,0CAA0C;gBAC1C,wCAAwC;aACzC,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACvB,wCAAwC;gBACxC,mCAAmC;aACpC,CAAC,CAAC,CAAC;gBACF,WAAW,KAAK,sCAAsC;aACvD;SACF,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,MAAc;QAC3B,OAAO;YACL,IAAI,EAAE,6BAA6B,MAAM,EAAE;YAC3C,GAAG,EAAE,QAAQ,MAAM,iDAAiD;YACpE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC;gBACvB,oDAAoD;gBACpD,wDAAwD;aACzD,CAAC,CAAC,CAAC;gBACF,WAAW,MAAM,uCAAuC;aACzD;SACF,CAAC;IACJ,CAAC;IAED,wBAAwB;QACtB,OAAO;YACL,IAAI,EAAE,kCAAkC;YACxC,GAAG,EAAE,+EAA+E;YACpF,GAAG,EAAE;gBACH,4DAA4D;gBAC5D,+CAA+C;aAChD;SACF,CAAC;IACJ,CAAC;IAED,eAAe,CAAC,IAAY,EAAE,MAAc;QAC1C,OAAO;YACL,IAAI,EAAE,8BAA8B,IAAI,EAAE;YAC1C,GAAG,EAAE,2EAA2E;YAChF,GAAG,EAAE;gBACH,aAAa,MAAM,iCAAiC;gBACpD,mCAAmC;gBACnC,+CAA+C;aAChD;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
|
package/dist/lib/git.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { WorktreeInfo } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Execute a git command and return stdout
|
|
7
|
+
* Uses spawnSync to properly handle arguments with spaces
|
|
7
8
|
*/
|
|
8
9
|
export declare function git(args: string[], cwd?: string): string;
|
|
9
10
|
/**
|
|
@@ -26,6 +27,22 @@ export declare function branchExists(branch: string, cwd?: string): boolean;
|
|
|
26
27
|
* Get the current branch name
|
|
27
28
|
*/
|
|
28
29
|
export declare function getCurrentBranch(cwd?: string): string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Check if HEAD is detached (not on any branch)
|
|
32
|
+
*/
|
|
33
|
+
export declare function isDetachedHead(cwd?: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Get the default branch name (from origin/HEAD or common defaults)
|
|
36
|
+
*/
|
|
37
|
+
export declare function getDefaultBranch(cwd?: string): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Get the merge-base between two refs
|
|
40
|
+
*/
|
|
41
|
+
export declare function getMergeBase(ref1: string, ref2: string, cwd?: string): string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Get the upstream tracking branch for the current branch
|
|
44
|
+
*/
|
|
45
|
+
export declare function getUpstreamBranch(cwd?: string): string | null;
|
|
29
46
|
/**
|
|
30
47
|
* Check if a file is tracked by git
|
|
31
48
|
*/
|
package/dist/lib/git.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAaxD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAOlE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW5D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqB5D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMpF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO7D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAOjE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAOjE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAiC1D;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,EACrB,GAAG,CAAC,EAAE,MAAM,GACX,IAAI,CAUN;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAOvF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAG/E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAO7D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBxE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAWlD"}
|
package/dist/lib/git.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Git operations for cry
|
|
3
3
|
*/
|
|
4
|
-
import { execSync, spawn } from 'node:child_process';
|
|
4
|
+
import { execSync, spawn, spawnSync } from 'node:child_process';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
/**
|
|
7
7
|
* Execute a git command and return stdout
|
|
8
|
+
* Uses spawnSync to properly handle arguments with spaces
|
|
8
9
|
*/
|
|
9
10
|
export function git(args, cwd) {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const stderr =
|
|
11
|
+
const result = spawnSync('git', args, {
|
|
12
|
+
cwd,
|
|
13
|
+
encoding: 'utf-8',
|
|
14
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
15
|
+
});
|
|
16
|
+
if (result.status !== 0) {
|
|
17
|
+
const stderr = result.stderr?.toString?.() || result.error?.message || 'Unknown git error';
|
|
17
18
|
throw new Error(stderr.trim());
|
|
18
19
|
}
|
|
20
|
+
return (result.stdout || '').trim();
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* Check if we're in a git repository
|
|
@@ -59,7 +61,71 @@ export function branchExists(branch, cwd) {
|
|
|
59
61
|
*/
|
|
60
62
|
export function getCurrentBranch(cwd) {
|
|
61
63
|
try {
|
|
62
|
-
|
|
64
|
+
const branch = git(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);
|
|
65
|
+
// Returns 'HEAD' when in detached state
|
|
66
|
+
if (branch === 'HEAD') {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return branch;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if HEAD is detached (not on any branch)
|
|
77
|
+
*/
|
|
78
|
+
export function isDetachedHead(cwd) {
|
|
79
|
+
try {
|
|
80
|
+
const branch = git(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);
|
|
81
|
+
return branch === 'HEAD';
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get the default branch name (from origin/HEAD or common defaults)
|
|
89
|
+
*/
|
|
90
|
+
export function getDefaultBranch(cwd) {
|
|
91
|
+
// Try origin/HEAD first
|
|
92
|
+
try {
|
|
93
|
+
const ref = git(['symbolic-ref', 'refs/remotes/origin/HEAD'], cwd);
|
|
94
|
+
// Returns something like "refs/remotes/origin/main"
|
|
95
|
+
const match = ref.match(/refs\/remotes\/origin\/(.+)$/);
|
|
96
|
+
if (match) {
|
|
97
|
+
return match[1];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// origin/HEAD not set, try other methods
|
|
102
|
+
}
|
|
103
|
+
// Try to find main or master
|
|
104
|
+
for (const candidate of ['main', 'master']) {
|
|
105
|
+
if (branchExists(candidate, cwd)) {
|
|
106
|
+
return candidate;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get the merge-base between two refs
|
|
113
|
+
*/
|
|
114
|
+
export function getMergeBase(ref1, ref2, cwd) {
|
|
115
|
+
try {
|
|
116
|
+
return git(['merge-base', ref1, ref2], cwd);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get the upstream tracking branch for the current branch
|
|
124
|
+
*/
|
|
125
|
+
export function getUpstreamBranch(cwd) {
|
|
126
|
+
try {
|
|
127
|
+
const upstream = git(['rev-parse', '--abbrev-ref', '@{upstream}'], cwd);
|
|
128
|
+
return upstream;
|
|
63
129
|
}
|
|
64
130
|
catch {
|
|
65
131
|
return null;
|
|
@@ -213,8 +279,10 @@ export function runCommand(command, cwd) {
|
|
|
213
279
|
*/
|
|
214
280
|
export function commandExists(cmd) {
|
|
215
281
|
try {
|
|
282
|
+
// Extract just the command name (first word) from a potentially complex command
|
|
283
|
+
const cmdName = cmd.split(/\s+/)[0];
|
|
216
284
|
const isWindows = process.platform === 'win32';
|
|
217
|
-
const checkCmd = isWindows ? `where ${
|
|
285
|
+
const checkCmd = isWindows ? `where ${cmdName}` : `which ${cmdName}`;
|
|
218
286
|
execSync(checkCmd, { stdio: 'pipe' });
|
|
219
287
|
return true;
|
|
220
288
|
}
|
package/dist/lib/git.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety module for dangerous operations
|
|
3
|
+
*
|
|
4
|
+
* This module enforces hard safety constraints:
|
|
5
|
+
* 1. Never auto-merge PRs
|
|
6
|
+
* 2. Never delete branches/worktrees without explicit confirmation
|
|
7
|
+
* 3. Never copy tracked files to worktrees
|
|
8
|
+
*
|
|
9
|
+
* All dangerous operations must go through this module.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Types of dangerous operations
|
|
13
|
+
*/
|
|
14
|
+
export type DangerousOperation = 'delete_worktree' | 'delete_branch' | 'force_remove_dirty' | 'cleanup_session' | 'merge_pr';
|
|
15
|
+
/**
|
|
16
|
+
* Confirmation state for dangerous operations
|
|
17
|
+
*/
|
|
18
|
+
export interface ConfirmationState {
|
|
19
|
+
operation: DangerousOperation;
|
|
20
|
+
confirmed: boolean;
|
|
21
|
+
method: 'prompt' | 'flag' | 'none';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* HARD CONSTRAINT: Merge is never allowed by default
|
|
25
|
+
* This function always returns false - merge must be done manually
|
|
26
|
+
*/
|
|
27
|
+
export declare function isMergeAllowed(): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* HARD CONSTRAINT: Check if deletion is explicitly confirmed
|
|
30
|
+
*
|
|
31
|
+
* Deletion requires EITHER:
|
|
32
|
+
* - Interactive confirmation (user typed 'y')
|
|
33
|
+
* - Explicit --yes flag
|
|
34
|
+
* - Explicit --cleanup flag (for finish command)
|
|
35
|
+
*/
|
|
36
|
+
export declare function isDeletionConfirmed(state: ConfirmationState): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Require explicit confirmation for a dangerous operation
|
|
39
|
+
*
|
|
40
|
+
* @param operation - Type of dangerous operation
|
|
41
|
+
* @param options - Confirmation options
|
|
42
|
+
* @returns ConfirmationState with confirmed status
|
|
43
|
+
*/
|
|
44
|
+
export declare function requireConfirmation(operation: DangerousOperation, options: {
|
|
45
|
+
skipPrompt?: boolean;
|
|
46
|
+
autoConfirm?: boolean;
|
|
47
|
+
message?: string;
|
|
48
|
+
}): Promise<ConfirmationState>;
|
|
49
|
+
/**
|
|
50
|
+
* Assert that a dangerous operation was properly confirmed
|
|
51
|
+
* Throws if not confirmed - use this as a guard
|
|
52
|
+
*/
|
|
53
|
+
export declare function assertConfirmed(state: ConfirmationState): void;
|
|
54
|
+
/**
|
|
55
|
+
* HARD CONSTRAINT: Check if a file can be copied to worktree
|
|
56
|
+
*
|
|
57
|
+
* A file can ONLY be copied if:
|
|
58
|
+
* 1. It is NOT tracked by git
|
|
59
|
+
* 2. It IS ignored by git (in .gitignore)
|
|
60
|
+
*
|
|
61
|
+
* This function does not do the actual check - that's in secrets.ts
|
|
62
|
+
* This documents the invariant.
|
|
63
|
+
*/
|
|
64
|
+
export interface FileCopyDecision {
|
|
65
|
+
path: string;
|
|
66
|
+
allowed: boolean;
|
|
67
|
+
reason: string;
|
|
68
|
+
isTracked: boolean;
|
|
69
|
+
isIgnored: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Validate that a file copy decision follows safety rules
|
|
73
|
+
*/
|
|
74
|
+
export declare function validateFileCopy(decision: FileCopyDecision): void;
|
|
75
|
+
/**
|
|
76
|
+
* Safety summary for logging/debugging
|
|
77
|
+
*/
|
|
78
|
+
export declare function getSafetyConstraints(): string[];
|
|
79
|
+
//# sourceMappingURL=safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety.d.ts","sourceRoot":"","sources":["../../src/lib/safety.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,iBAAiB,GACjB,eAAe,GACf,oBAAoB,GACpB,iBAAiB,GACjB,UAAU,CAAC;AAEf;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAIxC;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAWrE;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,kBAAkB,EAC7B,OAAO,EAAE;IACP,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACA,OAAO,CAAC,iBAAiB,CAAC,CAmB5B;AAuCD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAO9D;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAgBjE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAM/C"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety module for dangerous operations
|
|
3
|
+
*
|
|
4
|
+
* This module enforces hard safety constraints:
|
|
5
|
+
* 1. Never auto-merge PRs
|
|
6
|
+
* 2. Never delete branches/worktrees without explicit confirmation
|
|
7
|
+
* 3. Never copy tracked files to worktrees
|
|
8
|
+
*
|
|
9
|
+
* All dangerous operations must go through this module.
|
|
10
|
+
*/
|
|
11
|
+
import * as readline from 'node:readline';
|
|
12
|
+
/**
|
|
13
|
+
* HARD CONSTRAINT: Merge is never allowed by default
|
|
14
|
+
* This function always returns false - merge must be done manually
|
|
15
|
+
*/
|
|
16
|
+
export function isMergeAllowed() {
|
|
17
|
+
// SAFETY: Auto-merge is NEVER allowed
|
|
18
|
+
// Users must merge PRs manually through GitHub UI
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* HARD CONSTRAINT: Check if deletion is explicitly confirmed
|
|
23
|
+
*
|
|
24
|
+
* Deletion requires EITHER:
|
|
25
|
+
* - Interactive confirmation (user typed 'y')
|
|
26
|
+
* - Explicit --yes flag
|
|
27
|
+
* - Explicit --cleanup flag (for finish command)
|
|
28
|
+
*/
|
|
29
|
+
export function isDeletionConfirmed(state) {
|
|
30
|
+
if (!state.confirmed) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
// Must have explicit confirmation method
|
|
34
|
+
if (state.method === 'none') {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Require explicit confirmation for a dangerous operation
|
|
41
|
+
*
|
|
42
|
+
* @param operation - Type of dangerous operation
|
|
43
|
+
* @param options - Confirmation options
|
|
44
|
+
* @returns ConfirmationState with confirmed status
|
|
45
|
+
*/
|
|
46
|
+
export async function requireConfirmation(operation, options) {
|
|
47
|
+
// If --yes or --cleanup flag provided, treat as confirmed via flag
|
|
48
|
+
if (options.skipPrompt || options.autoConfirm) {
|
|
49
|
+
return {
|
|
50
|
+
operation,
|
|
51
|
+
confirmed: true,
|
|
52
|
+
method: 'flag',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Otherwise, require interactive prompt
|
|
56
|
+
const message = options.message ?? getDefaultMessage(operation);
|
|
57
|
+
const confirmed = await promptConfirmation(message);
|
|
58
|
+
return {
|
|
59
|
+
operation,
|
|
60
|
+
confirmed,
|
|
61
|
+
method: confirmed ? 'prompt' : 'none',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Interactive confirmation prompt
|
|
66
|
+
*/
|
|
67
|
+
async function promptConfirmation(message) {
|
|
68
|
+
const rl = readline.createInterface({
|
|
69
|
+
input: process.stdin,
|
|
70
|
+
output: process.stdout,
|
|
71
|
+
});
|
|
72
|
+
return new Promise((resolve) => {
|
|
73
|
+
rl.question(`${message} [y/N] `, (answer) => {
|
|
74
|
+
rl.close();
|
|
75
|
+
const normalized = answer.toLowerCase().trim();
|
|
76
|
+
resolve(normalized === 'y' || normalized === 'yes');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get default confirmation message for an operation
|
|
82
|
+
*/
|
|
83
|
+
function getDefaultMessage(operation) {
|
|
84
|
+
switch (operation) {
|
|
85
|
+
case 'delete_worktree':
|
|
86
|
+
return 'Remove worktree?';
|
|
87
|
+
case 'delete_branch':
|
|
88
|
+
return 'Delete branch?';
|
|
89
|
+
case 'force_remove_dirty':
|
|
90
|
+
return 'Worktree has uncommitted changes. Remove anyway?';
|
|
91
|
+
case 'cleanup_session':
|
|
92
|
+
return 'Clean up session (remove worktree)?';
|
|
93
|
+
case 'merge_pr':
|
|
94
|
+
// This should never be called - merge is not allowed
|
|
95
|
+
return 'Merge is not supported. Please merge manually.';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Assert that a dangerous operation was properly confirmed
|
|
100
|
+
* Throws if not confirmed - use this as a guard
|
|
101
|
+
*/
|
|
102
|
+
export function assertConfirmed(state) {
|
|
103
|
+
if (!isDeletionConfirmed(state)) {
|
|
104
|
+
throw new Error(`Safety violation: ${state.operation} was not explicitly confirmed. ` +
|
|
105
|
+
`Use --yes flag or answer 'y' to confirmation prompt.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Validate that a file copy decision follows safety rules
|
|
110
|
+
*/
|
|
111
|
+
export function validateFileCopy(decision) {
|
|
112
|
+
// HARD CONSTRAINT: Tracked files are NEVER copied
|
|
113
|
+
if (decision.isTracked && decision.allowed) {
|
|
114
|
+
throw new Error(`Safety violation: Attempted to copy tracked file '${decision.path}'. ` +
|
|
115
|
+
`Tracked files must NEVER be copied to worktrees.`);
|
|
116
|
+
}
|
|
117
|
+
// HARD CONSTRAINT: Non-ignored files should not be copied
|
|
118
|
+
if (!decision.isIgnored && decision.allowed) {
|
|
119
|
+
throw new Error(`Safety violation: Attempted to copy non-ignored file '${decision.path}'. ` +
|
|
120
|
+
`Only gitignored files can be copied to worktrees.`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Safety summary for logging/debugging
|
|
125
|
+
*/
|
|
126
|
+
export function getSafetyConstraints() {
|
|
127
|
+
return [
|
|
128
|
+
'Never auto-merge PRs (merge must be done manually)',
|
|
129
|
+
'Never delete branches/worktrees without explicit --yes flag or prompt confirmation',
|
|
130
|
+
'Never copy tracked files to worktrees (only gitignored files allowed)',
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=safety.js.map
|