claude-remote-cli 3.0.3 → 3.0.4
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/dist/bin/claude-remote-cli.js +0 -3
- package/dist/frontend/assets/index-Bw4iKQrv.css +32 -0
- package/dist/frontend/assets/index-C9kPfx3H.js +47 -0
- package/dist/frontend/index.html +2 -2
- package/dist/server/config.js +22 -0
- package/dist/server/git.js +193 -1
- package/dist/server/index.js +51 -292
- package/dist/server/push.js +3 -54
- package/dist/server/sessions.js +265 -180
- package/dist/server/types.js +7 -13
- package/dist/server/workspaces.js +448 -0
- package/dist/server/ws.js +31 -92
- package/dist/test/pr-state.test.js +164 -0
- package/dist/test/pull-requests.test.js +3 -3
- package/dist/test/sessions.test.js +7 -23
- package/package.json +1 -2
- package/dist/frontend/assets/index-BgOmCV-k.css +0 -32
- package/dist/frontend/assets/index-CKQHbnTN.js +0 -47
- package/dist/server/pty-handler.js +0 -214
- package/dist/server/sdk-handler.js +0 -536
package/dist/frontend/index.html
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
12
12
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
13
13
|
<meta name="theme-color" content="#1a1a1a" />
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-C9kPfx3H.js"></script>
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bw4iKQrv.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="app"></div>
|
package/dist/server/config.js
CHANGED
|
@@ -13,6 +13,7 @@ export const DEFAULTS = {
|
|
|
13
13
|
defaultYolo: false,
|
|
14
14
|
launchInTmux: false,
|
|
15
15
|
defaultNotifications: true,
|
|
16
|
+
workspaces: [],
|
|
16
17
|
};
|
|
17
18
|
export function loadConfig(configPath) {
|
|
18
19
|
if (!fs.existsSync(configPath)) {
|
|
@@ -61,3 +62,24 @@ export function deleteMeta(configPath, worktreePath) {
|
|
|
61
62
|
// File may not exist; ignore
|
|
62
63
|
}
|
|
63
64
|
}
|
|
65
|
+
export function getWorkspaceSettings(config, workspacePath) {
|
|
66
|
+
const globalDefaults = {
|
|
67
|
+
defaultAgent: config.defaultAgent,
|
|
68
|
+
defaultContinue: config.defaultContinue,
|
|
69
|
+
defaultYolo: config.defaultYolo,
|
|
70
|
+
launchInTmux: config.launchInTmux,
|
|
71
|
+
claudeArgs: config.claudeArgs,
|
|
72
|
+
};
|
|
73
|
+
const perWorkspace = config.workspaceSettings?.[workspacePath] || {};
|
|
74
|
+
// Per-workspace settings override global — only for defined keys
|
|
75
|
+
return { ...globalDefaults, ...perWorkspace };
|
|
76
|
+
}
|
|
77
|
+
export function setWorkspaceSettings(configPath, config, workspacePath, settings) {
|
|
78
|
+
if (!config.workspaceSettings)
|
|
79
|
+
config.workspaceSettings = {};
|
|
80
|
+
config.workspaceSettings[workspacePath] = {
|
|
81
|
+
...config.workspaceSettings[workspacePath],
|
|
82
|
+
...settings,
|
|
83
|
+
};
|
|
84
|
+
saveConfig(configPath, config);
|
|
85
|
+
}
|
package/dist/server/git.js
CHANGED
|
@@ -27,4 +27,196 @@ async function listBranches(repoPath, options = {}) {
|
|
|
27
27
|
return [];
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
async function getCurrentBranch(repoPath, options = {}) {
|
|
31
|
+
const run = options.exec || execFileAsync;
|
|
32
|
+
try {
|
|
33
|
+
const { stdout } = await run('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: repoPath });
|
|
34
|
+
return stdout.trim() || null;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function getActivityFeed(repoPath, options = {}) {
|
|
41
|
+
const run = options.exec || execFileAsync;
|
|
42
|
+
try {
|
|
43
|
+
const { stdout } = await run('git', [
|
|
44
|
+
'log',
|
|
45
|
+
'--all',
|
|
46
|
+
'--since=24h',
|
|
47
|
+
'--oneline',
|
|
48
|
+
'--max-count=50',
|
|
49
|
+
'--format=%H|%h|%s|%an|%ar|%D',
|
|
50
|
+
], { cwd: repoPath, timeout: 5000 });
|
|
51
|
+
const lines = stdout.split('\n').filter((line) => line.trim());
|
|
52
|
+
const entries = [];
|
|
53
|
+
for (const line of lines) {
|
|
54
|
+
try {
|
|
55
|
+
// Split into exactly 6 parts by the first 5 pipe characters
|
|
56
|
+
const parts = [];
|
|
57
|
+
let remaining = line;
|
|
58
|
+
for (let i = 0; i < 5; i++) {
|
|
59
|
+
const idx = remaining.indexOf('|');
|
|
60
|
+
if (idx === -1)
|
|
61
|
+
break;
|
|
62
|
+
parts.push(remaining.slice(0, idx));
|
|
63
|
+
remaining = remaining.slice(idx + 1);
|
|
64
|
+
}
|
|
65
|
+
parts.push(remaining);
|
|
66
|
+
if (parts.length < 5)
|
|
67
|
+
continue;
|
|
68
|
+
const hash = parts[0] ?? '';
|
|
69
|
+
const shortHash = parts[1] ?? '';
|
|
70
|
+
const message = parts[2] ?? '';
|
|
71
|
+
const author = parts[3] ?? '';
|
|
72
|
+
const timeAgo = parts[4] ?? '';
|
|
73
|
+
const decorations = parts[5] ?? '';
|
|
74
|
+
if (!hash || !shortHash)
|
|
75
|
+
continue;
|
|
76
|
+
const branches = decorations
|
|
77
|
+
.split(',')
|
|
78
|
+
.map((d) => d.trim())
|
|
79
|
+
.filter((d) => d && !d.startsWith('tag:') && d !== 'HEAD')
|
|
80
|
+
.map((d) => d.replace(/^HEAD -> /, '').replace(/^origin\//, ''));
|
|
81
|
+
entries.push({
|
|
82
|
+
hash: hash.trim(),
|
|
83
|
+
shortHash: shortHash.trim(),
|
|
84
|
+
message: message.trim(),
|
|
85
|
+
author: author.trim(),
|
|
86
|
+
timeAgo: timeAgo.trim(),
|
|
87
|
+
branches: [...new Set(branches)],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Skip malformed lines
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return entries;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function getCiStatus(repoPath, branch, options = {}) {
|
|
102
|
+
const run = options.exec || execFileAsync;
|
|
103
|
+
let stdout;
|
|
104
|
+
let stderr;
|
|
105
|
+
try {
|
|
106
|
+
({ stdout, stderr } = await run('gh', ['pr', 'checks', branch, '--json', 'name,state,conclusion'], { cwd: repoPath, timeout: 5000 }));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
if (err && typeof err === 'object') {
|
|
110
|
+
const errObj = err;
|
|
111
|
+
const errorText = errObj.stderr ?? errObj.message ?? '';
|
|
112
|
+
// gh not installed
|
|
113
|
+
if (errObj.code === 'ENOENT')
|
|
114
|
+
return null;
|
|
115
|
+
// Not authenticated
|
|
116
|
+
if (typeof errorText === 'string' &&
|
|
117
|
+
(errorText.includes('not logged into') || errorText.includes('authentication'))) {
|
|
118
|
+
return { total: 0, passing: 0, failing: 0, pending: 0, authError: true };
|
|
119
|
+
}
|
|
120
|
+
// No PR for branch
|
|
121
|
+
if (typeof errorText === 'string' &&
|
|
122
|
+
(errorText.includes('no pull requests found') || errorText.includes('Could not find'))) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
// gh may exit 0 but write errors or auth prompts to stderr
|
|
129
|
+
if (stderr && (stderr.includes('not logged into') || stderr.includes('authentication'))) {
|
|
130
|
+
return { total: 0, passing: 0, failing: 0, pending: 0, authError: true };
|
|
131
|
+
}
|
|
132
|
+
if (!stdout.trim())
|
|
133
|
+
return null;
|
|
134
|
+
try {
|
|
135
|
+
const checks = JSON.parse(stdout);
|
|
136
|
+
let passing = 0;
|
|
137
|
+
let failing = 0;
|
|
138
|
+
let pending = 0;
|
|
139
|
+
for (const check of checks) {
|
|
140
|
+
const conclusion = (check.conclusion ?? '').toUpperCase();
|
|
141
|
+
const state = (check.state ?? '').toUpperCase();
|
|
142
|
+
if (conclusion === 'SUCCESS' || conclusion === 'SKIPPED' || conclusion === 'NEUTRAL') {
|
|
143
|
+
passing++;
|
|
144
|
+
}
|
|
145
|
+
else if (conclusion === 'FAILURE' || conclusion === 'CANCELLED' || conclusion === 'TIMED_OUT') {
|
|
146
|
+
failing++;
|
|
147
|
+
}
|
|
148
|
+
else if (state === 'IN_PROGRESS' || state === 'QUEUED' || state === 'PENDING' || conclusion === '') {
|
|
149
|
+
pending++;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// Unknown conclusion — treat as pending rather than silently ignoring
|
|
153
|
+
pending++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { total: checks.length, passing, failing, pending };
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async function getPrForBranch(repoPath, branch, options = {}) {
|
|
163
|
+
const run = options.exec || execFileAsync;
|
|
164
|
+
let stdout;
|
|
165
|
+
try {
|
|
166
|
+
({ stdout } = await run('gh', [
|
|
167
|
+
'pr',
|
|
168
|
+
'view',
|
|
169
|
+
branch,
|
|
170
|
+
'--json',
|
|
171
|
+
'number,title,url,state,headRefName,baseRefName,reviewDecision,isDraft',
|
|
172
|
+
], { cwd: repoPath, timeout: 5000 }));
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
if (!stdout.trim())
|
|
178
|
+
return null;
|
|
179
|
+
try {
|
|
180
|
+
const data = JSON.parse(stdout);
|
|
181
|
+
return {
|
|
182
|
+
number: data.number,
|
|
183
|
+
title: data.title,
|
|
184
|
+
url: data.url,
|
|
185
|
+
state: data.state,
|
|
186
|
+
headRefName: data.headRefName,
|
|
187
|
+
baseRefName: data.baseRefName,
|
|
188
|
+
isDraft: data.isDraft,
|
|
189
|
+
reviewDecision: data.reviewDecision ?? null,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function switchBranch(repoPath, branch, options = {}) {
|
|
197
|
+
const run = options.exec || execFileAsync;
|
|
198
|
+
try {
|
|
199
|
+
await run('git', ['checkout', branch], { cwd: repoPath, timeout: 5000 });
|
|
200
|
+
return { success: true };
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
if (err && typeof err === 'object') {
|
|
204
|
+
const errObj = err;
|
|
205
|
+
const errorText = errObj.stderr ?? errObj.message ?? 'Unknown error';
|
|
206
|
+
return { success: false, error: errorText.trim() };
|
|
207
|
+
}
|
|
208
|
+
return { success: false, error: 'Unknown error' };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async function getCommitsAhead(repoPath, branch, baseBranch, options = {}) {
|
|
212
|
+
const run = options.exec || execFileAsync;
|
|
213
|
+
try {
|
|
214
|
+
const { stdout } = await run('git', ['rev-list', '--count', `${baseBranch}..${branch}`], { cwd: repoPath, timeout: 5000 });
|
|
215
|
+
const count = parseInt(stdout.trim(), 10);
|
|
216
|
+
return Number.isFinite(count) ? count : 0;
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
export { listBranches, normalizeBranchNames, getActivityFeed, getCiStatus, getPrForBranch, switchBranch, getCommitsAhead, getCurrentBranch, };
|