@meistrari/remy-cli 1.13.0 → 1.14.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/README.md +18 -7
- package/dist/remy.js +1506 -397
- package/package.json +1 -1
package/dist/remy.js
CHANGED
|
@@ -18,8 +18,8 @@ var __export = (target, all) => {
|
|
|
18
18
|
// src/commands.ts
|
|
19
19
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
20
20
|
import { spawn } from "child_process";
|
|
21
|
-
import { mkdir as mkdir5, readFile as readFile5, readdir as
|
|
22
|
-
import { tmpdir } from "os";
|
|
21
|
+
import { mkdir as mkdir5, readFile as readFile5, readdir as readdir3, rename as rename4, unlink as unlink3, writeFile as writeFile4 } from "fs/promises";
|
|
22
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
23
23
|
import { dirname as dirname6, join as join6 } from "path";
|
|
24
24
|
|
|
25
25
|
// ../../node_modules/.bun/@meistrari+auth-cli@1.6.1+aff0271b2b55d60c/node_modules/@meistrari/auth-cli/dist/index.mjs
|
|
@@ -31673,6 +31673,31 @@ var repositorySuggestionsInputSchema = exports_external2.strictObject({
|
|
|
31673
31673
|
installationId: exports_external2.string().min(1),
|
|
31674
31674
|
prompt: exports_external2.string().trim().min(1).max(20000)
|
|
31675
31675
|
});
|
|
31676
|
+
var branchSuggestionsInputSchema = exports_external2.strictObject({
|
|
31677
|
+
installationId: exports_external2.string().min(1),
|
|
31678
|
+
prompt: exports_external2.string().trim().min(1).max(20000),
|
|
31679
|
+
repositoryIds: exports_external2.array(exports_external2.string().min(1)).min(1).max(20)
|
|
31680
|
+
});
|
|
31681
|
+
var branchSuggestionsResponseSchema = exports_external2.strictObject({
|
|
31682
|
+
repositories: exports_external2.array(exports_external2.discriminatedUnion("suggestion_kind", [
|
|
31683
|
+
exports_external2.strictObject({
|
|
31684
|
+
repository_id: exports_external2.string().min(1),
|
|
31685
|
+
suggestion_kind: exports_external2.literal("continue_pull_request"),
|
|
31686
|
+
branch_name: exports_external2.string().min(1),
|
|
31687
|
+
pull_request: exports_external2.strictObject({
|
|
31688
|
+
number: exports_external2.number().int().positive(),
|
|
31689
|
+
title: exports_external2.string(),
|
|
31690
|
+
url: exports_external2.url()
|
|
31691
|
+
})
|
|
31692
|
+
}),
|
|
31693
|
+
exports_external2.strictObject({
|
|
31694
|
+
repository_id: exports_external2.string().min(1),
|
|
31695
|
+
suggestion_kind: exports_external2.literal("create_session_branch"),
|
|
31696
|
+
branch_name: exports_external2.null(),
|
|
31697
|
+
pull_request: exports_external2.null()
|
|
31698
|
+
})
|
|
31699
|
+
]))
|
|
31700
|
+
});
|
|
31676
31701
|
var repositorySuggestionsResponseSchema = exports_external2.discriminatedUnion("selection_kind", [
|
|
31677
31702
|
exports_external2.object({
|
|
31678
31703
|
selection_kind: exports_external2.literal("repositories"),
|
|
@@ -31757,19 +31782,95 @@ async function suggestRemoteRepositories({
|
|
|
31757
31782
|
confidence: parsed.data.confidence
|
|
31758
31783
|
};
|
|
31759
31784
|
}
|
|
31785
|
+
async function suggestRemoteBranches({
|
|
31786
|
+
client,
|
|
31787
|
+
input
|
|
31788
|
+
}) {
|
|
31789
|
+
const parsedInput = branchSuggestionsInputSchema.parse(input);
|
|
31790
|
+
const response = await client.request("/v1/repositories/branch-suggestions", {
|
|
31791
|
+
method: "POST",
|
|
31792
|
+
headers: { "content-type": "application/json" },
|
|
31793
|
+
body: JSON.stringify({
|
|
31794
|
+
github_installation_id: parsedInput.installationId,
|
|
31795
|
+
prompt: parsedInput.prompt,
|
|
31796
|
+
repository_ids: parsedInput.repositoryIds
|
|
31797
|
+
})
|
|
31798
|
+
});
|
|
31799
|
+
let body;
|
|
31800
|
+
try {
|
|
31801
|
+
body = await response.json();
|
|
31802
|
+
} catch (error93) {
|
|
31803
|
+
throw new CodingAgentProtocolError("Branch suggestions response was not valid JSON.", { cause: error93 });
|
|
31804
|
+
}
|
|
31805
|
+
const parsed = branchSuggestionsResponseSchema.safeParse(body);
|
|
31806
|
+
if (!parsed.success)
|
|
31807
|
+
throw new CodingAgentProtocolError("Branch suggestions response did not match the public API contract.", { cause: parsed.error });
|
|
31808
|
+
return parsed.data.repositories.map((repository) => repository.suggestion_kind === "continue_pull_request" ? {
|
|
31809
|
+
suggestionKind: "continuePullRequest",
|
|
31810
|
+
repositoryId: repository.repository_id,
|
|
31811
|
+
branchName: repository.branch_name,
|
|
31812
|
+
pullRequest: repository.pull_request
|
|
31813
|
+
} : {
|
|
31814
|
+
suggestionKind: "createSessionBranch",
|
|
31815
|
+
repositoryId: repository.repository_id,
|
|
31816
|
+
branchName: null,
|
|
31817
|
+
pullRequest: null
|
|
31818
|
+
});
|
|
31819
|
+
}
|
|
31820
|
+
|
|
31821
|
+
// ../../packages/coding-agent-client/src/users.ts
|
|
31822
|
+
var remoteUserPageDtoSchema = exports_external2.object({
|
|
31823
|
+
data: exports_external2.array(exports_external2.object({
|
|
31824
|
+
id: exports_external2.string(),
|
|
31825
|
+
name: exports_external2.string(),
|
|
31826
|
+
email: exports_external2.email()
|
|
31827
|
+
})),
|
|
31828
|
+
has_more: exports_external2.boolean(),
|
|
31829
|
+
next_cursor: exports_external2.string().nullable()
|
|
31830
|
+
});
|
|
31831
|
+
async function listRemoteUsers({
|
|
31832
|
+
client,
|
|
31833
|
+
limit,
|
|
31834
|
+
after,
|
|
31835
|
+
signal
|
|
31836
|
+
}) {
|
|
31837
|
+
const searchParams = new URLSearchParams;
|
|
31838
|
+
if (limit !== undefined)
|
|
31839
|
+
searchParams.set("limit", String(limit));
|
|
31840
|
+
if (after)
|
|
31841
|
+
searchParams.set("after", after);
|
|
31842
|
+
const suffix = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
|
|
31843
|
+
const response = await client.request(`/users${suffix}`, { signal });
|
|
31844
|
+
let body;
|
|
31845
|
+
try {
|
|
31846
|
+
body = await response.json();
|
|
31847
|
+
} catch (error93) {
|
|
31848
|
+
throw new CodingAgentProtocolError("User directory response was not valid JSON.", { cause: error93 });
|
|
31849
|
+
}
|
|
31850
|
+
const parsed = remoteUserPageDtoSchema.safeParse(body);
|
|
31851
|
+
if (!parsed.success)
|
|
31852
|
+
throw new CodingAgentProtocolError("User directory response did not match the API contract.", { cause: parsed.error });
|
|
31853
|
+
return {
|
|
31854
|
+
data: parsed.data.data.map((user) => ({ id: user.id, name: user.name, email: user.email })),
|
|
31855
|
+
hasMore: parsed.data.has_more,
|
|
31856
|
+
nextCursor: parsed.data.next_cursor
|
|
31857
|
+
};
|
|
31858
|
+
}
|
|
31760
31859
|
|
|
31761
31860
|
// ../../packages/coding-agent-client/src/files.ts
|
|
31861
|
+
import { Buffer } from "buffer";
|
|
31762
31862
|
import { createHash } from "crypto";
|
|
31763
31863
|
import { open } from "fs/promises";
|
|
31764
31864
|
import { basename } from "path";
|
|
31765
|
-
var
|
|
31865
|
+
var fileUploadMaxBytes = 20 * 1024 * 1024;
|
|
31866
|
+
var fileUploadMaxSizeLabel = `${fileUploadMaxBytes / (1024 * 1024)} MiB`;
|
|
31766
31867
|
var defaultPollIntervalMs = 250;
|
|
31767
31868
|
var sha256Schema = exports_external2.string().regex(/^[0-9a-f]{64}$/);
|
|
31768
31869
|
var fileIdentitySchema = exports_external2.strictObject({
|
|
31769
31870
|
id: exports_external2.string(),
|
|
31770
31871
|
filename: exports_external2.string().min(1).max(255),
|
|
31771
31872
|
media_type: exports_external2.string().min(1).max(255),
|
|
31772
|
-
byte_size: exports_external2.number().int().min(0).max(
|
|
31873
|
+
byte_size: exports_external2.number().int().min(0).max(fileUploadMaxBytes),
|
|
31773
31874
|
sha256: sha256Schema
|
|
31774
31875
|
});
|
|
31775
31876
|
var uploadSignedUrlSchema = exports_external2.strictObject({
|
|
@@ -31820,9 +31921,37 @@ var fileResponseSchema = exports_external2.union([
|
|
|
31820
31921
|
var createFileRequestSchema = exports_external2.strictObject({
|
|
31821
31922
|
filename: exports_external2.string().min(1).max(255),
|
|
31822
31923
|
media_type: exports_external2.string().min(1).max(255),
|
|
31823
|
-
byte_size: exports_external2.number().int().min(0).max(
|
|
31924
|
+
byte_size: exports_external2.number().int().min(0).max(fileUploadMaxBytes),
|
|
31824
31925
|
sha256: sha256Schema
|
|
31825
31926
|
});
|
|
31927
|
+
async function readFileHandleWithLimit({
|
|
31928
|
+
fileHandle,
|
|
31929
|
+
initialByteSize,
|
|
31930
|
+
maxBytes
|
|
31931
|
+
}) {
|
|
31932
|
+
const maximumBufferBytes = maxBytes + 1;
|
|
31933
|
+
let buffer = Buffer.allocUnsafe(Math.min(maximumBufferBytes, Math.max(1, initialByteSize + 1)));
|
|
31934
|
+
let byteLength = 0;
|
|
31935
|
+
while (true) {
|
|
31936
|
+
if (byteLength === buffer.byteLength) {
|
|
31937
|
+
if (byteLength > maxBytes)
|
|
31938
|
+
return { status: "limit_exceeded" };
|
|
31939
|
+
const nextBuffer = Buffer.allocUnsafe(Math.min(maximumBufferBytes, Math.max(buffer.byteLength * 2, byteLength + 1)));
|
|
31940
|
+
buffer.copy(nextBuffer, 0, 0, byteLength);
|
|
31941
|
+
buffer = nextBuffer;
|
|
31942
|
+
}
|
|
31943
|
+
const { bytesRead } = await fileHandle.read(buffer, byteLength, buffer.byteLength - byteLength, null);
|
|
31944
|
+
if (bytesRead === 0) {
|
|
31945
|
+
return {
|
|
31946
|
+
status: "complete",
|
|
31947
|
+
bytes: Buffer.from(buffer.subarray(0, byteLength))
|
|
31948
|
+
};
|
|
31949
|
+
}
|
|
31950
|
+
byteLength += bytesRead;
|
|
31951
|
+
if (byteLength > maxBytes)
|
|
31952
|
+
return { status: "limit_exceeded" };
|
|
31953
|
+
}
|
|
31954
|
+
}
|
|
31826
31955
|
async function reserveAndUploadFile({
|
|
31827
31956
|
client,
|
|
31828
31957
|
filePath,
|
|
@@ -31907,13 +32036,18 @@ async function readLocalUploadFile({ filePath, mediaType }) {
|
|
|
31907
32036
|
throw new LocalFileSelectionError(`Local attachment is not a regular file: ${filePath}`);
|
|
31908
32037
|
if (fileStat.size === 0)
|
|
31909
32038
|
throw new LocalFileSelectionError(`Local attachment is empty: ${filePath}`);
|
|
31910
|
-
if (fileStat.size >
|
|
31911
|
-
throw
|
|
31912
|
-
const
|
|
32039
|
+
if (fileStat.size > fileUploadMaxBytes)
|
|
32040
|
+
throw oversizedLocalFileError(filePath);
|
|
32041
|
+
const readResult = await readFileHandleWithLimit({
|
|
32042
|
+
fileHandle,
|
|
32043
|
+
initialByteSize: fileStat.size,
|
|
32044
|
+
maxBytes: fileUploadMaxBytes
|
|
32045
|
+
});
|
|
32046
|
+
if (readResult.status === "limit_exceeded")
|
|
32047
|
+
throw oversizedLocalFileError(filePath);
|
|
32048
|
+
const { bytes } = readResult;
|
|
31913
32049
|
if (bytes.byteLength === 0)
|
|
31914
32050
|
throw new LocalFileSelectionError(`Local attachment is empty: ${filePath}`);
|
|
31915
|
-
if (bytes.byteLength > maxFileBytes)
|
|
31916
|
-
throw new LocalFileSelectionError(`Local attachment exceeds ${maxFileBytes} bytes: ${filePath}`);
|
|
31917
32051
|
return {
|
|
31918
32052
|
filename: basename(filePath),
|
|
31919
32053
|
mediaType,
|
|
@@ -31925,6 +32059,9 @@ async function readLocalUploadFile({ filePath, mediaType }) {
|
|
|
31925
32059
|
await fileHandle.close();
|
|
31926
32060
|
}
|
|
31927
32061
|
}
|
|
32062
|
+
function oversizedLocalFileError(filePath) {
|
|
32063
|
+
return new LocalFileSelectionError(`Local attachment ${JSON.stringify(filePath)} is larger than ${fileUploadMaxSizeLabel}. Choose a file no larger than ${fileUploadMaxSizeLabel}.`);
|
|
32064
|
+
}
|
|
31928
32065
|
async function parseJson(response, failureMessage) {
|
|
31929
32066
|
try {
|
|
31930
32067
|
return await response.json();
|
|
@@ -32675,11 +32812,17 @@ var sessionTelaPageArtifactEventSchema = exports_external2.strictObject({
|
|
|
32675
32812
|
published_at: exports_external2.iso.datetime()
|
|
32676
32813
|
});
|
|
32677
32814
|
var sessionArtifactEventSchema = exports_external2.discriminatedUnion("kind", [
|
|
32678
|
-
sessionFileArtifactEventSchema
|
|
32815
|
+
sessionFileArtifactEventSchema.extend({
|
|
32816
|
+
download_url: exports_external2.string().optional(),
|
|
32817
|
+
preview_url: exports_external2.url().optional()
|
|
32818
|
+
}),
|
|
32679
32819
|
sessionTelaPageArtifactEventSchema
|
|
32680
32820
|
]);
|
|
32681
32821
|
var sessionArtifactSchema = exports_external2.discriminatedUnion("kind", [
|
|
32682
|
-
sessionFileArtifactEventSchema.extend({
|
|
32822
|
+
sessionFileArtifactEventSchema.extend({
|
|
32823
|
+
download_url: exports_external2.string(),
|
|
32824
|
+
preview_url: exports_external2.url().optional()
|
|
32825
|
+
}),
|
|
32683
32826
|
sessionTelaPageArtifactEventSchema.extend({ preview_url: exports_external2.string() })
|
|
32684
32827
|
]);
|
|
32685
32828
|
var sessionMessageSchema = exports_external2.strictObject({
|
|
@@ -32721,6 +32864,10 @@ var sessionConnectionSchema = exports_external2.discriminatedUnion("status", [
|
|
|
32721
32864
|
var createCodexSessionInputSchema = exports_external2.strictObject({
|
|
32722
32865
|
installationId: exports_external2.string().min(1),
|
|
32723
32866
|
repositoryIds: exports_external2.array(exports_external2.string().min(1)),
|
|
32867
|
+
repositoryBranchOverrides: exports_external2.array(exports_external2.strictObject({
|
|
32868
|
+
repositoryId: exports_external2.string().min(1),
|
|
32869
|
+
branchName: exports_external2.string().min(1)
|
|
32870
|
+
})).optional(),
|
|
32724
32871
|
prompt: exports_external2.string().refine((value) => value.trim().length > 0),
|
|
32725
32872
|
fileIds: exports_external2.array(exports_external2.string().min(1)),
|
|
32726
32873
|
model: codexModelIdSchema.optional(),
|
|
@@ -32762,6 +32909,7 @@ var withdrawSessionMessageResponseSchema = exports_external2.strictObject({
|
|
|
32762
32909
|
});
|
|
32763
32910
|
var sessionDetailResponseSchema = exports_external2.object({
|
|
32764
32911
|
id: exports_external2.string(),
|
|
32912
|
+
title: exports_external2.string().nullable(),
|
|
32765
32913
|
status: sessionStatusSchema,
|
|
32766
32914
|
session_number: exports_external2.number().int().positive(),
|
|
32767
32915
|
agent_status: exports_external2.string().min(1),
|
|
@@ -32852,7 +33000,13 @@ async function createCodexSession({
|
|
|
32852
33000
|
...parsedInput.reasoningEffort ? { reasoning_effort: parsedInput.reasoningEffort } : {}
|
|
32853
33001
|
}
|
|
32854
33002
|
} : {},
|
|
32855
|
-
repositories: parsedInput.repositoryIds.map((repositoryId) =>
|
|
33003
|
+
repositories: parsedInput.repositoryIds.map((repositoryId) => {
|
|
33004
|
+
const override = parsedInput.repositoryBranchOverrides?.find((candidate) => candidate.repositoryId === repositoryId);
|
|
33005
|
+
return {
|
|
33006
|
+
repository_id: repositoryId,
|
|
33007
|
+
...override ? { branch_name: override.branchName } : {}
|
|
33008
|
+
};
|
|
33009
|
+
}),
|
|
32856
33010
|
message: {
|
|
32857
33011
|
text: parsedInput.prompt,
|
|
32858
33012
|
file_ids: parsedInput.fileIds
|
|
@@ -32934,6 +33088,8 @@ async function listSessions({
|
|
|
32934
33088
|
client,
|
|
32935
33089
|
limit,
|
|
32936
33090
|
after,
|
|
33091
|
+
statuses,
|
|
33092
|
+
creatorUserIds,
|
|
32937
33093
|
signal
|
|
32938
33094
|
}) {
|
|
32939
33095
|
const searchParams = new URLSearchParams;
|
|
@@ -32941,6 +33097,10 @@ async function listSessions({
|
|
|
32941
33097
|
searchParams.set("limit", String(limit));
|
|
32942
33098
|
if (after)
|
|
32943
33099
|
searchParams.set("after", after);
|
|
33100
|
+
for (const status of statuses ?? [])
|
|
33101
|
+
searchParams.append("status", status);
|
|
33102
|
+
for (const creatorUserId of creatorUserIds ?? [])
|
|
33103
|
+
searchParams.append("creator_user_id", creatorUserId);
|
|
32944
33104
|
const suffix = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
|
|
32945
33105
|
const response = await client.request(`/v1/sessions${suffix}`, { signal });
|
|
32946
33106
|
const parsed = sessionListResponseSchema.safeParse(await parseJson2(response, "Session list response was not valid JSON."));
|
|
@@ -33027,7 +33187,8 @@ async function listSessionEvents({
|
|
|
33027
33187
|
client,
|
|
33028
33188
|
sessionId,
|
|
33029
33189
|
limit,
|
|
33030
|
-
after
|
|
33190
|
+
after,
|
|
33191
|
+
signal
|
|
33031
33192
|
}) {
|
|
33032
33193
|
const searchParams = new URLSearchParams;
|
|
33033
33194
|
if (limit !== undefined)
|
|
@@ -33035,7 +33196,7 @@ async function listSessionEvents({
|
|
|
33035
33196
|
if (after)
|
|
33036
33197
|
searchParams.set("after", after);
|
|
33037
33198
|
const suffix = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
|
|
33038
|
-
const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/events${suffix}
|
|
33199
|
+
const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/events${suffix}`, { signal });
|
|
33039
33200
|
const parsed = sessionEventsResponseSchema.safeParse(await parseJson3(response, "Session events response was not valid JSON."));
|
|
33040
33201
|
if (!parsed.success)
|
|
33041
33202
|
throw new CodingAgentProtocolError("Session events response did not match the public API contract.", { cause: parsed.error });
|
|
@@ -33971,7 +34132,9 @@ function createSessionViewState({ detail, activeMessageId }) {
|
|
|
33971
34132
|
kind: "session-view",
|
|
33972
34133
|
sessionId: detail.id,
|
|
33973
34134
|
...detail.sessionNumber === undefined ? {} : { sessionNumber: detail.sessionNumber },
|
|
34135
|
+
title: detail.title ?? null,
|
|
33974
34136
|
aggregateStatus: detail.status,
|
|
34137
|
+
...detail.agentStatus === undefined ? {} : { agentStatus: detail.agentStatus },
|
|
33975
34138
|
connectionStatus: "connected",
|
|
33976
34139
|
pullRequest: toSessionPullRequest(detail),
|
|
33977
34140
|
...activeMessageId !== undefined ? { activeMessageId } : {},
|
|
@@ -34004,7 +34167,9 @@ function updateSessionDetail({ state, detail }) {
|
|
|
34004
34167
|
...state,
|
|
34005
34168
|
sessionId: detail.id,
|
|
34006
34169
|
...detail.sessionNumber === undefined ? {} : { sessionNumber: detail.sessionNumber },
|
|
34170
|
+
..."title" in detail ? { title: detail.title ?? null } : {},
|
|
34007
34171
|
aggregateStatus: detail.status,
|
|
34172
|
+
...detail.agentStatus === undefined ? {} : { agentStatus: detail.agentStatus },
|
|
34008
34173
|
pullRequest: toSessionPullRequest(detail),
|
|
34009
34174
|
connectionPreviews: toConnectionPreviews(detail)
|
|
34010
34175
|
};
|
|
@@ -34076,7 +34241,9 @@ function projectRetainedEvent({
|
|
|
34076
34241
|
artifactId: artifact.artifact_id,
|
|
34077
34242
|
occurredAt,
|
|
34078
34243
|
title: artifact.filename,
|
|
34079
|
-
mediaType: artifact.media_type
|
|
34244
|
+
mediaType: artifact.media_type,
|
|
34245
|
+
...artifact.download_url === undefined ? {} : { downloadUrl: artifact.download_url },
|
|
34246
|
+
...artifact.preview_url === undefined ? {} : { previewUrl: artifact.preview_url }
|
|
34080
34247
|
} : {
|
|
34081
34248
|
kind: "artifact",
|
|
34082
34249
|
artifactKind: artifact.kind,
|
|
@@ -35061,12 +35228,12 @@ function createRemoteSessionController(dependencies) {
|
|
|
35061
35228
|
detail: input.detail,
|
|
35062
35229
|
activeMessageId: dependencies.activeMessageId ?? cache?.activeMessageId
|
|
35063
35230
|
});
|
|
35064
|
-
resolveReady();
|
|
35065
|
-
publishState();
|
|
35066
35231
|
if (input.mode === "cold-resume") {
|
|
35067
35232
|
const hydrationGeneration = beginReasoningAttempt();
|
|
35068
35233
|
await hydrateRetainedHistoryFromBeginning(hydrationGeneration);
|
|
35069
35234
|
}
|
|
35235
|
+
resolveReady();
|
|
35236
|
+
publishState();
|
|
35070
35237
|
await streamWithControllerReconnects();
|
|
35071
35238
|
}
|
|
35072
35239
|
function stop() {
|
|
@@ -35102,7 +35269,8 @@ function createRemoteSessionController(dependencies) {
|
|
|
35102
35269
|
async function recordActiveMessage(sessionMessageId) {
|
|
35103
35270
|
state = {
|
|
35104
35271
|
...getState(),
|
|
35105
|
-
activeMessageId: sessionMessageId
|
|
35272
|
+
activeMessageId: sessionMessageId,
|
|
35273
|
+
agentStatus: undefined
|
|
35106
35274
|
};
|
|
35107
35275
|
await writeCache();
|
|
35108
35276
|
publishState();
|
|
@@ -35123,7 +35291,8 @@ function createRemoteSessionController(dependencies) {
|
|
|
35123
35291
|
const page = await dependencies.listSessionEvents({
|
|
35124
35292
|
sessionId: dependencies.sessionId,
|
|
35125
35293
|
limit: sessionHistoryPageSize,
|
|
35126
|
-
after
|
|
35294
|
+
after,
|
|
35295
|
+
signal: abortController.signal
|
|
35127
35296
|
});
|
|
35128
35297
|
for (const item of page.data) {
|
|
35129
35298
|
await reduceFrameAndPersist({
|
|
@@ -35377,7 +35546,10 @@ async function sleepMs(ms, signal) {
|
|
|
35377
35546
|
}
|
|
35378
35547
|
|
|
35379
35548
|
// src/tui/dashboard.ts
|
|
35380
|
-
import { bg, BoxRenderable, bold as bold2, CliRenderEvents as CliRenderEvents2, fg as fg3, ScrollBoxRenderable, stringToStyledText as stringToStyledText2, TextRenderable } from "@opentui/core";
|
|
35549
|
+
import { bg, BoxRenderable as BoxRenderable2, bold as bold2, CliRenderEvents as CliRenderEvents2, fg as fg3, ScrollBoxRenderable, stringToStyledText as stringToStyledText2, TextRenderable as TextRenderable2 } from "@opentui/core";
|
|
35550
|
+
|
|
35551
|
+
// src/tui/composer.ts
|
|
35552
|
+
import { BoxRenderable, TextRenderable, TextareaRenderable } from "@opentui/core";
|
|
35381
35553
|
|
|
35382
35554
|
// src/tui/composer-divider.ts
|
|
35383
35555
|
import { dim as dim2, fg as fg2, StyledText as StyledText2 } from "@opentui/core";
|
|
@@ -35572,6 +35744,98 @@ function renderComposerDivider({ width, tag, leadLabel }) {
|
|
|
35572
35744
|
]);
|
|
35573
35745
|
}
|
|
35574
35746
|
|
|
35747
|
+
// src/tui/composer-height.ts
|
|
35748
|
+
var composerMaxRows = 6;
|
|
35749
|
+
function resizeComposer(composer) {
|
|
35750
|
+
composer.height = Math.min(Math.max(composer.editorView.getTotalVirtualLineCount(), 1), composerMaxRows);
|
|
35751
|
+
}
|
|
35752
|
+
|
|
35753
|
+
// src/tui/composer.ts
|
|
35754
|
+
var defaultPlaceholder = "Message Remy\u2026 Tab paths attach \xB7 Enter/Shift+Enter send \xB7 Option+Enter newline";
|
|
35755
|
+
function createComposer({
|
|
35756
|
+
renderer,
|
|
35757
|
+
parent,
|
|
35758
|
+
tag,
|
|
35759
|
+
topDividerLabel,
|
|
35760
|
+
placeholder = defaultPlaceholder,
|
|
35761
|
+
screenPaddingX = 1,
|
|
35762
|
+
onSubmit,
|
|
35763
|
+
onContentChange
|
|
35764
|
+
}) {
|
|
35765
|
+
const root = new BoxRenderable(renderer, { width: "100%", flexDirection: "column", flexShrink: 0 });
|
|
35766
|
+
const dividerTop = new TextRenderable(renderer, { content: "", flexShrink: 0 });
|
|
35767
|
+
const textarea = new TextareaRenderable(renderer, {
|
|
35768
|
+
width: "100%",
|
|
35769
|
+
height: 1,
|
|
35770
|
+
flexShrink: 0,
|
|
35771
|
+
wrapMode: "word",
|
|
35772
|
+
placeholder,
|
|
35773
|
+
keyBindings: [
|
|
35774
|
+
{ name: "return", action: "submit" },
|
|
35775
|
+
{ name: "return", shift: true, action: "submit" },
|
|
35776
|
+
{ name: "return", meta: true, action: "newline" },
|
|
35777
|
+
{ name: "j", ctrl: true, action: "newline" }
|
|
35778
|
+
],
|
|
35779
|
+
onSubmit
|
|
35780
|
+
});
|
|
35781
|
+
const dividerBottom = new TextRenderable(renderer, { content: "", flexShrink: 0 });
|
|
35782
|
+
let mounted = false;
|
|
35783
|
+
root.add(dividerTop);
|
|
35784
|
+
root.add(textarea);
|
|
35785
|
+
root.add(dividerBottom);
|
|
35786
|
+
textarea.onContentChange = () => {
|
|
35787
|
+
resizeComposer(textarea);
|
|
35788
|
+
onContentChange?.();
|
|
35789
|
+
};
|
|
35790
|
+
function render() {
|
|
35791
|
+
const dividerWidth = renderer.width - screenPaddingX * 2;
|
|
35792
|
+
dividerTop.content = renderComposerDivider({ width: dividerWidth, tag, leadLabel: topDividerLabel?.() });
|
|
35793
|
+
dividerBottom.content = renderComposerDivider({ width: dividerWidth });
|
|
35794
|
+
}
|
|
35795
|
+
return {
|
|
35796
|
+
textarea,
|
|
35797
|
+
mount() {
|
|
35798
|
+
if (mounted)
|
|
35799
|
+
return;
|
|
35800
|
+
mounted = true;
|
|
35801
|
+
parent.add(root);
|
|
35802
|
+
resizeComposer(textarea);
|
|
35803
|
+
render();
|
|
35804
|
+
},
|
|
35805
|
+
unmount() {
|
|
35806
|
+
if (!mounted)
|
|
35807
|
+
return;
|
|
35808
|
+
mounted = false;
|
|
35809
|
+
parent.remove(root);
|
|
35810
|
+
},
|
|
35811
|
+
render,
|
|
35812
|
+
focus() {
|
|
35813
|
+
textarea.focus();
|
|
35814
|
+
},
|
|
35815
|
+
destroy() {
|
|
35816
|
+
if (mounted)
|
|
35817
|
+
parent.remove(root);
|
|
35818
|
+
mounted = false;
|
|
35819
|
+
root.destroyRecursively();
|
|
35820
|
+
}
|
|
35821
|
+
};
|
|
35822
|
+
}
|
|
35823
|
+
|
|
35824
|
+
// src/tui/fuzzy-match.ts
|
|
35825
|
+
function fuzzyMatches(value, query) {
|
|
35826
|
+
const normalizedQuery = query.replaceAll(/\s/g, "").toLowerCase();
|
|
35827
|
+
if (!normalizedQuery)
|
|
35828
|
+
return true;
|
|
35829
|
+
let queryIndex = 0;
|
|
35830
|
+
for (const character of value.toLowerCase()) {
|
|
35831
|
+
if (character === normalizedQuery[queryIndex])
|
|
35832
|
+
queryIndex += 1;
|
|
35833
|
+
if (queryIndex === normalizedQuery.length)
|
|
35834
|
+
return true;
|
|
35835
|
+
}
|
|
35836
|
+
return false;
|
|
35837
|
+
}
|
|
35838
|
+
|
|
35575
35839
|
// src/tui/renderer.ts
|
|
35576
35840
|
import { CliRenderEvents, createClipboard, createCliRenderer, createHostClipboard, createRendererClipboardAdapter } from "@opentui/core";
|
|
35577
35841
|
var rendererDestroyed = new WeakMap;
|
|
@@ -35654,8 +35918,17 @@ async function renderRemyView({
|
|
|
35654
35918
|
|
|
35655
35919
|
// src/tui/dashboard.ts
|
|
35656
35920
|
var DASHBOARD_POLL_INTERVAL_MS = 1e4;
|
|
35921
|
+
var lifecycleStatusOptions = [
|
|
35922
|
+
{ value: "open", label: "Open" },
|
|
35923
|
+
{ value: "completed", label: "Completed" },
|
|
35924
|
+
{ value: "failed", label: "Failed" },
|
|
35925
|
+
{ value: "cancelled", label: "Cancelled" }
|
|
35926
|
+
];
|
|
35657
35927
|
async function createDashboardTui({
|
|
35658
35928
|
initialPage,
|
|
35929
|
+
initialFilters = { statuses: [], creators: [] },
|
|
35930
|
+
authors: initialAuthors = [],
|
|
35931
|
+
loadAuthors,
|
|
35659
35932
|
loadPage,
|
|
35660
35933
|
createRenderer = createDefaultRenderer
|
|
35661
35934
|
}) {
|
|
@@ -35664,6 +35937,7 @@ async function createDashboardTui({
|
|
|
35664
35937
|
let keyHandler;
|
|
35665
35938
|
let resizeHandler;
|
|
35666
35939
|
let refreshTimer;
|
|
35940
|
+
let contentScrollHandler;
|
|
35667
35941
|
let rendererDestroyed2 = false;
|
|
35668
35942
|
const loadPageAbortController = new AbortController;
|
|
35669
35943
|
try {
|
|
@@ -35675,28 +35949,61 @@ async function createDashboardTui({
|
|
|
35675
35949
|
loadPageAbortController.abort();
|
|
35676
35950
|
}, render = function() {
|
|
35677
35951
|
const contentWidth = renderer.width - 4;
|
|
35678
|
-
const
|
|
35952
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
35953
|
+
const rowsBelow = rowsBelowVisibleWindow({ sessionCount: sessions.length, selectedIndex, height: renderer.height });
|
|
35679
35954
|
const isNarrow = renderer.width < 72;
|
|
35680
|
-
const selectedSession =
|
|
35681
|
-
const sessionRows =
|
|
35955
|
+
const selectedSession = filterMode ? undefined : sessions[selectedIndex];
|
|
35956
|
+
const sessionRows = filterMode ? formatFilterMenu({ mode: filterMode, authors, width: contentWidth }) : sessions.length === 0 ? stringToStyledText2(sessionSearchQuery ? "No sessions match the search." : hasActiveFilters(filters) ? "No sessions match the active filters." : "No remote sessions yet.") : isNarrow ? formatSessionCard({
|
|
35682
35957
|
session: selectedSession,
|
|
35683
35958
|
selectedIndex,
|
|
35684
|
-
sessionCount:
|
|
35959
|
+
sessionCount: sessions.length,
|
|
35685
35960
|
width: contentWidth
|
|
35686
35961
|
}) : formatSessionList({
|
|
35687
|
-
sessions
|
|
35962
|
+
sessions,
|
|
35688
35963
|
selectedIndex,
|
|
35689
35964
|
width: contentWidth,
|
|
35690
35965
|
height: renderer.height
|
|
35691
35966
|
});
|
|
35692
|
-
statusBand.content = dashboardStatusBand({ page, pageIndex });
|
|
35967
|
+
statusBand.content = dashboardStatusBand({ page, pageIndex, filters, sessionSearchQuery });
|
|
35693
35968
|
content.content = joinStyled([stringToStyledText2(""), sessionRows], `
|
|
35694
35969
|
`);
|
|
35970
|
+
desiredContentScrollY = filterMode ? filterMode.selectedIndex + 2 : 0;
|
|
35971
|
+
if (!contentScrollHandler) {
|
|
35972
|
+
contentScrollHandler = () => {
|
|
35973
|
+
contentScrollHandler = undefined;
|
|
35974
|
+
if (destroyed)
|
|
35975
|
+
return;
|
|
35976
|
+
listRegion.scrollTo({ x: 0, y: desiredContentScrollY });
|
|
35977
|
+
renderer.requestRender();
|
|
35978
|
+
};
|
|
35979
|
+
renderer.once(CliRenderEvents2.FRAME, contentScrollHandler);
|
|
35980
|
+
}
|
|
35695
35981
|
const showPreview = !isNarrow && selectedSession !== undefined;
|
|
35696
35982
|
previewBand.visible = showPreview;
|
|
35697
35983
|
previewBand.content = showPreview ? formatSessionPreview({ session: selectedSession, width: contentWidth }) : stringToStyledText2("");
|
|
35698
|
-
|
|
35699
|
-
|
|
35984
|
+
const activeSearchComposer = filterMode?.searching ? "filter" : sessionSearchOpen ? "session" : undefined;
|
|
35985
|
+
if (activeSearchComposer !== mountedSearchComposer) {
|
|
35986
|
+
if (mountedSearchComposer === "filter")
|
|
35987
|
+
filterSearchComposer.unmount();
|
|
35988
|
+
else if (mountedSearchComposer === "session")
|
|
35989
|
+
sessionSearchComposer.unmount();
|
|
35990
|
+
mountedSearchComposer = activeSearchComposer;
|
|
35991
|
+
if (activeSearchComposer === "filter") {
|
|
35992
|
+
filterSearchComposer.mount();
|
|
35993
|
+
filterSearchComposer.focus();
|
|
35994
|
+
} else if (activeSearchComposer === "session") {
|
|
35995
|
+
sessionSearchComposer.mount();
|
|
35996
|
+
sessionSearchComposer.focus();
|
|
35997
|
+
} else {
|
|
35998
|
+
listRegion.focus();
|
|
35999
|
+
}
|
|
36000
|
+
}
|
|
36001
|
+
if (activeSearchComposer === "filter")
|
|
36002
|
+
filterSearchComposer.render();
|
|
36003
|
+
else if (activeSearchComposer === "session")
|
|
36004
|
+
sessionSearchComposer.render();
|
|
36005
|
+
separator.content = activeSearchComposer ? "" : renderComposerDivider({ width: contentWidth });
|
|
36006
|
+
footer.content = filterMode ? dashboardFilterFooter({ mode: filterMode, loadingPage, loadingAuthors, pageError, authorError }) : dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen, sessionSearchOpen });
|
|
35700
36007
|
renderer.requestRender();
|
|
35701
36008
|
}, finish = function(value) {
|
|
35702
36009
|
if (settled)
|
|
@@ -35711,9 +36018,40 @@ async function createDashboardTui({
|
|
|
35711
36018
|
stopPageRefresh();
|
|
35712
36019
|
rejectDraft(error93);
|
|
35713
36020
|
}, moveSelectionTo = function(index) {
|
|
35714
|
-
|
|
36021
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
36022
|
+
if (sessions.length === 0)
|
|
36023
|
+
return;
|
|
36024
|
+
selectedIndex = Math.max(0, Math.min(index, sessions.length - 1));
|
|
36025
|
+
render();
|
|
36026
|
+
}, applyFilterSearch = function() {
|
|
36027
|
+
if (!filterMode?.searching)
|
|
36028
|
+
return;
|
|
36029
|
+
filterMode = { ...filterMode, searching: false, selectedIndex: 0 };
|
|
36030
|
+
filterSearchComposer.textarea.setText("");
|
|
36031
|
+
render();
|
|
36032
|
+
}, cancelFilterSearch = function() {
|
|
36033
|
+
if (!filterMode?.searching)
|
|
36034
|
+
return;
|
|
36035
|
+
filterMode = { ...filterMode, searching: false, query: "", selectedIndex: 0 };
|
|
36036
|
+
filterSearchComposer.textarea.setText("");
|
|
36037
|
+
render();
|
|
36038
|
+
}, openSessionSearch = function() {
|
|
36039
|
+
sessionSearchOpen = true;
|
|
36040
|
+
sessionSearchComposer.textarea.setText(sessionSearchQuery);
|
|
36041
|
+
render();
|
|
36042
|
+
}, applySessionSearch = function() {
|
|
36043
|
+
if (!sessionSearchOpen)
|
|
36044
|
+
return;
|
|
36045
|
+
sessionSearchOpen = false;
|
|
36046
|
+
selectedIndex = 0;
|
|
36047
|
+
render();
|
|
36048
|
+
}, cancelSessionSearch = function() {
|
|
36049
|
+
if (!sessionSearchOpen)
|
|
35715
36050
|
return;
|
|
35716
|
-
|
|
36051
|
+
sessionSearchOpen = false;
|
|
36052
|
+
sessionSearchQuery = "";
|
|
36053
|
+
selectedIndex = 0;
|
|
36054
|
+
sessionSearchComposer.textarea.setText("");
|
|
35717
36055
|
render();
|
|
35718
36056
|
}, handleSessionKey = function(key) {
|
|
35719
36057
|
if (logoutConfirmationOpen) {
|
|
@@ -35726,6 +36064,84 @@ async function createDashboardTui({
|
|
|
35726
36064
|
}
|
|
35727
36065
|
return;
|
|
35728
36066
|
}
|
|
36067
|
+
if (sessionSearchOpen) {
|
|
36068
|
+
if (key.name === "escape") {
|
|
36069
|
+
key.preventDefault();
|
|
36070
|
+
cancelSessionSearch();
|
|
36071
|
+
}
|
|
36072
|
+
return;
|
|
36073
|
+
}
|
|
36074
|
+
if (filterMode) {
|
|
36075
|
+
const options = visibleDashboardFilterOptions({ mode: filterMode, authors });
|
|
36076
|
+
if (key.name === "escape") {
|
|
36077
|
+
key.preventDefault();
|
|
36078
|
+
if (filterMode.searching) {
|
|
36079
|
+
cancelFilterSearch();
|
|
36080
|
+
return;
|
|
36081
|
+
}
|
|
36082
|
+
filterMode = undefined;
|
|
36083
|
+
pageError = undefined;
|
|
36084
|
+
authorError = undefined;
|
|
36085
|
+
render();
|
|
36086
|
+
return;
|
|
36087
|
+
}
|
|
36088
|
+
if (filterMode.searching)
|
|
36089
|
+
return;
|
|
36090
|
+
if (loadingAuthors) {
|
|
36091
|
+
key.preventDefault();
|
|
36092
|
+
return;
|
|
36093
|
+
}
|
|
36094
|
+
const filterListFocused = renderer.currentFocusedRenderable === listRegion;
|
|
36095
|
+
if (filterListFocused && (key.name === "G" || key.name === "g" && key.shift)) {
|
|
36096
|
+
key.preventDefault();
|
|
36097
|
+
awaitingVimGoToTop = false;
|
|
36098
|
+
if (options.length > 0)
|
|
36099
|
+
filterMode = { ...filterMode, selectedIndex: options.length - 1 };
|
|
36100
|
+
render();
|
|
36101
|
+
return;
|
|
36102
|
+
}
|
|
36103
|
+
if (filterListFocused && key.name === "g") {
|
|
36104
|
+
key.preventDefault();
|
|
36105
|
+
if (awaitingVimGoToTop && options.length > 0)
|
|
36106
|
+
filterMode = { ...filterMode, selectedIndex: 0 };
|
|
36107
|
+
awaitingVimGoToTop = !awaitingVimGoToTop;
|
|
36108
|
+
render();
|
|
36109
|
+
return;
|
|
36110
|
+
}
|
|
36111
|
+
awaitingVimGoToTop = false;
|
|
36112
|
+
if (key.name === "down" || key.name === "up" || key.name === "j" || key.name === "k") {
|
|
36113
|
+
key.preventDefault();
|
|
36114
|
+
if (options.length > 0) {
|
|
36115
|
+
const backwards = key.name === "up" || key.name === "k";
|
|
36116
|
+
filterMode = { ...filterMode, selectedIndex: (filterMode.selectedIndex + (backwards ? -1 : 1) + options.length) % options.length };
|
|
36117
|
+
}
|
|
36118
|
+
render();
|
|
36119
|
+
return;
|
|
36120
|
+
}
|
|
36121
|
+
if (key.name === "space" || key.name === "left" || key.name === "right" || key.name === "h" || key.name === "l") {
|
|
36122
|
+
key.preventDefault();
|
|
36123
|
+
const option = options[filterMode.selectedIndex];
|
|
36124
|
+
if (!option)
|
|
36125
|
+
return;
|
|
36126
|
+
const selected = key.name === "space" ? !isDashboardFilterOptionSelected({ mode: filterMode, option }) : key.name === "right" || key.name === "l";
|
|
36127
|
+
filterMode = updateDashboardFilterSelection({ mode: filterMode, option, selected, authors });
|
|
36128
|
+
render();
|
|
36129
|
+
return;
|
|
36130
|
+
}
|
|
36131
|
+
if (key.name === "s") {
|
|
36132
|
+
key.preventDefault();
|
|
36133
|
+
filterMode = { ...filterMode, query: "", searching: true, selectedIndex: 0 };
|
|
36134
|
+
filterSearchComposer.textarea.setText("");
|
|
36135
|
+
render();
|
|
36136
|
+
return;
|
|
36137
|
+
}
|
|
36138
|
+
if (key.name === "return" || key.name === "enter") {
|
|
36139
|
+
key.preventDefault();
|
|
36140
|
+
const nextFilters = { statuses: [...filterMode.statuses], creators: [...filterMode.creators] };
|
|
36141
|
+
applyFilters(nextFilters);
|
|
36142
|
+
}
|
|
36143
|
+
return;
|
|
36144
|
+
}
|
|
35729
36145
|
const listFocused = renderer.currentFocusedRenderable === listRegion;
|
|
35730
36146
|
if (key.name === "down") {
|
|
35731
36147
|
key.preventDefault();
|
|
@@ -35754,7 +36170,7 @@ async function createDashboardTui({
|
|
|
35754
36170
|
if (listFocused && (key.name === "G" || key.name === "g" && key.shift)) {
|
|
35755
36171
|
key.preventDefault();
|
|
35756
36172
|
awaitingVimGoToTop = false;
|
|
35757
|
-
moveSelectionTo(page.sessions.length - 1);
|
|
36173
|
+
moveSelectionTo(visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery }).length - 1);
|
|
35758
36174
|
return;
|
|
35759
36175
|
}
|
|
35760
36176
|
if (listFocused && key.name === "g") {
|
|
@@ -35779,7 +36195,7 @@ async function createDashboardTui({
|
|
|
35779
36195
|
awaitingVimGoToTop = false;
|
|
35780
36196
|
if (key.name === "return" || key.name === "enter") {
|
|
35781
36197
|
key.preventDefault();
|
|
35782
|
-
const session = page.sessions[selectedIndex];
|
|
36198
|
+
const session = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery })[selectedIndex];
|
|
35783
36199
|
if (session)
|
|
35784
36200
|
finish({ kind: "session", sessionId: session.id });
|
|
35785
36201
|
return;
|
|
@@ -35789,6 +36205,23 @@ async function createDashboardTui({
|
|
|
35789
36205
|
finish({ kind: "new" });
|
|
35790
36206
|
return;
|
|
35791
36207
|
}
|
|
36208
|
+
if (key.name === "s") {
|
|
36209
|
+
key.preventDefault();
|
|
36210
|
+
openSessionSearch();
|
|
36211
|
+
return;
|
|
36212
|
+
}
|
|
36213
|
+
if (key.name === "f") {
|
|
36214
|
+
key.preventDefault();
|
|
36215
|
+
authorError = undefined;
|
|
36216
|
+
filterMode = createFilterMode({ kind: "status", filters });
|
|
36217
|
+
render();
|
|
36218
|
+
return;
|
|
36219
|
+
}
|
|
36220
|
+
if (key.name === "a") {
|
|
36221
|
+
key.preventDefault();
|
|
36222
|
+
openAuthorFilter();
|
|
36223
|
+
return;
|
|
36224
|
+
}
|
|
35792
36225
|
if (key.name === "L" || key.shift && key.name === "l") {
|
|
35793
36226
|
key.preventDefault();
|
|
35794
36227
|
logoutConfirmationOpen = true;
|
|
@@ -35805,21 +36238,19 @@ async function createDashboardTui({
|
|
|
35805
36238
|
finish(null);
|
|
35806
36239
|
}
|
|
35807
36240
|
};
|
|
35808
|
-
const root = new
|
|
35809
|
-
const statusBand = new
|
|
35810
|
-
const content = new
|
|
36241
|
+
const root = new BoxRenderable2(renderer, { width: "100%", height: "100%", flexDirection: "column", paddingX: 2, overflow: "hidden" });
|
|
36242
|
+
const statusBand = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36243
|
+
const content = new TextRenderable2(renderer, { content: "" });
|
|
35811
36244
|
const listRegion = new ScrollBoxRenderable(renderer, { flexGrow: 1, flexShrink: 1, minHeight: 0, scrollY: true });
|
|
35812
|
-
const previewBand = new
|
|
35813
|
-
const
|
|
35814
|
-
const
|
|
35815
|
-
|
|
35816
|
-
event.preventDefault();
|
|
35817
|
-
listRegion.focus();
|
|
35818
|
-
};
|
|
36245
|
+
const previewBand = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36246
|
+
const filterSearchRegion = new BoxRenderable2(renderer, { width: "100%", flexShrink: 0 });
|
|
36247
|
+
const separator = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36248
|
+
const footer = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
35819
36249
|
listRegion.add(content);
|
|
35820
36250
|
root.add(statusBand);
|
|
35821
36251
|
root.add(listRegion);
|
|
35822
36252
|
root.add(previewBand);
|
|
36253
|
+
root.add(filterSearchRegion);
|
|
35823
36254
|
root.add(separator);
|
|
35824
36255
|
root.add(footer);
|
|
35825
36256
|
renderer.root.add(root);
|
|
@@ -35829,8 +36260,22 @@ async function createDashboardTui({
|
|
|
35829
36260
|
let loadingPage = false;
|
|
35830
36261
|
let pageRequestInFlight = false;
|
|
35831
36262
|
let pageError;
|
|
36263
|
+
let authorError;
|
|
36264
|
+
let authors = initialAuthors;
|
|
36265
|
+
let authorsLoaded = loadAuthors === undefined || initialAuthors.length > 0;
|
|
36266
|
+
let loadingAuthors = false;
|
|
35832
36267
|
let awaitingVimGoToTop = false;
|
|
35833
36268
|
let logoutConfirmationOpen = false;
|
|
36269
|
+
let filters = initialFilters;
|
|
36270
|
+
let filterMode;
|
|
36271
|
+
let sessionSearchOpen = false;
|
|
36272
|
+
let sessionSearchQuery = "";
|
|
36273
|
+
root.onMouseDown = (event) => {
|
|
36274
|
+
if (filterMode?.searching || sessionSearchOpen)
|
|
36275
|
+
return;
|
|
36276
|
+
event.preventDefault();
|
|
36277
|
+
listRegion.focus();
|
|
36278
|
+
};
|
|
35834
36279
|
let destroyed = false;
|
|
35835
36280
|
let rendererDestroyPromise;
|
|
35836
36281
|
let settled = false;
|
|
@@ -35840,10 +36285,41 @@ async function createDashboardTui({
|
|
|
35840
36285
|
let rejectDraft = () => {
|
|
35841
36286
|
return;
|
|
35842
36287
|
};
|
|
36288
|
+
let desiredContentScrollY = 0;
|
|
35843
36289
|
const action = new Promise((resolve, reject) => {
|
|
35844
36290
|
resolveAction = resolve;
|
|
35845
36291
|
rejectDraft = reject;
|
|
35846
36292
|
});
|
|
36293
|
+
let mountedSearchComposer;
|
|
36294
|
+
const filterSearchComposer = createComposer({
|
|
36295
|
+
renderer,
|
|
36296
|
+
parent: filterSearchRegion,
|
|
36297
|
+
tag: "Filter",
|
|
36298
|
+
placeholder: "Filter options\u2026",
|
|
36299
|
+
screenPaddingX: 2,
|
|
36300
|
+
onSubmit: () => applyFilterSearch(),
|
|
36301
|
+
onContentChange: () => {
|
|
36302
|
+
if (!filterMode?.searching)
|
|
36303
|
+
return;
|
|
36304
|
+
filterMode = { ...filterMode, query: filterSearchComposer.textarea.plainText, selectedIndex: 0 };
|
|
36305
|
+
render();
|
|
36306
|
+
}
|
|
36307
|
+
});
|
|
36308
|
+
const sessionSearchComposer = createComposer({
|
|
36309
|
+
renderer,
|
|
36310
|
+
parent: filterSearchRegion,
|
|
36311
|
+
tag: "Search",
|
|
36312
|
+
placeholder: "Search by ID, title, or number\u2026",
|
|
36313
|
+
screenPaddingX: 2,
|
|
36314
|
+
onSubmit: () => applySessionSearch(),
|
|
36315
|
+
onContentChange: () => {
|
|
36316
|
+
if (!sessionSearchOpen)
|
|
36317
|
+
return;
|
|
36318
|
+
sessionSearchQuery = sessionSearchComposer.textarea.plainText;
|
|
36319
|
+
selectedIndex = 0;
|
|
36320
|
+
render();
|
|
36321
|
+
}
|
|
36322
|
+
});
|
|
35847
36323
|
async function loadAdjacentPage(direction) {
|
|
35848
36324
|
if (pageRequestInFlight)
|
|
35849
36325
|
return;
|
|
@@ -35860,7 +36336,8 @@ async function createDashboardTui({
|
|
|
35860
36336
|
return;
|
|
35861
36337
|
page = loadedPage;
|
|
35862
36338
|
pageIndex = Math.max(1, pageIndex + (direction === "next" ? 1 : -1));
|
|
35863
|
-
|
|
36339
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
36340
|
+
selectedIndex = direction === "next" ? 0 : Math.max(0, sessions.length - 1);
|
|
35864
36341
|
} catch (error93) {
|
|
35865
36342
|
if (!destroyed && !settled)
|
|
35866
36343
|
pageError = error93 instanceof Error ? error93.message : String(error93);
|
|
@@ -35879,7 +36356,7 @@ async function createDashboardTui({
|
|
|
35879
36356
|
const refreshedPage = await loadPage({ target: "current", signal: loadPageAbortController.signal });
|
|
35880
36357
|
if (destroyed || settled)
|
|
35881
36358
|
return;
|
|
35882
|
-
const selectedSessionId = page.sessions[selectedIndex]?.id;
|
|
36359
|
+
const selectedSessionId = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery })[selectedIndex]?.id;
|
|
35883
36360
|
page = refreshedPage;
|
|
35884
36361
|
const refreshedSelectedIndex = selectedSessionId ? page.sessions.findIndex((session) => session.id === selectedSessionId) : -1;
|
|
35885
36362
|
selectedIndex = refreshedSelectedIndex >= 0 ? refreshedSelectedIndex : Math.max(0, Math.min(selectedIndex, page.sessions.length - 1));
|
|
@@ -35894,10 +36371,53 @@ async function createDashboardTui({
|
|
|
35894
36371
|
pageRequestInFlight = false;
|
|
35895
36372
|
}
|
|
35896
36373
|
}
|
|
36374
|
+
async function applyFilters(nextFilters) {
|
|
36375
|
+
if (loadingPage)
|
|
36376
|
+
return;
|
|
36377
|
+
loadingPage = true;
|
|
36378
|
+
pageError = undefined;
|
|
36379
|
+
authorError = undefined;
|
|
36380
|
+
render();
|
|
36381
|
+
try {
|
|
36382
|
+
page = await loadPage({ filters: nextFilters, signal: loadPageAbortController.signal });
|
|
36383
|
+
filters = nextFilters;
|
|
36384
|
+
pageIndex = 1;
|
|
36385
|
+
selectedIndex = 0;
|
|
36386
|
+
filterMode = undefined;
|
|
36387
|
+
} catch (error93) {
|
|
36388
|
+
pageError = error93 instanceof Error ? error93.message : String(error93);
|
|
36389
|
+
} finally {
|
|
36390
|
+
loadingPage = false;
|
|
36391
|
+
render();
|
|
36392
|
+
}
|
|
36393
|
+
}
|
|
36394
|
+
async function openAuthorFilter() {
|
|
36395
|
+
filterMode = createFilterMode({ kind: "creator", filters });
|
|
36396
|
+
authorError = undefined;
|
|
36397
|
+
render();
|
|
36398
|
+
if (authorsLoaded || loadingAuthors || !loadAuthors)
|
|
36399
|
+
return;
|
|
36400
|
+
loadingAuthors = true;
|
|
36401
|
+
render();
|
|
36402
|
+
try {
|
|
36403
|
+
authors = await loadAuthors();
|
|
36404
|
+
authorsLoaded = true;
|
|
36405
|
+
if (filterMode?.kind === "creator") {
|
|
36406
|
+
const firstSelectedIndex = authors.findIndex((author) => filterMode?.creators.some((creator) => creator.id === author.id));
|
|
36407
|
+
filterMode = { ...filterMode, selectedIndex: Math.max(0, firstSelectedIndex) };
|
|
36408
|
+
}
|
|
36409
|
+
} catch (error93) {
|
|
36410
|
+
authorError = error93 instanceof Error ? error93.message : String(error93);
|
|
36411
|
+
} finally {
|
|
36412
|
+
loadingAuthors = false;
|
|
36413
|
+
render();
|
|
36414
|
+
}
|
|
36415
|
+
}
|
|
35897
36416
|
async function moveSelection(direction) {
|
|
35898
|
-
|
|
36417
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
36418
|
+
if (loadingPage || sessions.length === 0)
|
|
35899
36419
|
return;
|
|
35900
|
-
const isBoundary = direction === "next" ? selectedIndex ===
|
|
36420
|
+
const isBoundary = direction === "next" ? selectedIndex === sessions.length - 1 : selectedIndex === 0;
|
|
35901
36421
|
if (!isBoundary) {
|
|
35902
36422
|
selectedIndex += direction === "next" ? 1 : -1;
|
|
35903
36423
|
render();
|
|
@@ -35912,7 +36432,13 @@ async function createDashboardTui({
|
|
|
35912
36432
|
}
|
|
35913
36433
|
return false;
|
|
35914
36434
|
};
|
|
35915
|
-
|
|
36435
|
+
filterSearchComposer.textarea.onKeyDown = handleSessionKey;
|
|
36436
|
+
sessionSearchComposer.textarea.onKeyDown = handleSessionKey;
|
|
36437
|
+
keyHandler = (key) => {
|
|
36438
|
+
if (mountedSearchComposer && key.name !== "escape" && !(key.ctrl && key.name === "c"))
|
|
36439
|
+
return;
|
|
36440
|
+
handleSessionKey(key);
|
|
36441
|
+
};
|
|
35916
36442
|
renderer.addInputHandler(inputHandler);
|
|
35917
36443
|
renderer.keyInput.on("keypress", keyHandler);
|
|
35918
36444
|
renderer.on(CliRenderEvents2.RENDER_ERROR, (event) => fail(event.error));
|
|
@@ -35939,6 +36465,10 @@ async function createDashboardTui({
|
|
|
35939
36465
|
if (resizeHandler)
|
|
35940
36466
|
renderer.off(CliRenderEvents2.RESIZE, resizeHandler);
|
|
35941
36467
|
stopPageRefresh();
|
|
36468
|
+
if (contentScrollHandler)
|
|
36469
|
+
renderer.off(CliRenderEvents2.FRAME, contentScrollHandler);
|
|
36470
|
+
filterSearchComposer.destroy();
|
|
36471
|
+
sessionSearchComposer.destroy();
|
|
35942
36472
|
rendererDestroyed2 = true;
|
|
35943
36473
|
renderer.destroy();
|
|
35944
36474
|
},
|
|
@@ -35956,6 +36486,8 @@ async function createDashboardTui({
|
|
|
35956
36486
|
if (refreshTimer !== undefined)
|
|
35957
36487
|
clearInterval(refreshTimer);
|
|
35958
36488
|
loadPageAbortController.abort();
|
|
36489
|
+
if (contentScrollHandler)
|
|
36490
|
+
renderer.off(CliRenderEvents2.FRAME, contentScrollHandler);
|
|
35959
36491
|
if (!rendererDestroyed2) {
|
|
35960
36492
|
rendererDestroyed2 = true;
|
|
35961
36493
|
renderer.destroy();
|
|
@@ -35993,6 +36525,63 @@ function rowsBelowVisibleWindow({ sessionCount, selectedIndex, height }) {
|
|
|
35993
36525
|
const { firstIndex, rowCount } = visibleListWindow({ sessionCount, selectedIndex, height });
|
|
35994
36526
|
return Math.max(0, sessionCount - (firstIndex + rowCount));
|
|
35995
36527
|
}
|
|
36528
|
+
function createFilterMode({ kind, filters }) {
|
|
36529
|
+
return {
|
|
36530
|
+
kind,
|
|
36531
|
+
selectedIndex: 0,
|
|
36532
|
+
statuses: [...filters.statuses],
|
|
36533
|
+
creators: [...filters.creators],
|
|
36534
|
+
query: "",
|
|
36535
|
+
searching: false
|
|
36536
|
+
};
|
|
36537
|
+
}
|
|
36538
|
+
function formatFilterMenu({ mode, authors, width }) {
|
|
36539
|
+
const options = visibleDashboardFilterOptions({ mode, authors });
|
|
36540
|
+
const selectedCount = mode.kind === "status" ? mode.statuses.length : mode.creators.length;
|
|
36541
|
+
const heading = `${mode.kind === "status" ? "Filter by status" : "Filter by author"} \xB7 ${selectedCount} selected \xB7 empty shows all`;
|
|
36542
|
+
const rows = options.map((option, index) => {
|
|
36543
|
+
const active = isDashboardFilterOptionSelected({ mode, option });
|
|
36544
|
+
const label = `${active ? "[x]" : "[ ]"} ${truncate(option.label, Math.max(1, width - 4))}`;
|
|
36545
|
+
const chunk = index === mode.selectedIndex ? bg(PALETTE.selectionBg)(fg3(PALETTE.bodyText)(label.padEnd(width))) : active ? bold2(fg3(PALETTE.humanAccent)(label)) : fg3(PALETTE.dimText)(label);
|
|
36546
|
+
return [chunk];
|
|
36547
|
+
});
|
|
36548
|
+
return joinStyled([
|
|
36549
|
+
[bold2(fg3(PALETTE.bodyText)(truncate(heading, width)))],
|
|
36550
|
+
...rows.length > 0 ? rows : [[fg3(PALETTE.dimText)("No matching options.")]]
|
|
36551
|
+
], `
|
|
36552
|
+
`);
|
|
36553
|
+
}
|
|
36554
|
+
function dashboardFilterOptions({ kind, authors }) {
|
|
36555
|
+
return kind === "status" ? lifecycleStatusOptions.map((option) => ({ kind: "status", id: option.value, label: option.label })) : authors.map((author) => ({ kind: "creator", id: author.id, label: author.name }));
|
|
36556
|
+
}
|
|
36557
|
+
function visibleDashboardFilterOptions({ mode, authors }) {
|
|
36558
|
+
return dashboardFilterOptions({ kind: mode.kind, authors }).filter((option) => fuzzyMatches(option.label, mode.query));
|
|
36559
|
+
}
|
|
36560
|
+
function isDashboardFilterOptionSelected({ mode, option }) {
|
|
36561
|
+
return option.kind === "status" ? mode.statuses.includes(option.id) : mode.creators.some((creator) => creator.id === option.id);
|
|
36562
|
+
}
|
|
36563
|
+
function updateDashboardFilterSelection({ mode, option, selected, authors }) {
|
|
36564
|
+
if (option.kind === "status") {
|
|
36565
|
+
const selectedStatuses = new Set(mode.statuses);
|
|
36566
|
+
if (selected)
|
|
36567
|
+
selectedStatuses.add(option.id);
|
|
36568
|
+
else
|
|
36569
|
+
selectedStatuses.delete(option.id);
|
|
36570
|
+
return {
|
|
36571
|
+
...mode,
|
|
36572
|
+
statuses: lifecycleStatusOptions.map((entry) => entry.value).filter((status) => selectedStatuses.has(status))
|
|
36573
|
+
};
|
|
36574
|
+
}
|
|
36575
|
+
const selectedCreatorIds = new Set(mode.creators.map((creator) => creator.id));
|
|
36576
|
+
if (selected)
|
|
36577
|
+
selectedCreatorIds.add(option.id);
|
|
36578
|
+
else
|
|
36579
|
+
selectedCreatorIds.delete(option.id);
|
|
36580
|
+
return {
|
|
36581
|
+
...mode,
|
|
36582
|
+
creators: authors.filter((author) => selectedCreatorIds.has(author.id))
|
|
36583
|
+
};
|
|
36584
|
+
}
|
|
35996
36585
|
function formatSessionList({ sessions, selectedIndex, width, height }) {
|
|
35997
36586
|
const { firstIndex, rowCount } = visibleListWindow({ sessionCount: sessions.length, selectedIndex, height });
|
|
35998
36587
|
const visibleSessions = sessions.slice(firstIndex, firstIndex + rowCount);
|
|
@@ -36079,18 +36668,34 @@ function formatUpdatedAt(value) {
|
|
|
36079
36668
|
return `${Math.floor(seconds / (60 * 60))}h ago`;
|
|
36080
36669
|
return new Date(milliseconds).toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
36081
36670
|
}
|
|
36082
|
-
function
|
|
36083
|
-
|
|
36671
|
+
function matchesSessionSearch({ session, query }) {
|
|
36672
|
+
return fuzzyMatches(session.id, query) || fuzzyMatches(session.title ?? "", query) || fuzzyMatches(String(session.sessionNumber), query);
|
|
36673
|
+
}
|
|
36674
|
+
function visibleDashboardSessions({ sessions, query }) {
|
|
36675
|
+
return sessions.filter((session) => matchesSessionSearch({ session, query }));
|
|
36676
|
+
}
|
|
36677
|
+
function dashboardStatusBand({ page, pageIndex, filters, sessionSearchQuery }) {
|
|
36678
|
+
const openCount = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery }).filter((session) => session.status === "open").length;
|
|
36084
36679
|
const paging = [`page ${pageIndex}`];
|
|
36085
36680
|
if (page.canGoPrevious)
|
|
36086
36681
|
paging.push("\u2190 prev");
|
|
36087
36682
|
if (page.hasMore)
|
|
36088
36683
|
paging.push("more \u2192");
|
|
36089
|
-
|
|
36684
|
+
const activeFilters = [
|
|
36685
|
+
filters.statuses.length > 0 ? `status: ${filters.statuses.map(sessionStatusLabel).join(", ")}` : undefined,
|
|
36686
|
+
filters.creators.length > 0 ? `author: ${filters.creators.map((creator) => creator.name).join(", ")}` : undefined,
|
|
36687
|
+
sessionSearchQuery ? `search: ${sessionSearchQuery}` : undefined
|
|
36688
|
+
].filter((value) => value !== undefined);
|
|
36689
|
+
return `remy \xB7 sessions \xB7 ${openCount} open${activeFilters.length > 0 ? ` \xB7 ${activeFilters.join(" \xB7 ")}` : ""} \xB7 ${paging.join(" \xB7 ")}`;
|
|
36090
36690
|
}
|
|
36091
|
-
function
|
|
36691
|
+
function hasActiveFilters(filters) {
|
|
36692
|
+
return filters.statuses.length > 0 || filters.creators.length > 0;
|
|
36693
|
+
}
|
|
36694
|
+
function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen, sessionSearchOpen }) {
|
|
36092
36695
|
if (logoutConfirmationOpen)
|
|
36093
36696
|
return "Log out of Remy? y confirms \xB7 Esc cancels";
|
|
36697
|
+
if (sessionSearchOpen)
|
|
36698
|
+
return "type to search \xB7 \u23CE apply search \xB7 Esc clear search";
|
|
36094
36699
|
const parts = [];
|
|
36095
36700
|
if (loadingPage)
|
|
36096
36701
|
parts.push("Loading sessions\u2026");
|
|
@@ -36098,9 +36703,22 @@ function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmation
|
|
|
36098
36703
|
parts.push(`Could not load sessions: ${pageError}`);
|
|
36099
36704
|
if (rowsBelow > 0)
|
|
36100
36705
|
parts.push(`\u2193 ${rowsBelow} more below`);
|
|
36101
|
-
parts.push("\u2191\u2193 move \xB7 \u23CE open \xB7 n new session \xB7 \u21E7l log out \xB7 q quit");
|
|
36706
|
+
parts.push("\u2191\u2193 move \xB7 \u23CE open \xB7 s search \xB7 f status \xB7 a author \xB7 n new session \xB7 \u21E7l log out \xB7 q quit");
|
|
36102
36707
|
return parts.join(" \xB7 ");
|
|
36103
36708
|
}
|
|
36709
|
+
function dashboardFilterFooter({ mode, loadingPage, loadingAuthors, pageError, authorError }) {
|
|
36710
|
+
if (loadingAuthors)
|
|
36711
|
+
return "Loading authors\u2026 \xB7 Esc cancel";
|
|
36712
|
+
if (loadingPage)
|
|
36713
|
+
return "Applying filter\u2026";
|
|
36714
|
+
if (mode.searching)
|
|
36715
|
+
return "type to filter \xB7 \u23CE apply search \xB7 Esc cancel search";
|
|
36716
|
+
return [
|
|
36717
|
+
authorError ? `Could not load authors: ${authorError}` : undefined,
|
|
36718
|
+
pageError ? `Could not apply filter: ${pageError}` : undefined,
|
|
36719
|
+
"\u2191\u2193/jk move \xB7 space toggle \xB7 \u2190\u2192/hl mark \xB7 s search \xB7 \u23CE apply \xB7 Esc cancel"
|
|
36720
|
+
].filter((value) => value !== undefined).join(" \xB7 ");
|
|
36721
|
+
}
|
|
36104
36722
|
function truncate(value, width) {
|
|
36105
36723
|
if (value.length <= width)
|
|
36106
36724
|
return value;
|
|
@@ -36111,87 +36729,33 @@ async function createDefaultRenderer() {
|
|
|
36111
36729
|
}
|
|
36112
36730
|
|
|
36113
36731
|
// src/tui/new-session-wizard.ts
|
|
36114
|
-
import { bg as bg3, BoxRenderable as BoxRenderable3, bold as bold3, CliRenderEvents as CliRenderEvents3, dim as
|
|
36115
|
-
|
|
36116
|
-
// src/tui/composer.ts
|
|
36117
|
-
import { BoxRenderable as BoxRenderable2, TextRenderable as TextRenderable2, TextareaRenderable } from "@opentui/core";
|
|
36732
|
+
import { bg as bg3, BoxRenderable as BoxRenderable3, bold as bold3, CliRenderEvents as CliRenderEvents3, dim as dim4, fg as fg6, ScrollBoxRenderable as ScrollBoxRenderable2, StyledText as StyledText5, stringToStyledText as stringToStyledText4, TextRenderable as TextRenderable3 } from "@opentui/core";
|
|
36118
36733
|
|
|
36119
|
-
// src/tui/
|
|
36120
|
-
|
|
36121
|
-
function
|
|
36122
|
-
|
|
36734
|
+
// src/tui/attachment-presentation.ts
|
|
36735
|
+
import { dim as dim3, fg as fg4, StyledText as StyledText3 } from "@opentui/core";
|
|
36736
|
+
function attachmentUploadMessage(filenames) {
|
|
36737
|
+
return filenames.length === 1 ? `Attaching ${filenames[0]}\u2026` : `Attaching ${filenames.length} files\u2026`;
|
|
36738
|
+
}
|
|
36739
|
+
function renderAttachmentFeedback(feedback) {
|
|
36740
|
+
const presentation = feedback.kind === "progress" ? { glyph: "\u28FF", color: PALETTE.progress } : feedback.kind === "success" ? { glyph: "\u2713", color: PALETTE.statusCompleted } : { glyph: "\u2717", color: PALETTE.failure };
|
|
36741
|
+
return new StyledText3([
|
|
36742
|
+
fg4(presentation.color)(`${presentation.glyph} `),
|
|
36743
|
+
fg4(feedback.kind === "failure" ? PALETTE.failure : PALETTE.bodyText)(feedback.message)
|
|
36744
|
+
]);
|
|
36123
36745
|
}
|
|
36124
|
-
|
|
36125
|
-
|
|
36126
|
-
|
|
36127
|
-
|
|
36128
|
-
|
|
36129
|
-
|
|
36130
|
-
|
|
36131
|
-
|
|
36132
|
-
|
|
36133
|
-
onSubmit,
|
|
36134
|
-
onContentChange
|
|
36135
|
-
}) {
|
|
36136
|
-
const root = new BoxRenderable2(renderer, { width: "100%", flexDirection: "column", flexShrink: 0 });
|
|
36137
|
-
const dividerTop = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36138
|
-
const textarea = new TextareaRenderable(renderer, {
|
|
36139
|
-
width: "100%",
|
|
36140
|
-
height: 1,
|
|
36141
|
-
flexShrink: 0,
|
|
36142
|
-
wrapMode: "word",
|
|
36143
|
-
placeholder,
|
|
36144
|
-
keyBindings: [
|
|
36145
|
-
{ name: "return", action: "submit" },
|
|
36146
|
-
{ name: "return", shift: true, action: "newline" },
|
|
36147
|
-
{ name: "j", ctrl: true, action: "newline" }
|
|
36148
|
-
],
|
|
36149
|
-
onSubmit
|
|
36150
|
-
});
|
|
36151
|
-
const dividerBottom = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36152
|
-
let mounted = false;
|
|
36153
|
-
root.add(dividerTop);
|
|
36154
|
-
root.add(textarea);
|
|
36155
|
-
root.add(dividerBottom);
|
|
36156
|
-
textarea.onContentChange = () => {
|
|
36157
|
-
resizeComposer(textarea);
|
|
36158
|
-
onContentChange?.();
|
|
36159
|
-
};
|
|
36160
|
-
function render() {
|
|
36161
|
-
dividerTop.content = renderComposerDivider({ width: renderer.width - 2, tag, leadLabel: topDividerLabel?.() });
|
|
36162
|
-
dividerBottom.content = renderComposerDivider({ width: renderer.width - 2 });
|
|
36163
|
-
}
|
|
36164
|
-
return {
|
|
36165
|
-
textarea,
|
|
36166
|
-
mount() {
|
|
36167
|
-
if (mounted)
|
|
36168
|
-
return;
|
|
36169
|
-
mounted = true;
|
|
36170
|
-
parent.add(root);
|
|
36171
|
-
resizeComposer(textarea);
|
|
36172
|
-
render();
|
|
36173
|
-
},
|
|
36174
|
-
unmount() {
|
|
36175
|
-
if (!mounted)
|
|
36176
|
-
return;
|
|
36177
|
-
mounted = false;
|
|
36178
|
-
parent.remove(root);
|
|
36179
|
-
},
|
|
36180
|
-
render,
|
|
36181
|
-
focus() {
|
|
36182
|
-
textarea.focus();
|
|
36183
|
-
},
|
|
36184
|
-
destroy() {
|
|
36185
|
-
if (mounted)
|
|
36186
|
-
parent.remove(root);
|
|
36187
|
-
mounted = false;
|
|
36188
|
-
root.destroyRecursively();
|
|
36189
|
-
}
|
|
36190
|
-
};
|
|
36746
|
+
function renderAttachmentSummary(attachments) {
|
|
36747
|
+
if (attachments.length === 0)
|
|
36748
|
+
return new StyledText3([]);
|
|
36749
|
+
const label = attachments.length === 1 ? "Attachment" : `${attachments.length} attachments`;
|
|
36750
|
+
return new StyledText3([
|
|
36751
|
+
dim3(fg4(PALETTE.dimText)("\u21B3 ")),
|
|
36752
|
+
fg4(PALETTE.tool)(label),
|
|
36753
|
+
dim3(fg4(PALETTE.dimText)(` \xB7 ${attachments.map((attachment) => attachment.filename).join(" \xB7 ")}`))
|
|
36754
|
+
]);
|
|
36191
36755
|
}
|
|
36192
36756
|
|
|
36193
36757
|
// src/tui/path-completion-menu.ts
|
|
36194
|
-
import { bg as bg2, fg as
|
|
36758
|
+
import { bg as bg2, fg as fg5, stringToStyledText as stringToStyledText3, StyledText as StyledText4 } from "@opentui/core";
|
|
36195
36759
|
var maximumVisibleCompletions = 8;
|
|
36196
36760
|
function renderPathCompletionMenu({ completions, selectedIndex }) {
|
|
36197
36761
|
if (completions.length === 0)
|
|
@@ -36202,7 +36766,7 @@ function renderPathCompletionMenu({ completions, selectedIndex }) {
|
|
|
36202
36766
|
const completionIndex = firstIndex + index;
|
|
36203
36767
|
const selected = completionIndex === selectedIndex;
|
|
36204
36768
|
const rowStyle = selected ? bg2(PALETTE.selectionBg) : (chunk) => chunk;
|
|
36205
|
-
return new
|
|
36769
|
+
return new StyledText4([rowStyle(fg5(PALETTE.bodyText)(`${selected ? "\u203A " : " "}${completion.display}`))]);
|
|
36206
36770
|
});
|
|
36207
36771
|
const overflow = completions.length > visibleCompletions.length ? stringToStyledText3(`${firstIndex + 1}-${firstIndex + visibleCompletions.length} of ${completions.length}`) : undefined;
|
|
36208
36772
|
return joinStyled([
|
|
@@ -36242,13 +36806,62 @@ async function completePathToken({ text, cwd, homeDirectory = homedir() }) {
|
|
|
36242
36806
|
const normalized = replacementPath.split(sep).join("/");
|
|
36243
36807
|
const suffix = entry.isDirectory() ? "/" : "";
|
|
36244
36808
|
const completed = `${normalized}${suffix}`;
|
|
36245
|
-
return {
|
|
36809
|
+
return {
|
|
36810
|
+
display: completed,
|
|
36811
|
+
replacement: `${token.startsWith("@") ? "@" : ""}${completed}`,
|
|
36812
|
+
...entry.isFile() ? { attachmentPath: completedPath } : {}
|
|
36813
|
+
};
|
|
36246
36814
|
});
|
|
36247
36815
|
}
|
|
36248
36816
|
function replaceActivePathToken({ text, replacement }) {
|
|
36249
36817
|
const token = activePathToken(text);
|
|
36250
36818
|
return token === undefined ? text : `${text.slice(0, text.length - token.length)}${replacement}`;
|
|
36251
36819
|
}
|
|
36820
|
+
function selectedAttachmentPaths({ text, selections }) {
|
|
36821
|
+
return selections.filter((selection) => text.slice(selection.start, selection.end) === selection.token && hasPathTokenBoundaries({ text, selection })).map((selection) => selection.attachmentPath);
|
|
36822
|
+
}
|
|
36823
|
+
function createSelectedPathCompletion({ attachmentPath, token, end }) {
|
|
36824
|
+
return { attachmentPath, token, start: end - token.length, end };
|
|
36825
|
+
}
|
|
36826
|
+
function reconcileSelectedPathCompletions({
|
|
36827
|
+
previousText,
|
|
36828
|
+
text,
|
|
36829
|
+
selections
|
|
36830
|
+
}) {
|
|
36831
|
+
if (text === previousText)
|
|
36832
|
+
return selections;
|
|
36833
|
+
let changeStart = 0;
|
|
36834
|
+
while (changeStart < previousText.length && changeStart < text.length && previousText[changeStart] === text[changeStart])
|
|
36835
|
+
changeStart += 1;
|
|
36836
|
+
let previousChangeEnd = previousText.length;
|
|
36837
|
+
let nextChangeEnd = text.length;
|
|
36838
|
+
while (previousChangeEnd > changeStart && nextChangeEnd > changeStart && previousText[previousChangeEnd - 1] === text[nextChangeEnd - 1]) {
|
|
36839
|
+
previousChangeEnd -= 1;
|
|
36840
|
+
nextChangeEnd -= 1;
|
|
36841
|
+
}
|
|
36842
|
+
const offsetChange = nextChangeEnd - previousChangeEnd;
|
|
36843
|
+
return selections.flatMap((selection) => {
|
|
36844
|
+
if (selection.end <= changeStart)
|
|
36845
|
+
return [selection];
|
|
36846
|
+
if (selection.start >= previousChangeEnd) {
|
|
36847
|
+
return [{
|
|
36848
|
+
...selection,
|
|
36849
|
+
start: selection.start + offsetChange,
|
|
36850
|
+
end: selection.end + offsetChange
|
|
36851
|
+
}];
|
|
36852
|
+
}
|
|
36853
|
+
return [];
|
|
36854
|
+
}).filter((selection) => hasPathTokenBoundaries({ text, selection }));
|
|
36855
|
+
}
|
|
36856
|
+
function hasPathTokenBoundaries({ text, selection }) {
|
|
36857
|
+
let before = selection.start;
|
|
36858
|
+
while (before > 0 && "([{'\"`".includes(text[before - 1]))
|
|
36859
|
+
before -= 1;
|
|
36860
|
+
let after = selection.end;
|
|
36861
|
+
while (after < text.length && '),.;:!?]}"`'.includes(text[after]))
|
|
36862
|
+
after += 1;
|
|
36863
|
+
return (before === 0 || /\s/.test(text[before - 1])) && (after === text.length || /\s/.test(text[after]));
|
|
36864
|
+
}
|
|
36252
36865
|
function activePathToken(text) {
|
|
36253
36866
|
const match = text.match(/(?:^|\s)(\S*)$/);
|
|
36254
36867
|
return match?.[1];
|
|
@@ -36261,6 +36874,7 @@ async function createNewSessionWizard({
|
|
|
36261
36874
|
repositories,
|
|
36262
36875
|
reloadRepositories,
|
|
36263
36876
|
suggestRepositories,
|
|
36877
|
+
suggestBranches,
|
|
36264
36878
|
readClipboardImage,
|
|
36265
36879
|
savePastedImage,
|
|
36266
36880
|
attachPromptPaths,
|
|
@@ -36297,15 +36911,19 @@ async function createNewSessionWizard({
|
|
|
36297
36911
|
else
|
|
36298
36912
|
overrides.set(repositoryId, isSelected);
|
|
36299
36913
|
}, renderRepositoryLoadingSkeleton = function() {
|
|
36300
|
-
return new
|
|
36301
|
-
|
|
36914
|
+
return new StyledText5([
|
|
36915
|
+
dim4(fg6(PALETTE.dimText)(`\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
|
|
36302
36916
|
\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
|
|
36303
36917
|
\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588`))
|
|
36304
36918
|
]);
|
|
36305
36919
|
}, render = function() {
|
|
36306
36920
|
const repositoryListVisible = step === "repositories" || step === "repositorySearch";
|
|
36307
36921
|
const composerVisible = step === "prompt" || step === "repositorySearch" || step === "recompute";
|
|
36308
|
-
const orient = (body) => joinStyled([renderStepIndicator({
|
|
36922
|
+
const orient = (body) => joinStyled([renderStepIndicator({
|
|
36923
|
+
step,
|
|
36924
|
+
hasInstallationChoice: installationIds.length > 1,
|
|
36925
|
+
hasBranchChoice: step === "loadingBranches" || branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch")
|
|
36926
|
+
}), body], `
|
|
36309
36927
|
`);
|
|
36310
36928
|
if (composerVisible && !composerMounted) {
|
|
36311
36929
|
composerMounted = true;
|
|
@@ -36323,19 +36941,22 @@ async function createNewSessionWizard({
|
|
|
36323
36941
|
repositoryRows.content = "";
|
|
36324
36942
|
repositoryDivider.content = "";
|
|
36325
36943
|
repositoryFooter.content = "";
|
|
36326
|
-
const attachmentText =
|
|
36944
|
+
const attachmentText = renderAttachmentSummary(attachments);
|
|
36945
|
+
const attachmentStatus = attachmentFeedback ? renderAttachmentFeedback(attachmentFeedback) : undefined;
|
|
36327
36946
|
if (step === "prompt") {
|
|
36328
36947
|
const parts = [
|
|
36329
|
-
new
|
|
36948
|
+
new StyledText5([stepHeader("New remote Codex session")]),
|
|
36330
36949
|
stringToStyledText4("Describe the work you want Remy to do. Press Enter to continue; Esc back.")
|
|
36331
36950
|
];
|
|
36332
|
-
if (attachmentText)
|
|
36951
|
+
if (attachmentText.chunks.length > 0)
|
|
36333
36952
|
parts.push(attachmentText);
|
|
36334
36953
|
const completionMenu = pathCompletionMenuOpen ? renderPathCompletionMenu({ completions: pathCompletions, selectedIndex: completionIndex }) : undefined;
|
|
36335
36954
|
if (completionMenu)
|
|
36336
36955
|
parts.push(completionMenu);
|
|
36337
36956
|
if (status)
|
|
36338
36957
|
parts.push(stringToStyledText4(status));
|
|
36958
|
+
if (attachmentStatus)
|
|
36959
|
+
parts.push(attachmentStatus);
|
|
36339
36960
|
content.content = orient(joinStyled(parts, `
|
|
36340
36961
|
|
|
36341
36962
|
`));
|
|
@@ -36343,7 +36964,7 @@ async function createNewSessionWizard({
|
|
|
36343
36964
|
const rows = joinStyled(installationIds.map((id, index) => renderSelectableRow({ label: id, isCursor: index === installationIndex })), `
|
|
36344
36965
|
`);
|
|
36345
36966
|
content.content = orient(joinStyled([
|
|
36346
|
-
new
|
|
36967
|
+
new StyledText5([stepHeader("Select GitHub installation")]),
|
|
36347
36968
|
rows,
|
|
36348
36969
|
stringToStyledText4("\u2191\u2193 move \xB7 \u23CE continue \xB7 esc back")
|
|
36349
36970
|
], `
|
|
@@ -36352,11 +36973,11 @@ async function createNewSessionWizard({
|
|
|
36352
36973
|
} else if (step === "loadingRepositories") {
|
|
36353
36974
|
const failed = repositoryLoadState === "failed";
|
|
36354
36975
|
const parts = [
|
|
36355
|
-
new
|
|
36976
|
+
new StyledText5([stepHeader("Select repositories")]),
|
|
36356
36977
|
stringToStyledText4(failed ? "Could not load your repositories." : "Loading your repos\u2026")
|
|
36357
36978
|
];
|
|
36358
36979
|
if (failed) {
|
|
36359
|
-
parts.push(new
|
|
36980
|
+
parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(`(${status})`))]));
|
|
36360
36981
|
parts.push(stringToStyledText4("r retry \xB7 esc back"));
|
|
36361
36982
|
} else {
|
|
36362
36983
|
parts.push(renderRepositoryLoadingSkeleton());
|
|
@@ -36367,12 +36988,41 @@ async function createNewSessionWizard({
|
|
|
36367
36988
|
`));
|
|
36368
36989
|
} else if (step === "loadingSuggestions") {
|
|
36369
36990
|
content.content = orient(joinStyled([
|
|
36370
|
-
new
|
|
36371
|
-
new
|
|
36372
|
-
new
|
|
36991
|
+
new StyledText5([stepHeader("Select repositories")]),
|
|
36992
|
+
new StyledText5([fg6(PALETTE.progress)(`${suggestionSpinnerFrames[suggestionSpinnerFrame]} Remy is matching your prompt to repositories\u2026`)]),
|
|
36993
|
+
new StyledText5([dim4(fg6(PALETTE.dimText)("This can take a few seconds."))]),
|
|
36373
36994
|
stringToStyledText4("s select repositories yourself \xB7 esc back")
|
|
36374
36995
|
], `
|
|
36375
36996
|
|
|
36997
|
+
`));
|
|
36998
|
+
} else if (step === "loadingBranches") {
|
|
36999
|
+
content.content = orient(joinStyled([
|
|
37000
|
+
new StyledText5([stepHeader("Choose branches")]),
|
|
37001
|
+
new StyledText5([fg6(PALETTE.progress)(`${suggestionSpinnerFrames[suggestionSpinnerFrame]} Remy is checking for related open pull requests\u2026`)]),
|
|
37002
|
+
new StyledText5([dim4(fg6(PALETTE.dimText)("This can take a few seconds."))]),
|
|
37003
|
+
stringToStyledText4("esc back")
|
|
37004
|
+
], `
|
|
37005
|
+
|
|
37006
|
+
`));
|
|
37007
|
+
} else if (step === "branches") {
|
|
37008
|
+
const existing = branchSuggestions.filter((suggestion) => suggestion.suggestionKind !== "createSessionBranch");
|
|
37009
|
+
const parts = [new StyledText5([stepHeader("Choose branches")])];
|
|
37010
|
+
if (existing.length > 0) {
|
|
37011
|
+
parts.push(stringToStyledText4("Remy found related open pull-request work:"));
|
|
37012
|
+
for (const suggestion of existing) {
|
|
37013
|
+
const repository = installationRepositories().find((candidate) => candidate.id === suggestion.repositoryId);
|
|
37014
|
+
parts.push(stringToStyledText4(`${repository?.fullName ?? suggestion.repositoryId} \u2014 ${suggestion.branchName}${suggestion.pullRequest ? ` (PR #${suggestion.pullRequest.number}: ${suggestion.pullRequest.title})` : ""}`));
|
|
37015
|
+
}
|
|
37016
|
+
parts.push(new StyledText5(renderSelectableRow({ label: "Continue from the suggested branch", isCursor: branchChoice === "existing" })));
|
|
37017
|
+
parts.push(new StyledText5(renderSelectableRow({ label: "Create a new session branch", isCursor: branchChoice === "new" })));
|
|
37018
|
+
parts.push(stringToStyledText4("\u2191\u2193 choose \xB7 \u23CE continue \xB7 esc back"));
|
|
37019
|
+
} else {
|
|
37020
|
+
parts.push(stringToStyledText4("Remy recommends creating a new session branch."));
|
|
37021
|
+
parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(status || "No related open pull request was identified from your request."))]));
|
|
37022
|
+
parts.push(stringToStyledText4("\u23CE continue \xB7 esc back"));
|
|
37023
|
+
}
|
|
37024
|
+
content.content = orient(joinStyled(parts, `
|
|
37025
|
+
|
|
36376
37026
|
`));
|
|
36377
37027
|
} else if (repositoryListVisible) {
|
|
36378
37028
|
const selectionOverrides = searchSelections ?? manualSelections;
|
|
@@ -36387,15 +37037,17 @@ async function createNewSessionWizard({
|
|
|
36387
37037
|
`) : step === "repositorySearch" ? "No matching repositories." : "No repositories available for this installation.";
|
|
36388
37038
|
repositoryDivider.content = renderComposerDivider({ width: renderer.width - 2 });
|
|
36389
37039
|
repositoryFooter.content = step === "repositorySearch" ? "type to filter \xB7 \u23CE apply \xB7 esc cancel search" : "\u2191\u2193 move \xB7 space toggle \xB7 \u2190\u2192 mark \xB7 s search \xB7 r recompute \xB7 \u23CE continue \xB7 esc back";
|
|
36390
|
-
const parts = step === "repositorySearch" ? [new
|
|
36391
|
-
new
|
|
37040
|
+
const parts = step === "repositorySearch" ? [new StyledText5([stepHeader("Search repositories")]), stringToStyledText4("Fuzzy matching repository names.")] : [
|
|
37041
|
+
new StyledText5([stepHeader("Select repositories")]),
|
|
36392
37042
|
stringToStyledText4(`${selected.size} selected \xB7 ${suggestedIds.size} suggested by Remy${suggestionConfidence ? ` (${suggestionConfidence} confidence)` : ""}`),
|
|
36393
37043
|
stringToStyledText4("Suggestions are based on your prompt and recompute instruction.")
|
|
36394
37044
|
];
|
|
36395
|
-
if (attachmentText)
|
|
37045
|
+
if (attachmentText.chunks.length > 0)
|
|
36396
37046
|
parts.push(attachmentText);
|
|
36397
37047
|
if (status && step === "repositories")
|
|
36398
37048
|
parts.push(stringToStyledText4(status));
|
|
37049
|
+
if (attachmentStatus)
|
|
37050
|
+
parts.push(attachmentStatus);
|
|
36399
37051
|
content.content = orient(joinStyled(parts, `
|
|
36400
37052
|
|
|
36401
37053
|
`));
|
|
@@ -36403,11 +37055,11 @@ async function createNewSessionWizard({
|
|
|
36403
37055
|
editor.placeholder = "Filter repositories\u2026";
|
|
36404
37056
|
} else if (step === "recompute") {
|
|
36405
37057
|
const parts = [
|
|
36406
|
-
new
|
|
37058
|
+
new StyledText5([stepHeader("Recompute suggestions")]),
|
|
36407
37059
|
stringToStyledText4("Suggestions use your prompt and instruction. Existing manual toggles stay selected or unselected."),
|
|
36408
37060
|
stringToStyledText4("Enter instruction, then press Enter to continue. Esc back.")
|
|
36409
37061
|
];
|
|
36410
|
-
if (attachmentText)
|
|
37062
|
+
if (attachmentText.chunks.length > 0)
|
|
36411
37063
|
parts.push(attachmentText);
|
|
36412
37064
|
content.content = orient(joinStyled(parts, `
|
|
36413
37065
|
|
|
@@ -36415,26 +37067,31 @@ async function createNewSessionWizard({
|
|
|
36415
37067
|
editor.placeholder = "e.g. worker, migration, dashboard";
|
|
36416
37068
|
} else {
|
|
36417
37069
|
const selected = installationRepositories().filter((repository) => selectedRepositoryIds().has(repository.id));
|
|
36418
|
-
const
|
|
36419
|
-
|
|
37070
|
+
const branchOverrides = branchChoice === "existing" ? branchSuggestions.filter((suggestion) => suggestion.suggestionKind !== "createSessionBranch") : [];
|
|
37071
|
+
const meta5 = new StyledText5([
|
|
37072
|
+
fg6(PALETTE.bodyText)(`Installation: ${selectedInstallationId() ?? "(missing)"}
|
|
37073
|
+
`),
|
|
37074
|
+
fg6(PALETTE.bodyText)(`Repositories: ${selected.map((repository) => repository.fullName).join(", ") || "(none)"}
|
|
36420
37075
|
`),
|
|
36421
|
-
|
|
37076
|
+
fg6(PALETTE.bodyText)(`Branches: ${branchOverrides.length > 0 ? branchOverrides.map((override) => override.branchName).join(", ") : "new session branches"}
|
|
36422
37077
|
`),
|
|
36423
|
-
|
|
36424
|
-
|
|
37078
|
+
fg6(PALETTE.bodyText)(`Model: ${newSessionModelLabel(model)} \xB7 ${newSessionReasoningEffortLabel(reasoningEffort)} reasoning`),
|
|
37079
|
+
dim4(fg6(PALETTE.dimText)(" \u25B8 m model \xB7 \u2190\u2192 reasoning"))
|
|
36425
37080
|
]);
|
|
36426
37081
|
const parts = [
|
|
36427
|
-
new
|
|
36428
|
-
new
|
|
37082
|
+
new StyledText5([stepHeader("Confirm new session")]),
|
|
37083
|
+
new StyledText5([renderRoleLabel({ label: "You", role: "human" })]),
|
|
36429
37084
|
renderMessageBody({ text: prompt, role: "human" }),
|
|
36430
37085
|
meta5
|
|
36431
37086
|
];
|
|
36432
|
-
if (attachmentText)
|
|
37087
|
+
if (attachmentText.chunks.length > 0)
|
|
36433
37088
|
parts.push(attachmentText);
|
|
36434
|
-
parts.push(new
|
|
36435
|
-
parts.push(stringToStyledText4("\u23CE create \xB7 e edit prompt \xB7 r repositories \xB7 esc back"));
|
|
37089
|
+
parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(branchOverrides.length > 0 ? "Remy will continue on the selected existing branch and update its pull request; you land in the session view." : "Remy will clone the selected repositories, work on new session branches, and open a pull request for each repository; you land in the session view."))]));
|
|
37090
|
+
parts.push(stringToStyledText4(branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch") ? "\u23CE create \xB7 e edit prompt \xB7 r repositories \xB7 b branches \xB7 esc back" : "\u23CE create \xB7 e edit prompt \xB7 r repositories \xB7 esc back"));
|
|
36436
37091
|
if (status)
|
|
36437
37092
|
parts.push(stringToStyledText4(status));
|
|
37093
|
+
if (attachmentStatus)
|
|
37094
|
+
parts.push(attachmentStatus);
|
|
36438
37095
|
content.content = orient(joinStyled(parts, `
|
|
36439
37096
|
|
|
36440
37097
|
`));
|
|
@@ -36459,7 +37116,7 @@ async function createNewSessionWizard({
|
|
|
36459
37116
|
syncSuggestionLoadingIndicator();
|
|
36460
37117
|
renderer.requestRender();
|
|
36461
37118
|
}, syncSuggestionLoadingIndicator = function() {
|
|
36462
|
-
if (destroyed || settled || step !== "loadingSuggestions") {
|
|
37119
|
+
if (destroyed || settled || step !== "loadingSuggestions" && step !== "loadingBranches") {
|
|
36463
37120
|
if (suggestionSpinnerTimer) {
|
|
36464
37121
|
clearInterval(suggestionSpinnerTimer);
|
|
36465
37122
|
suggestionSpinnerTimer = undefined;
|
|
@@ -36470,22 +37127,28 @@ async function createNewSessionWizard({
|
|
|
36470
37127
|
if (suggestionSpinnerTimer)
|
|
36471
37128
|
return;
|
|
36472
37129
|
suggestionSpinnerTimer = setInterval(() => {
|
|
36473
|
-
if (destroyed || settled || step !== "loadingSuggestions") {
|
|
37130
|
+
if (destroyed || settled || step !== "loadingSuggestions" && step !== "loadingBranches") {
|
|
36474
37131
|
syncSuggestionLoadingIndicator();
|
|
36475
37132
|
return;
|
|
36476
37133
|
}
|
|
36477
37134
|
suggestionSpinnerFrame = (suggestionSpinnerFrame + 1) % suggestionSpinnerFrames.length;
|
|
36478
37135
|
render();
|
|
36479
37136
|
}, suggestionSpinnerIntervalMs);
|
|
37137
|
+
}, invalidatePendingConfirmation = function() {
|
|
37138
|
+
pendingConfirmation = undefined;
|
|
37139
|
+
if (attachmentFeedback?.kind === "progress")
|
|
37140
|
+
attachmentFeedback = undefined;
|
|
36480
37141
|
}, finish = function(value) {
|
|
36481
37142
|
if (settled)
|
|
36482
37143
|
return;
|
|
37144
|
+
invalidatePendingConfirmation();
|
|
36483
37145
|
settled = true;
|
|
36484
37146
|
syncSuggestionLoadingIndicator();
|
|
36485
37147
|
resolveDraft(value);
|
|
36486
37148
|
}, fail = function(error93) {
|
|
36487
37149
|
if (settled)
|
|
36488
37150
|
return;
|
|
37151
|
+
invalidatePendingConfirmation();
|
|
36489
37152
|
settled = true;
|
|
36490
37153
|
syncSuggestionLoadingIndicator();
|
|
36491
37154
|
rejectDraft(error93);
|
|
@@ -36556,7 +37219,15 @@ async function createNewSessionWizard({
|
|
|
36556
37219
|
returnToPromptFromRepositoryLoading();
|
|
36557
37220
|
return;
|
|
36558
37221
|
}
|
|
37222
|
+
if (step === "loadingBranches" || step === "branches") {
|
|
37223
|
+
branchRequestGeneration += 1;
|
|
37224
|
+
step = "repositories";
|
|
37225
|
+
render();
|
|
37226
|
+
return;
|
|
37227
|
+
}
|
|
36559
37228
|
if (step === "recompute" || step === "confirmation") {
|
|
37229
|
+
if (step === "confirmation")
|
|
37230
|
+
invalidatePendingConfirmation();
|
|
36560
37231
|
step = "repositories";
|
|
36561
37232
|
editor.setText("");
|
|
36562
37233
|
render();
|
|
@@ -36584,6 +37255,15 @@ async function createNewSessionWizard({
|
|
|
36584
37255
|
const completedText = replaceActivePathToken({ text: editor.plainText, replacement: completion.replacement });
|
|
36585
37256
|
editor.setText(completedText);
|
|
36586
37257
|
editor.cursorOffset = completedText.length;
|
|
37258
|
+
if (completion.attachmentPath) {
|
|
37259
|
+
const selectedCompletion = createSelectedPathCompletion({
|
|
37260
|
+
attachmentPath: completion.attachmentPath,
|
|
37261
|
+
token: completion.replacement,
|
|
37262
|
+
end: completedText.length
|
|
37263
|
+
});
|
|
37264
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
37265
|
+
}
|
|
37266
|
+
previousPromptEditorText = completedText;
|
|
36587
37267
|
}
|
|
36588
37268
|
pathCompletions = [];
|
|
36589
37269
|
pathCompletionMenuOpen = false;
|
|
@@ -36609,12 +37289,14 @@ async function createNewSessionWizard({
|
|
|
36609
37289
|
if (step === "confirmation") {
|
|
36610
37290
|
if (key.name === "m") {
|
|
36611
37291
|
key.preventDefault();
|
|
37292
|
+
invalidatePendingConfirmation();
|
|
36612
37293
|
model = cycle({ values: newSessionModels, value: model, direction: 1 });
|
|
36613
37294
|
render();
|
|
36614
37295
|
return;
|
|
36615
37296
|
}
|
|
36616
37297
|
if (key.name === "left" || key.name === "right") {
|
|
36617
37298
|
key.preventDefault();
|
|
37299
|
+
invalidatePendingConfirmation();
|
|
36618
37300
|
reasoningEffort = clampedStep({
|
|
36619
37301
|
values: newSessionReasoningEfforts,
|
|
36620
37302
|
value: reasoningEffort,
|
|
@@ -36625,6 +37307,7 @@ async function createNewSessionWizard({
|
|
|
36625
37307
|
}
|
|
36626
37308
|
if (key.name === "e") {
|
|
36627
37309
|
key.preventDefault();
|
|
37310
|
+
invalidatePendingConfirmation();
|
|
36628
37311
|
step = "prompt";
|
|
36629
37312
|
editor.setText(prompt);
|
|
36630
37313
|
render();
|
|
@@ -36632,11 +37315,19 @@ async function createNewSessionWizard({
|
|
|
36632
37315
|
}
|
|
36633
37316
|
if (key.name === "r") {
|
|
36634
37317
|
key.preventDefault();
|
|
37318
|
+
invalidatePendingConfirmation();
|
|
36635
37319
|
step = "repositories";
|
|
36636
37320
|
editor.setText("");
|
|
36637
37321
|
render();
|
|
36638
37322
|
return;
|
|
36639
37323
|
}
|
|
37324
|
+
if (key.name === "b" && branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch")) {
|
|
37325
|
+
key.preventDefault();
|
|
37326
|
+
invalidatePendingConfirmation();
|
|
37327
|
+
step = "branches";
|
|
37328
|
+
render();
|
|
37329
|
+
return;
|
|
37330
|
+
}
|
|
36640
37331
|
}
|
|
36641
37332
|
if (step === "repositorySearch")
|
|
36642
37333
|
return;
|
|
@@ -36657,8 +37348,16 @@ async function createNewSessionWizard({
|
|
|
36657
37348
|
return;
|
|
36658
37349
|
}
|
|
36659
37350
|
}
|
|
37351
|
+
if (step === "branches" && branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch") && (key.name === "up" || key.name === "down" || key.name === "left" || key.name === "right")) {
|
|
37352
|
+
key.preventDefault();
|
|
37353
|
+
branchChoice = branchChoice === "existing" ? "new" : "existing";
|
|
37354
|
+
render();
|
|
37355
|
+
return;
|
|
37356
|
+
}
|
|
36660
37357
|
if (!composerMounted && (key.name === "return" || key.name === "enter")) {
|
|
36661
37358
|
key.preventDefault();
|
|
37359
|
+
if (step === "loadingBranches")
|
|
37360
|
+
return;
|
|
36662
37361
|
submitEditor();
|
|
36663
37362
|
return;
|
|
36664
37363
|
}
|
|
@@ -36747,6 +37446,7 @@ async function createNewSessionWizard({
|
|
|
36747
37446
|
let repositoryLoadState = Array.isArray(repositories) ? "ready" : "loading";
|
|
36748
37447
|
let repositoryRequestGeneration = 0;
|
|
36749
37448
|
let suggestionRequestGeneration = 0;
|
|
37449
|
+
let branchRequestGeneration = 0;
|
|
36750
37450
|
let installationIds = [...new Set(loadedRepositories.map((repository) => repository.githubInstallationId))].sort((left, right) => left.localeCompare(right));
|
|
36751
37451
|
let step = initialDraft ? "confirmation" : "prompt";
|
|
36752
37452
|
let prompt = initialDraft?.prompt ?? "";
|
|
@@ -36758,7 +37458,9 @@ async function createNewSessionWizard({
|
|
|
36758
37458
|
let recomputeInstruction = "";
|
|
36759
37459
|
let suggestionSpinnerFrame = 0;
|
|
36760
37460
|
let attachments = [...initialDraft?.attachments ?? []];
|
|
37461
|
+
let previousPromptEditorText = "";
|
|
36761
37462
|
let pathCompletions = [];
|
|
37463
|
+
let selectedPathCompletions = [];
|
|
36762
37464
|
let completionIndex = 0;
|
|
36763
37465
|
let pathCompletionMenuOpen = false;
|
|
36764
37466
|
let repositoryHeaderHeight;
|
|
@@ -36767,8 +37469,17 @@ async function createNewSessionWizard({
|
|
|
36767
37469
|
let searchSelections;
|
|
36768
37470
|
let suggestedIds = new Set;
|
|
36769
37471
|
let suggestionConfidence;
|
|
37472
|
+
let branchSuggestions = initialDraft?.repositoryBranchOverrides?.map((override) => ({
|
|
37473
|
+
suggestionKind: "continueExistingBranch",
|
|
37474
|
+
repositoryId: override.repositoryId,
|
|
37475
|
+
branchName: override.branchName,
|
|
37476
|
+
pullRequest: null
|
|
37477
|
+
})) ?? [];
|
|
37478
|
+
let branchChoice = initialDraft?.repositoryBranchOverrides?.length ? "existing" : "new";
|
|
36770
37479
|
let status = initialError ?? "";
|
|
37480
|
+
let attachmentFeedback;
|
|
36771
37481
|
let pendingPastedImageWrites = 0;
|
|
37482
|
+
let pendingConfirmation;
|
|
36772
37483
|
let destroyed = false;
|
|
36773
37484
|
let rendererDestroyPromise;
|
|
36774
37485
|
let settled = false;
|
|
@@ -36791,6 +37502,14 @@ async function createNewSessionWizard({
|
|
|
36791
37502
|
submitEditor();
|
|
36792
37503
|
},
|
|
36793
37504
|
onContentChange: () => {
|
|
37505
|
+
if (step === "prompt") {
|
|
37506
|
+
selectedPathCompletions = reconcileSelectedPathCompletions({
|
|
37507
|
+
previousText: previousPromptEditorText,
|
|
37508
|
+
text: editor.plainText,
|
|
37509
|
+
selections: selectedPathCompletions
|
|
37510
|
+
});
|
|
37511
|
+
previousPromptEditorText = editor.plainText;
|
|
37512
|
+
}
|
|
36794
37513
|
pathCompletionMenuOpen = false;
|
|
36795
37514
|
if (step === "repositorySearch") {
|
|
36796
37515
|
repositoryQuery = editor.plainText;
|
|
@@ -36826,6 +37545,35 @@ async function createNewSessionWizard({
|
|
|
36826
37545
|
status = error93 instanceof Error ? error93.message : String(error93);
|
|
36827
37546
|
}
|
|
36828
37547
|
}
|
|
37548
|
+
async function startBranchReview() {
|
|
37549
|
+
const installationId = selectedInstallationId();
|
|
37550
|
+
if (!installationId)
|
|
37551
|
+
return;
|
|
37552
|
+
const repositoryIds = [...selectedRepositoryIds()];
|
|
37553
|
+
const requestGeneration = ++branchRequestGeneration;
|
|
37554
|
+
branchSuggestions = [];
|
|
37555
|
+
branchChoice = "new";
|
|
37556
|
+
step = "loadingBranches";
|
|
37557
|
+
status = "";
|
|
37558
|
+
render();
|
|
37559
|
+
try {
|
|
37560
|
+
const suggestions = await suggestBranches({ installationId, prompt, repositoryIds });
|
|
37561
|
+
if (destroyed || requestGeneration !== branchRequestGeneration || step !== "loadingBranches")
|
|
37562
|
+
return;
|
|
37563
|
+
branchSuggestions = suggestions;
|
|
37564
|
+
const hasExistingBranch = branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch");
|
|
37565
|
+
branchChoice = hasExistingBranch ? "existing" : "new";
|
|
37566
|
+
step = hasExistingBranch ? "branches" : "confirmation";
|
|
37567
|
+
} catch (error93) {
|
|
37568
|
+
if (destroyed || requestGeneration !== branchRequestGeneration || step !== "loadingBranches")
|
|
37569
|
+
return;
|
|
37570
|
+
branchSuggestions = [];
|
|
37571
|
+
branchChoice = "new";
|
|
37572
|
+
status = error93 instanceof Error ? error93.message : String(error93);
|
|
37573
|
+
step = "confirmation";
|
|
37574
|
+
}
|
|
37575
|
+
render();
|
|
37576
|
+
}
|
|
36829
37577
|
async function openPathCompletionMenu() {
|
|
36830
37578
|
if (destroyed)
|
|
36831
37579
|
return;
|
|
@@ -36857,28 +37605,33 @@ async function createNewSessionWizard({
|
|
|
36857
37605
|
}
|
|
36858
37606
|
async function pasteClipboardImage() {
|
|
36859
37607
|
if (!readClipboardImage || !savePastedImage) {
|
|
36860
|
-
|
|
37608
|
+
attachmentFeedback = { kind: "failure", message: "Clipboard image paste is unavailable." };
|
|
36861
37609
|
render();
|
|
36862
37610
|
return;
|
|
36863
37611
|
}
|
|
36864
37612
|
pendingPastedImageWrites += 1;
|
|
36865
|
-
|
|
37613
|
+
attachmentFeedback = { kind: "progress", message: "Saving clipboard image\u2026" };
|
|
36866
37614
|
render();
|
|
36867
37615
|
try {
|
|
36868
37616
|
const image = await readClipboardImage();
|
|
36869
37617
|
const path = await savePastedImage(image);
|
|
36870
37618
|
if (!destroyed) {
|
|
36871
37619
|
editor.insertText(path);
|
|
36872
|
-
|
|
37620
|
+
const selectedCompletion = createSelectedPathCompletion({ attachmentPath: path, token: path, end: editor.cursorOffset });
|
|
37621
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
37622
|
+
previousPromptEditorText = editor.plainText;
|
|
37623
|
+
attachmentFeedback = { kind: "success", message: "Clipboard image ready to attach." };
|
|
36873
37624
|
}
|
|
36874
37625
|
} catch (error93) {
|
|
36875
|
-
|
|
37626
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
36876
37627
|
} finally {
|
|
36877
37628
|
pendingPastedImageWrites -= 1;
|
|
36878
37629
|
}
|
|
36879
37630
|
render();
|
|
36880
37631
|
}
|
|
36881
37632
|
async function confirm() {
|
|
37633
|
+
if (destroyed || settled || step !== "confirmation" || pendingConfirmation)
|
|
37634
|
+
return;
|
|
36882
37635
|
const installationId = selectedInstallationId();
|
|
36883
37636
|
if (!installationId) {
|
|
36884
37637
|
status = "GitHub installation selection is required.";
|
|
@@ -36890,22 +37643,55 @@ async function createNewSessionWizard({
|
|
|
36890
37643
|
fail(new Error("Selected repositories must belong to the chosen GitHub installation."));
|
|
36891
37644
|
return;
|
|
36892
37645
|
}
|
|
37646
|
+
const repositoryBranchOverrides = branchChoice === "existing" ? branchSuggestions.flatMap((suggestion) => suggestion.suggestionKind === "createSessionBranch" ? [] : [{ repositoryId: suggestion.repositoryId, branchName: suggestion.branchName }]) : [];
|
|
37647
|
+
const confirmation = Symbol("confirmation");
|
|
37648
|
+
const confirmedAttachments = [...attachments];
|
|
37649
|
+
const confirmedPrompt = prompt;
|
|
37650
|
+
const confirmedModel = model;
|
|
37651
|
+
const confirmedReasoningEffort = reasoningEffort;
|
|
37652
|
+
const confirmedSelectedPaths = selectedAttachmentPaths({ text: confirmedPrompt, selections: selectedPathCompletions });
|
|
37653
|
+
pendingConfirmation = confirmation;
|
|
37654
|
+
const ownsConfirmation = () => !destroyed && !settled && step === "confirmation" && pendingConfirmation === confirmation;
|
|
36893
37655
|
if (attachPromptPaths) {
|
|
36894
|
-
|
|
36895
|
-
render();
|
|
37656
|
+
attachmentFeedback = undefined;
|
|
36896
37657
|
try {
|
|
36897
37658
|
const promptAttachments = await attachPromptPaths({
|
|
36898
|
-
text:
|
|
36899
|
-
excludedPaths:
|
|
37659
|
+
text: confirmedPrompt,
|
|
37660
|
+
excludedPaths: confirmedAttachments.flatMap((attachment) => attachment.sourcePath ? [attachment.sourcePath] : []),
|
|
37661
|
+
selectedPaths: confirmedSelectedPaths,
|
|
37662
|
+
onUploadStart: ({ filenames }) => {
|
|
37663
|
+
if (!ownsConfirmation())
|
|
37664
|
+
return;
|
|
37665
|
+
attachmentFeedback = { kind: "progress", message: attachmentUploadMessage(filenames) };
|
|
37666
|
+
render();
|
|
37667
|
+
}
|
|
36900
37668
|
});
|
|
36901
|
-
|
|
37669
|
+
if (!ownsConfirmation())
|
|
37670
|
+
return;
|
|
37671
|
+
confirmedAttachments.push(...promptAttachments);
|
|
37672
|
+
attachments = confirmedAttachments;
|
|
37673
|
+
selectedPathCompletions = [];
|
|
37674
|
+
attachmentFeedback = undefined;
|
|
36902
37675
|
} catch (error93) {
|
|
36903
|
-
|
|
37676
|
+
if (!ownsConfirmation())
|
|
37677
|
+
return;
|
|
37678
|
+
pendingConfirmation = undefined;
|
|
37679
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
36904
37680
|
render();
|
|
36905
37681
|
return;
|
|
36906
37682
|
}
|
|
36907
37683
|
}
|
|
36908
|
-
|
|
37684
|
+
if (!ownsConfirmation())
|
|
37685
|
+
return;
|
|
37686
|
+
finish({
|
|
37687
|
+
prompt: confirmedPrompt,
|
|
37688
|
+
githubInstallationId: installationId,
|
|
37689
|
+
repositories: selected,
|
|
37690
|
+
...repositoryBranchOverrides.length > 0 ? { repositoryBranchOverrides } : {},
|
|
37691
|
+
attachments: confirmedAttachments,
|
|
37692
|
+
model: confirmedModel,
|
|
37693
|
+
reasoningEffort: confirmedReasoningEffort
|
|
37694
|
+
});
|
|
36909
37695
|
}
|
|
36910
37696
|
async function submitEditor() {
|
|
36911
37697
|
const value = editor.plainText.trim();
|
|
@@ -36939,8 +37725,12 @@ async function createNewSessionWizard({
|
|
|
36939
37725
|
return;
|
|
36940
37726
|
}
|
|
36941
37727
|
if (step === "repositories") {
|
|
36942
|
-
step = "confirmation";
|
|
36943
37728
|
editor.setText("");
|
|
37729
|
+
startBranchReview();
|
|
37730
|
+
return;
|
|
37731
|
+
}
|
|
37732
|
+
if (step === "branches") {
|
|
37733
|
+
step = "confirmation";
|
|
36944
37734
|
render();
|
|
36945
37735
|
return;
|
|
36946
37736
|
}
|
|
@@ -37004,6 +37794,7 @@ async function createNewSessionWizard({
|
|
|
37004
37794
|
destroy() {
|
|
37005
37795
|
if (rendererDestroyed2)
|
|
37006
37796
|
return;
|
|
37797
|
+
invalidatePendingConfirmation();
|
|
37007
37798
|
destroyed = true;
|
|
37008
37799
|
if (inputHandler)
|
|
37009
37800
|
renderer.removeInputHandler(inputHandler);
|
|
@@ -37040,12 +37831,13 @@ async function createNewSessionWizard({
|
|
|
37040
37831
|
}
|
|
37041
37832
|
}
|
|
37042
37833
|
function stepHeader(title) {
|
|
37043
|
-
return bold3(
|
|
37834
|
+
return bold3(fg6(PALETTE.remyAccent)(title));
|
|
37044
37835
|
}
|
|
37045
37836
|
var WIZARD_STEPS = [
|
|
37046
37837
|
{ key: "describe", label: "Describe" },
|
|
37047
37838
|
{ key: "installation", label: "Installation" },
|
|
37048
37839
|
{ key: "repositories", label: "Repositories" },
|
|
37840
|
+
{ key: "branches", label: "Branches" },
|
|
37049
37841
|
{ key: "confirm", label: "Confirm" }
|
|
37050
37842
|
];
|
|
37051
37843
|
function wizardStepKey(step) {
|
|
@@ -37053,52 +37845,41 @@ function wizardStepKey(step) {
|
|
|
37053
37845
|
return "describe";
|
|
37054
37846
|
if (step === "installation")
|
|
37055
37847
|
return "installation";
|
|
37848
|
+
if (step === "loadingBranches" || step === "branches")
|
|
37849
|
+
return "branches";
|
|
37056
37850
|
if (step === "confirmation")
|
|
37057
37851
|
return "confirm";
|
|
37058
37852
|
return "repositories";
|
|
37059
37853
|
}
|
|
37060
|
-
function renderStepIndicator({ step, hasInstallationChoice }) {
|
|
37061
|
-
const steps = WIZARD_STEPS.filter((entry) => entry.key !== "installation" || hasInstallationChoice);
|
|
37854
|
+
function renderStepIndicator({ step, hasInstallationChoice, hasBranchChoice = false }) {
|
|
37855
|
+
const steps = WIZARD_STEPS.filter((entry) => (entry.key !== "installation" || hasInstallationChoice) && (entry.key !== "branches" || hasBranchChoice));
|
|
37062
37856
|
const currentKey = wizardStepKey(step);
|
|
37063
37857
|
const currentIndex = steps.findIndex((entry) => entry.key === currentKey);
|
|
37064
37858
|
const position = currentIndex >= 0 ? currentIndex + 1 : steps.length;
|
|
37065
37859
|
const crumbs = [];
|
|
37066
37860
|
steps.forEach((entry, index) => {
|
|
37067
37861
|
if (index > 0)
|
|
37068
|
-
crumbs.push(
|
|
37069
|
-
crumbs.push(entry.key === currentKey ? bold3(
|
|
37862
|
+
crumbs.push(fg6(PALETTE.dimText)(" \u203A "));
|
|
37863
|
+
crumbs.push(entry.key === currentKey ? bold3(fg6(PALETTE.bodyText)(entry.label)) : fg6(PALETTE.dimText)(entry.label));
|
|
37070
37864
|
});
|
|
37071
|
-
return new
|
|
37072
|
-
bold3(
|
|
37073
|
-
|
|
37865
|
+
return new StyledText5([
|
|
37866
|
+
bold3(fg6(PALETTE.remyAccent)(`Step ${position} of ${steps.length}`)),
|
|
37867
|
+
fg6(PALETTE.dimText)(" "),
|
|
37074
37868
|
...crumbs
|
|
37075
37869
|
]);
|
|
37076
37870
|
}
|
|
37077
37871
|
function renderSelectableRow({ label, isCursor }) {
|
|
37078
37872
|
const line = `${isCursor ? "> " : " "}${label}`;
|
|
37079
|
-
const styled =
|
|
37873
|
+
const styled = fg6(PALETTE.bodyText)(line);
|
|
37080
37874
|
return [isCursor ? bg3(PALETTE.selectionBg)(styled) : styled];
|
|
37081
37875
|
}
|
|
37082
37876
|
function renderRepositoryRow({ repository, isCursor, isChecked, source }) {
|
|
37083
37877
|
const marker = isChecked ? "[x]" : "[ ]";
|
|
37084
37878
|
const line = `${isCursor ? "> " : " "}${marker} ${repository.fullName}`;
|
|
37085
|
-
const styled = isChecked ? bold3(
|
|
37086
|
-
const tag = source === "suggested" && isChecked ?
|
|
37879
|
+
const styled = isChecked ? bold3(fg6(PALETTE.humanAccent)(line)) : fg6(PALETTE.dimText)(line);
|
|
37880
|
+
const tag = source === "suggested" && isChecked ? dim4(fg6(PALETTE.tool)(" \u25C6 suggested by Remy")) : undefined;
|
|
37087
37881
|
return [isCursor ? bg3(PALETTE.selectionBg)(styled) : styled, ...tag ? [tag] : []];
|
|
37088
37882
|
}
|
|
37089
|
-
function fuzzyMatches(value, query) {
|
|
37090
|
-
const normalizedQuery = query.replaceAll(/\s/g, "").toLowerCase();
|
|
37091
|
-
if (!normalizedQuery)
|
|
37092
|
-
return true;
|
|
37093
|
-
let queryIndex = 0;
|
|
37094
|
-
for (const character of value.toLowerCase()) {
|
|
37095
|
-
if (character === normalizedQuery[queryIndex])
|
|
37096
|
-
queryIndex += 1;
|
|
37097
|
-
if (queryIndex === normalizedQuery.length)
|
|
37098
|
-
return true;
|
|
37099
|
-
}
|
|
37100
|
-
return false;
|
|
37101
|
-
}
|
|
37102
37883
|
function cycle({ values, value, direction }) {
|
|
37103
37884
|
const currentIndex = values.indexOf(value);
|
|
37104
37885
|
const nextIndex = (currentIndex + direction + values.length) % values.length;
|
|
@@ -37114,7 +37895,7 @@ async function createDefaultRenderer2() {
|
|
|
37114
37895
|
}
|
|
37115
37896
|
|
|
37116
37897
|
// src/tui/session-view.ts
|
|
37117
|
-
import { CliRenderEvents as CliRenderEvents4, BoxRenderable as BoxRenderable4, bg as bg4, bold as bold4, dim as
|
|
37898
|
+
import { CliRenderEvents as CliRenderEvents4, BoxRenderable as BoxRenderable4, bg as bg4, bold as bold4, dim as dim5, fg as fg7, ScrollBoxRenderable as ScrollBoxRenderable3, StyledText as StyledText6, stringToStyledText as stringToStyledText5, TextRenderable as TextRenderable4 } from "@opentui/core";
|
|
37118
37899
|
var composerSlashCommands = [
|
|
37119
37900
|
{ value: "/sessions", description: "Back to the session list" },
|
|
37120
37901
|
{ value: "/complete", description: "Finish this session" },
|
|
@@ -37195,6 +37976,7 @@ async function createSessionTui({
|
|
|
37195
37976
|
}
|
|
37196
37977
|
promptHistoryIndex = direction === "up" ? Math.max(0, promptHistoryIndex - 1) : Math.min(promptHistory.length, promptHistoryIndex + 1);
|
|
37197
37978
|
const text = promptHistoryIndex === promptHistory.length ? promptHistoryDraft : promptHistory[promptHistoryIndex];
|
|
37979
|
+
selectedPathCompletions = [];
|
|
37198
37980
|
historyReplacement = text;
|
|
37199
37981
|
composer.setText(text);
|
|
37200
37982
|
composer.cursorOffset = text.length;
|
|
@@ -37217,10 +37999,10 @@ async function createSessionTui({
|
|
|
37217
37999
|
syncTerminalSessionControls();
|
|
37218
38000
|
syncWorkingIndicator();
|
|
37219
38001
|
renderRetainedTranscript();
|
|
37220
|
-
const assistantPreview = latestState.previews.assistantText ? new
|
|
37221
|
-
`)),
|
|
37222
|
-
const reasoningPreview = latestState.previews.reasoningText ? new
|
|
37223
|
-
`)),
|
|
38002
|
+
const assistantPreview = latestState.previews.assistantText ? new StyledText6([dim5(fg7(PALETTE.dimText)(`Live assistant preview
|
|
38003
|
+
`)), fg7(PALETTE.remyAccent)(latestState.previews.assistantText)]) : new StyledText6([]);
|
|
38004
|
+
const reasoningPreview = latestState.previews.reasoningText ? new StyledText6([dim5(fg7(PALETTE.dimText)(`Live reasoning preview
|
|
38005
|
+
`)), dim5(fg7(PALETTE.dimText)(latestState.previews.reasoningText))]) : new StyledText6([]);
|
|
37224
38006
|
const working = isRemyWorking(latestState);
|
|
37225
38007
|
const startedAt = workingTurnStartedAt(latestState);
|
|
37226
38008
|
const elapsedMs = startedAt === undefined ? 0 : Math.max(0, now3() - Date.parse(startedAt));
|
|
@@ -37229,7 +38011,7 @@ async function createSessionTui({
|
|
|
37229
38011
|
frame: workingSpinnerFrames[spinnerFrameIndex],
|
|
37230
38012
|
elapsedMs,
|
|
37231
38013
|
mode: stopState.kind === "stopping" ? "stopping" : "working"
|
|
37232
|
-
}) : new
|
|
38014
|
+
}) : new StyledText6([]);
|
|
37233
38015
|
const liveContent = [assistantPreview, reasoningPreview, liveIndicator].filter((part) => part.chunks.length > 0);
|
|
37234
38016
|
liveTranscript.content = liveContent.length === 0 ? "" : joinStyled([stringToStyledText5(`
|
|
37235
38017
|
|
|
@@ -37245,6 +38027,7 @@ async function createSessionTui({
|
|
|
37245
38027
|
const commandMenu = renderSlashCommandMenu({ commands: slashCompletions, selectedIndex: slashCompletionIndex });
|
|
37246
38028
|
const composerStatus = [
|
|
37247
38029
|
helpVisible ? renderHelpPanel() : undefined,
|
|
38030
|
+
attachmentFeedback ? renderAttachmentFeedback(attachmentFeedback) : undefined,
|
|
37248
38031
|
composerFeedback ? stringToStyledText5(composerFeedback) : undefined,
|
|
37249
38032
|
submissionStatus,
|
|
37250
38033
|
commandMenu,
|
|
@@ -37317,10 +38100,17 @@ async function createSessionTui({
|
|
|
37317
38100
|
}
|
|
37318
38101
|
slashCompletions = composerSlashCommands.filter((command) => command.value.startsWith(value) && command.value !== value && (!["/complete", "/cancel"].includes(command.value) || latestState.aggregateStatus === "open"));
|
|
37319
38102
|
slashCompletionIndex = 0;
|
|
38103
|
+
}, invalidateAttachmentPreparation = function() {
|
|
38104
|
+
attachmentPreparationOwner = undefined;
|
|
38105
|
+
}, ownsAttachmentPreparation = function(owner) {
|
|
38106
|
+
return attachmentPreparationOwner === owner && !destroyed && !settled;
|
|
38107
|
+
}, viewIsActive = function() {
|
|
38108
|
+
return !destroyed && !settled;
|
|
37320
38109
|
}, finish = function(result, error93) {
|
|
37321
38110
|
if (settled)
|
|
37322
38111
|
return;
|
|
37323
38112
|
settled = true;
|
|
38113
|
+
invalidateAttachmentPreparation();
|
|
37324
38114
|
if (error93)
|
|
37325
38115
|
rejectAction(error93);
|
|
37326
38116
|
else
|
|
@@ -37344,17 +38134,21 @@ async function createSessionTui({
|
|
|
37344
38134
|
let settled = false;
|
|
37345
38135
|
let activityExpanded = false;
|
|
37346
38136
|
let composerDraft = initialComposer ?? { text: "", attachments: [] };
|
|
38137
|
+
let previousComposerText = composerDraft.text;
|
|
37347
38138
|
let pathCompletions = [];
|
|
38139
|
+
let selectedPathCompletions = [];
|
|
37348
38140
|
let completionIndex = 0;
|
|
37349
38141
|
let pathCompletionMenuOpen = false;
|
|
37350
38142
|
let slashCompletions = [];
|
|
37351
38143
|
let slashCompletionIndex = 0;
|
|
37352
38144
|
let composerFeedback = initialFeedback;
|
|
38145
|
+
let attachmentFeedback;
|
|
37353
38146
|
const localPromptHistory = [];
|
|
37354
38147
|
let promptHistoryIndex;
|
|
37355
38148
|
let promptHistoryDraft = "";
|
|
37356
38149
|
let historyReplacement;
|
|
37357
38150
|
let pendingPastedImageWrites = 0;
|
|
38151
|
+
let attachmentPreparationOwner;
|
|
37358
38152
|
let admittedSubmissions = [];
|
|
37359
38153
|
let stopState = { kind: "idle" };
|
|
37360
38154
|
let lifecycleRequestState = { kind: "idle" };
|
|
@@ -37393,6 +38187,12 @@ async function createSessionTui({
|
|
|
37393
38187
|
submitComposer();
|
|
37394
38188
|
},
|
|
37395
38189
|
onContentChange: () => {
|
|
38190
|
+
selectedPathCompletions = reconcileSelectedPathCompletions({
|
|
38191
|
+
previousText: previousComposerText,
|
|
38192
|
+
text: composer.plainText,
|
|
38193
|
+
selections: selectedPathCompletions
|
|
38194
|
+
});
|
|
38195
|
+
previousComposerText = composer.plainText;
|
|
37396
38196
|
if (historyReplacement !== composer.plainText)
|
|
37397
38197
|
promptHistoryIndex = undefined;
|
|
37398
38198
|
historyReplacement = undefined;
|
|
@@ -37473,12 +38273,12 @@ async function createSessionTui({
|
|
|
37473
38273
|
};
|
|
37474
38274
|
async function pasteClipboardImage() {
|
|
37475
38275
|
if (!readClipboardImage || !savePastedImage) {
|
|
37476
|
-
|
|
38276
|
+
attachmentFeedback = { kind: "failure", message: "Clipboard image paste is unavailable." };
|
|
37477
38277
|
render();
|
|
37478
38278
|
return;
|
|
37479
38279
|
}
|
|
37480
38280
|
pendingPastedImageWrites += 1;
|
|
37481
|
-
|
|
38281
|
+
attachmentFeedback = { kind: "progress", message: "Saving clipboard image\u2026" };
|
|
37482
38282
|
render();
|
|
37483
38283
|
try {
|
|
37484
38284
|
const image = await readClipboardImage();
|
|
@@ -37486,10 +38286,13 @@ async function createSessionTui({
|
|
|
37486
38286
|
if (destroyed)
|
|
37487
38287
|
return;
|
|
37488
38288
|
composer.insertText(path);
|
|
37489
|
-
|
|
38289
|
+
const selectedCompletion = createSelectedPathCompletion({ attachmentPath: path, token: path, end: composer.cursorOffset });
|
|
38290
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
38291
|
+
previousComposerText = composer.plainText;
|
|
38292
|
+
attachmentFeedback = { kind: "success", message: "Clipboard image ready to attach." };
|
|
37490
38293
|
} catch (error93) {
|
|
37491
38294
|
if (!destroyed)
|
|
37492
|
-
|
|
38295
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
37493
38296
|
} finally {
|
|
37494
38297
|
pendingPastedImageWrites -= 1;
|
|
37495
38298
|
if (!destroyed)
|
|
@@ -37549,6 +38352,15 @@ async function createSessionTui({
|
|
|
37549
38352
|
const completedText = replaceActivePathToken({ text: composer.plainText, replacement: completion.replacement });
|
|
37550
38353
|
composer.setText(completedText);
|
|
37551
38354
|
composer.cursorOffset = completedText.length;
|
|
38355
|
+
if (completion.attachmentPath) {
|
|
38356
|
+
const selectedCompletion = createSelectedPathCompletion({
|
|
38357
|
+
attachmentPath: completion.attachmentPath,
|
|
38358
|
+
token: completion.replacement,
|
|
38359
|
+
end: completedText.length
|
|
38360
|
+
});
|
|
38361
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
38362
|
+
}
|
|
38363
|
+
previousComposerText = completedText;
|
|
37552
38364
|
}
|
|
37553
38365
|
pathCompletions = [];
|
|
37554
38366
|
pathCompletionMenuOpen = false;
|
|
@@ -37579,8 +38391,10 @@ async function createSessionTui({
|
|
|
37579
38391
|
render();
|
|
37580
38392
|
}
|
|
37581
38393
|
async function submitComposer() {
|
|
38394
|
+
if (attachmentPreparationOwner)
|
|
38395
|
+
return;
|
|
37582
38396
|
if (pendingPastedImageWrites > 0) {
|
|
37583
|
-
|
|
38397
|
+
attachmentFeedback = { kind: "progress", message: "Saving pasted image\u2026" };
|
|
37584
38398
|
render();
|
|
37585
38399
|
return;
|
|
37586
38400
|
}
|
|
@@ -37588,33 +38402,61 @@ async function createSessionTui({
|
|
|
37588
38402
|
const value = composerDraft.text;
|
|
37589
38403
|
if (value.trim() === "")
|
|
37590
38404
|
return;
|
|
37591
|
-
|
|
38405
|
+
const preparationOwner = Symbol("attachment preparation");
|
|
38406
|
+
attachmentPreparationOwner = preparationOwner;
|
|
38407
|
+
if (await handleComposerCommand(value)) {
|
|
38408
|
+
if (attachmentPreparationOwner === preparationOwner)
|
|
38409
|
+
invalidateAttachmentPreparation();
|
|
38410
|
+
return;
|
|
38411
|
+
}
|
|
38412
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
37592
38413
|
return;
|
|
37593
38414
|
if (lifecycleRequestState.kind === "pending") {
|
|
37594
38415
|
composerFeedback = `${lifecycleOperationPresentParticiple(lifecycleRequestState.operation)} the session \u2014 messages are paused.`;
|
|
37595
38416
|
render();
|
|
38417
|
+
invalidateAttachmentPreparation();
|
|
37596
38418
|
return;
|
|
37597
38419
|
}
|
|
37598
38420
|
if (latestState.aggregateStatus !== "open") {
|
|
37599
38421
|
composerFeedback = "This session is already terminal. Run /sessions to return to the session list.";
|
|
37600
38422
|
render();
|
|
38423
|
+
invalidateAttachmentPreparation();
|
|
37601
38424
|
return;
|
|
37602
38425
|
}
|
|
37603
38426
|
if (attachPromptPaths) {
|
|
37604
|
-
|
|
37605
|
-
render();
|
|
38427
|
+
let handedOffToAdmission = false;
|
|
37606
38428
|
try {
|
|
37607
38429
|
const attachments = await attachPromptPaths({
|
|
37608
38430
|
text: composerDraft.text,
|
|
37609
|
-
excludedPaths: composerDraft.attachments.flatMap((attachment) => attachment.sourcePath ? [attachment.sourcePath] : [])
|
|
38431
|
+
excludedPaths: composerDraft.attachments.flatMap((attachment) => attachment.sourcePath ? [attachment.sourcePath] : []),
|
|
38432
|
+
selectedPaths: selectedAttachmentPaths({ text: composerDraft.text, selections: selectedPathCompletions }),
|
|
38433
|
+
onUploadStart: ({ filenames }) => {
|
|
38434
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38435
|
+
return;
|
|
38436
|
+
attachmentFeedback = { kind: "progress", message: attachmentUploadMessage(filenames) };
|
|
38437
|
+
render();
|
|
38438
|
+
}
|
|
37610
38439
|
});
|
|
38440
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38441
|
+
return;
|
|
37611
38442
|
composerDraft = { ...composerDraft, attachments: [...composerDraft.attachments, ...attachments] };
|
|
38443
|
+
selectedPathCompletions = [];
|
|
38444
|
+
attachmentFeedback = undefined;
|
|
38445
|
+
handedOffToAdmission = true;
|
|
37612
38446
|
} catch (error93) {
|
|
37613
|
-
|
|
38447
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38448
|
+
return;
|
|
38449
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
37614
38450
|
render();
|
|
37615
38451
|
return;
|
|
38452
|
+
} finally {
|
|
38453
|
+
if (!handedOffToAdmission && attachmentPreparationOwner === preparationOwner)
|
|
38454
|
+
invalidateAttachmentPreparation();
|
|
37616
38455
|
}
|
|
37617
38456
|
}
|
|
38457
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38458
|
+
return;
|
|
38459
|
+
invalidateAttachmentPreparation();
|
|
37618
38460
|
const submission = {
|
|
37619
38461
|
id: crypto.randomUUID(),
|
|
37620
38462
|
idempotencyKey: crypto.randomUUID(),
|
|
@@ -37651,13 +38493,13 @@ async function createSessionTui({
|
|
|
37651
38493
|
if (command === "/complete") {
|
|
37652
38494
|
composer.setText("");
|
|
37653
38495
|
composerDraft = { ...composerDraft, text: "" };
|
|
37654
|
-
|
|
38496
|
+
handleLifecycleRequest("complete");
|
|
37655
38497
|
return true;
|
|
37656
38498
|
}
|
|
37657
38499
|
if (command === "/cancel") {
|
|
37658
38500
|
composer.setText("");
|
|
37659
38501
|
composerDraft = { ...composerDraft, text: "" };
|
|
37660
|
-
|
|
38502
|
+
handleLifecycleRequest("cancel");
|
|
37661
38503
|
return true;
|
|
37662
38504
|
}
|
|
37663
38505
|
if (command === "/logout") {
|
|
@@ -37685,16 +38527,22 @@ async function createSessionTui({
|
|
|
37685
38527
|
attachments: submission.attachments,
|
|
37686
38528
|
idempotencyKey: submission.idempotencyKey
|
|
37687
38529
|
});
|
|
38530
|
+
if (!viewIsActive())
|
|
38531
|
+
return;
|
|
37688
38532
|
localPromptHistory.push(submission.text);
|
|
37689
38533
|
const messageId = latestState.activeMessageId;
|
|
37690
38534
|
admittedSubmissions = messageId ? admittedSubmissions.map((candidate) => candidate.id === submission.id ? { id: candidate.id, idempotencyKey: candidate.idempotencyKey, text: candidate.text, attachments: candidate.attachments, status: "submitted", messageId } : candidate) : admittedSubmissions.filter((candidate) => candidate.id !== submission.id);
|
|
37691
38535
|
composerFeedback = undefined;
|
|
37692
38536
|
} catch (error93) {
|
|
38537
|
+
if (!viewIsActive())
|
|
38538
|
+
return;
|
|
37693
38539
|
admittedSubmissions = admittedSubmissions.map((candidate) => candidate.id === submission.id ? { id: candidate.id, idempotencyKey: candidate.idempotencyKey, text: candidate.text, attachments: candidate.attachments, status: "failed", error: error93 instanceof Error ? error93.message : String(error93) } : candidate);
|
|
37694
38540
|
}
|
|
37695
38541
|
render();
|
|
37696
38542
|
}
|
|
37697
38543
|
async function retryOldestFailedSubmission() {
|
|
38544
|
+
if (!viewIsActive())
|
|
38545
|
+
return;
|
|
37698
38546
|
const failed = admittedSubmissions.find((submission) => submission.status === "failed");
|
|
37699
38547
|
if (!failed)
|
|
37700
38548
|
return;
|
|
@@ -37810,6 +38658,7 @@ async function createSessionTui({
|
|
|
37810
38658
|
if (destroyed)
|
|
37811
38659
|
return;
|
|
37812
38660
|
destroyed = true;
|
|
38661
|
+
invalidateAttachmentPreparation();
|
|
37813
38662
|
if (workingSpinnerTimer) {
|
|
37814
38663
|
clearInterval(workingSpinnerTimer);
|
|
37815
38664
|
workingSpinnerTimer = undefined;
|
|
@@ -37851,9 +38700,9 @@ function renderSlashCommandMenu({ commands, selectedIndex }) {
|
|
|
37851
38700
|
const rows = commands.map((command, index) => {
|
|
37852
38701
|
const selected = index === selectedIndex;
|
|
37853
38702
|
const rowStyle = selected ? bg4(PALETTE.selectionBg) : (chunk) => chunk;
|
|
37854
|
-
return new
|
|
37855
|
-
rowStyle(
|
|
37856
|
-
rowStyle(
|
|
38703
|
+
return new StyledText6([
|
|
38704
|
+
rowStyle(fg7(PALETTE.remyAccent)(`${selected ? "\u203A " : " "}${command.value.padEnd(12)}`)),
|
|
38705
|
+
rowStyle(fg7(PALETTE.dimText)(command.description))
|
|
37857
38706
|
]);
|
|
37858
38707
|
});
|
|
37859
38708
|
return joinStyled([joinStyled(rows, `
|
|
@@ -37874,15 +38723,15 @@ var helpEntries = [
|
|
|
37874
38723
|
];
|
|
37875
38724
|
function renderHelpPanel() {
|
|
37876
38725
|
const width = helpEntries.reduce((max, entry) => Math.max(max, entry.token.length), 0) + 2;
|
|
37877
|
-
const rows = [new
|
|
38726
|
+
const rows = [new StyledText6([dim5(fg7(PALETTE.dimText)("What you can do here"))])];
|
|
37878
38727
|
let lastGroup;
|
|
37879
38728
|
for (const entry of helpEntries) {
|
|
37880
38729
|
if (lastGroup !== undefined && entry.group !== lastGroup)
|
|
37881
|
-
rows.push(new
|
|
38730
|
+
rows.push(new StyledText6([fg7(PALETTE.dimText)(" ")]));
|
|
37882
38731
|
lastGroup = entry.group;
|
|
37883
|
-
rows.push(new
|
|
37884
|
-
|
|
37885
|
-
|
|
38732
|
+
rows.push(new StyledText6([
|
|
38733
|
+
fg7(PALETTE.remyAccent)(` ${entry.token.padEnd(width)}`),
|
|
38734
|
+
dim5(fg7(PALETTE.dimText)(entry.description))
|
|
37886
38735
|
]));
|
|
37887
38736
|
}
|
|
37888
38737
|
return joinStyled(rows, `
|
|
@@ -37896,23 +38745,22 @@ function renderStatusBand({ state, working, elapsedMs, stopState, lifecycleReque
|
|
|
37896
38745
|
const pendingOperation = lifecycleRequestState?.kind === "pending" ? lifecycleRequestState.operation : undefined;
|
|
37897
38746
|
const presentation = pendingOperation ? { label: lifecycleOperationPresentParticiple(pendingOperation), color: PALETTE.approvalQuestion } : stopping ? { label: "Stopping", color: PALETTE.approvalQuestion } : sessionViewStatePresentation({ aggregateStatus: state.aggregateStatus, isWorking: working });
|
|
37898
38747
|
const elapsed = (working || stopping) && elapsedMs !== undefined && elapsedMs > 0 ? ` ${formatElapsed(elapsedMs)}` : "";
|
|
37899
|
-
const connection = state.connectionStatus === "connected" ?
|
|
37900
|
-
const sessionLabel = state.sessionNumber !== undefined ?
|
|
37901
|
-
const context = renderContextStatus(state.context.snapshot);
|
|
38748
|
+
const connection = state.connectionStatus === "connected" ? new StyledText6([fg7(PALETTE.statusCompleted)("\u25CF "), dim5(fg7(PALETTE.dimText)("connected"))]) : new StyledText6([fg7(PALETTE.approvalQuestion)("\u25CF reconnecting\u2026")]);
|
|
38749
|
+
const sessionLabel = state.sessionNumber !== undefined ? `#${state.sessionNumber}` : state.sessionId;
|
|
37902
38750
|
const pullRequest = state.pullRequest;
|
|
37903
|
-
|
|
37904
|
-
|
|
37905
|
-
...repositoryLabel ? [
|
|
37906
|
-
|
|
37907
|
-
bold4(
|
|
37908
|
-
|
|
37909
|
-
|
|
37910
|
-
|
|
37911
|
-
|
|
37912
|
-
|
|
37913
|
-
|
|
37914
|
-
|
|
37915
|
-
|
|
38751
|
+
const contextStatus = renderContextStatus(state.context.snapshot);
|
|
38752
|
+
const headerContext = [
|
|
38753
|
+
...repositoryLabel ? [[dim5(fg7(PALETTE.dimText)(repositoryLabel))]] : [],
|
|
38754
|
+
...pullRequest ? [[bold4(fg7(statusColor(pullRequest.status))(`PR#${pullRequest.number}`))]] : [],
|
|
38755
|
+
[bold4(fg7(presentation.color)(`${presentation.label}${elapsed}`))],
|
|
38756
|
+
connection.chunks,
|
|
38757
|
+
...contextStatus ? [[dim5(fg7(PALETTE.dimText)(contextStatus))]] : []
|
|
38758
|
+
];
|
|
38759
|
+
return joinStyled([
|
|
38760
|
+
[bold4(fg7(PALETTE.bodyText)(`${sessionLabel} ${state.title ?? "Untitled session"}`))],
|
|
38761
|
+
joinStyled(headerContext, " \xB7 ")
|
|
38762
|
+
], `
|
|
38763
|
+
`);
|
|
37916
38764
|
}
|
|
37917
38765
|
function renderContextStatus(snapshot) {
|
|
37918
38766
|
if (snapshot.status === "unknown")
|
|
@@ -37923,33 +38771,39 @@ function renderContextStatus(snapshot) {
|
|
|
37923
38771
|
function renderTerminalSessionBand({ aggregateStatus }) {
|
|
37924
38772
|
const status = sessionStatusLabel(aggregateStatus).toLowerCase();
|
|
37925
38773
|
return joinStyled([
|
|
37926
|
-
new
|
|
37927
|
-
bg4(PALETTE.selectionBg)(bold4(
|
|
37928
|
-
bg4(PALETTE.selectionBg)(
|
|
38774
|
+
new StyledText6([
|
|
38775
|
+
bg4(PALETTE.selectionBg)(bold4(fg7(PALETTE.systemAccent)(` Session ${status} `))),
|
|
38776
|
+
bg4(PALETTE.selectionBg)(fg7(PALETTE.bodyText)("\u2014 this conversation is closed. "))
|
|
37929
38777
|
]),
|
|
37930
|
-
new
|
|
37931
|
-
bg4(PALETTE.selectionBg)(
|
|
37932
|
-
bg4(PALETTE.selectionBg)(
|
|
37933
|
-
bg4(PALETTE.selectionBg)(
|
|
37934
|
-
bg4(PALETTE.selectionBg)(
|
|
38778
|
+
new StyledText6([
|
|
38779
|
+
bg4(PALETTE.selectionBg)(fg7(PALETTE.remyAccent)(" esc: ")),
|
|
38780
|
+
bg4(PALETTE.selectionBg)(dim5(fg7(PALETTE.dimText)("return to dashboard \xB7 "))),
|
|
38781
|
+
bg4(PALETTE.selectionBg)(fg7(PALETTE.remyAccent)("n: ")),
|
|
38782
|
+
bg4(PALETTE.selectionBg)(dim5(fg7(PALETTE.dimText)("new session ")))
|
|
37935
38783
|
])
|
|
37936
38784
|
], `
|
|
37937
38785
|
`);
|
|
37938
38786
|
}
|
|
37939
38787
|
function renderActionBar({ state, working, stopState, lifecycleRequestState }) {
|
|
37940
38788
|
if (lifecycleRequestState.kind === "pending")
|
|
37941
|
-
return new
|
|
38789
|
+
return new StyledText6([dim5(fg7(PALETTE.dimText)(`${lifecycleOperationPresentParticiple(lifecycleRequestState.operation).toLowerCase()} the session\u2026`))]);
|
|
37942
38790
|
if (state.aggregateStatus !== "open")
|
|
37943
|
-
return new
|
|
38791
|
+
return new StyledText6([]);
|
|
37944
38792
|
const stopToken = working && stopState.kind === "failed" ? ["esc retry stop"] : [];
|
|
37945
38793
|
const idleGroup = lifecycleRequestState.kind === "failed" ? `/${lifecycleRequestState.operation} retries \xB7 /new \xB7 /sessions` : "/new \xB7 /sessions \xB7 /complete \xB7 /cancel";
|
|
37946
38794
|
const tokens = working ? ["/ commands", ...stopToken, "ctrl+o activity"] : ["/ commands", idleGroup, "ctrl+o activity"];
|
|
37947
|
-
return new
|
|
38795
|
+
return new StyledText6([dim5(fg7(PALETTE.dimText)(tokens.join(" \xB7 ")))]);
|
|
37948
38796
|
}
|
|
37949
38797
|
function isRemyWorking(state) {
|
|
37950
|
-
if (state.aggregateStatus !== "open" || state.connectionStatus !== "connected")
|
|
38798
|
+
if (state.aggregateStatus !== "open" || state.connectionStatus !== "connected") {
|
|
37951
38799
|
return false;
|
|
37952
|
-
|
|
38800
|
+
}
|
|
38801
|
+
if (state.activeMessageId !== undefined && state.messageTurns[state.activeMessageId]?.outcome === undefined)
|
|
38802
|
+
return true;
|
|
38803
|
+
return isWorkingAgentStatus(state.agentStatus) && hasRunningTurn(state);
|
|
38804
|
+
}
|
|
38805
|
+
function isWorkingAgentStatus(agentStatus) {
|
|
38806
|
+
return agentStatus === undefined || agentStatus === "pending" || agentStatus === "queued" || agentStatus === "running";
|
|
37953
38807
|
}
|
|
37954
38808
|
function workingTurnStartedAt(state) {
|
|
37955
38809
|
const activeTurn = state.activeMessageId === undefined ? undefined : state.messageTurns[state.activeMessageId];
|
|
@@ -37990,58 +38844,64 @@ function renderTimeline({ state, activityExpanded }) {
|
|
|
37990
38844
|
}
|
|
37991
38845
|
function renderTimelineItem(item, activityExpanded) {
|
|
37992
38846
|
if (item.kind === "message") {
|
|
37993
|
-
const header = new
|
|
38847
|
+
const header = new StyledText6([
|
|
37994
38848
|
renderTimestampChunk(item.occurredAt),
|
|
37995
38849
|
renderRoleLabel({ label: item.author.label, role: item.author.role }),
|
|
37996
|
-
...item.author.source ? [
|
|
38850
|
+
...item.author.source ? [dim5(fg7(PALETTE.dimText)(` (${item.author.source})`))] : []
|
|
37997
38851
|
]);
|
|
37998
|
-
|
|
38852
|
+
const attachments = renderAttachmentSummary(item.attachments);
|
|
38853
|
+
return joinStyled([
|
|
38854
|
+
header,
|
|
38855
|
+
renderMessageBody({ text: item.text, role: item.author.role }),
|
|
38856
|
+
...attachments.chunks.length > 0 ? [attachments] : []
|
|
38857
|
+
], `
|
|
37999
38858
|
`);
|
|
38000
38859
|
}
|
|
38001
38860
|
if (item.kind === "plan")
|
|
38002
38861
|
return renderPlan({ item, expanded: activityExpanded });
|
|
38003
38862
|
if (item.artifactKind === "tela_page") {
|
|
38004
|
-
return new
|
|
38863
|
+
return new StyledText6([
|
|
38005
38864
|
renderTimestampChunk(item.occurredAt),
|
|
38006
|
-
bold4(
|
|
38007
|
-
|
|
38008
|
-
|
|
38865
|
+
bold4(fg7(PALETTE.tool)("Tela Page: ")),
|
|
38866
|
+
fg7(PALETTE.bodyText)(item.title),
|
|
38867
|
+
dim5(fg7(PALETTE.dimText)(` \xB7 ${item.url}`))
|
|
38009
38868
|
]);
|
|
38010
38869
|
}
|
|
38011
|
-
return new
|
|
38870
|
+
return new StyledText6([
|
|
38012
38871
|
renderTimestampChunk(item.occurredAt),
|
|
38013
|
-
bold4(
|
|
38014
|
-
|
|
38872
|
+
bold4(fg7(PALETTE.tool)("Artifact: ")),
|
|
38873
|
+
fg7(PALETTE.bodyText)(item.title),
|
|
38874
|
+
...item.previewUrl ? [dim5(fg7(PALETTE.dimText)(` (${item.previewUrl})`))] : []
|
|
38015
38875
|
]);
|
|
38016
38876
|
}
|
|
38017
38877
|
var collapsedPlanItemLimit = 5;
|
|
38018
38878
|
function renderPlan({ item, expanded }) {
|
|
38019
38879
|
if (item.items.length === 0)
|
|
38020
|
-
return new
|
|
38880
|
+
return new StyledText6([]);
|
|
38021
38881
|
const completed = item.items.filter((workItem) => workItem.status === "completed").length;
|
|
38022
38882
|
const visibleItems = expanded ? item.items : collapsedPlanItems(item.items);
|
|
38023
38883
|
const rows = visibleItems.map((workItem) => {
|
|
38024
38884
|
if (workItem.status === "in_progress") {
|
|
38025
|
-
return new
|
|
38026
|
-
|
|
38027
|
-
bold4(
|
|
38885
|
+
return new StyledText6([
|
|
38886
|
+
fg7(PALETTE.remyAccent)(" \u25B8 "),
|
|
38887
|
+
bold4(fg7(PALETTE.bodyText)(workItem.title))
|
|
38028
38888
|
]);
|
|
38029
38889
|
}
|
|
38030
38890
|
const glyph = workItem.status === "completed" ? "\u2713" : "\u25CB";
|
|
38031
|
-
return new
|
|
38032
|
-
|
|
38033
|
-
|
|
38891
|
+
return new StyledText6([
|
|
38892
|
+
fg7(PALETTE.dimText)(` ${glyph} `),
|
|
38893
|
+
dim5(fg7(PALETTE.dimText)(workItem.title))
|
|
38034
38894
|
]);
|
|
38035
38895
|
});
|
|
38036
38896
|
const hidden = item.items.length - visibleItems.length;
|
|
38037
38897
|
return joinStyled([
|
|
38038
|
-
new
|
|
38898
|
+
new StyledText6([
|
|
38039
38899
|
renderTimestampChunk(item.occurredAt),
|
|
38040
|
-
bold4(
|
|
38041
|
-
|
|
38900
|
+
bold4(fg7(PALETTE.remyAccent)("Plan")),
|
|
38901
|
+
dim5(fg7(PALETTE.dimText)(` \xB7 ${completed}/${item.items.length} done`))
|
|
38042
38902
|
]),
|
|
38043
38903
|
...rows,
|
|
38044
|
-
...hidden > 0 ? [new
|
|
38904
|
+
...hidden > 0 ? [new StyledText6([dim5(fg7(PALETTE.dimText)(` \u2026 ${hidden} more task${hidden === 1 ? "" : "s"} \xB7 Ctrl+O details`))])] : []
|
|
38045
38905
|
], `
|
|
38046
38906
|
`);
|
|
38047
38907
|
}
|
|
@@ -38088,7 +38948,7 @@ function canonicalValuesEqual(left, right) {
|
|
|
38088
38948
|
}
|
|
38089
38949
|
function renderActivityGroup({ items, activityExpanded }) {
|
|
38090
38950
|
if (items.length === 0)
|
|
38091
|
-
return new
|
|
38951
|
+
return new StyledText6([]);
|
|
38092
38952
|
const summary = renderActivitySummary({ items, includeTimestamp: activityExpanded });
|
|
38093
38953
|
const lastInFlightIndex = items.length - 1;
|
|
38094
38954
|
if (!activityExpanded) {
|
|
@@ -38097,9 +38957,9 @@ function renderActivityGroup({ items, activityExpanded }) {
|
|
|
38097
38957
|
item,
|
|
38098
38958
|
inFlight: item.card.kind === "tool" && tailStart + index === lastInFlightIndex
|
|
38099
38959
|
}));
|
|
38100
|
-
const elision = tailStart > 0 ? new
|
|
38960
|
+
const elision = tailStart > 0 ? new StyledText6([dim5(fg7(PALETTE.dimText)(` \u2026 ${tailStart} earlier step${tailStart === 1 ? "" : "s"}`))]) : undefined;
|
|
38101
38961
|
return joinStyled([
|
|
38102
|
-
new
|
|
38962
|
+
new StyledText6([renderTimestampChunk(items[0].occurredAt), dim5(fg7(PALETTE.dimText)("Worked"))]),
|
|
38103
38963
|
...elision ? [elision] : [],
|
|
38104
38964
|
...tail,
|
|
38105
38965
|
summary
|
|
@@ -38118,9 +38978,9 @@ function renderExpandedActivityHierarchy({ items, lastInFlightIndex }) {
|
|
|
38118
38978
|
const attribution = item.card.attribution;
|
|
38119
38979
|
if (attribution?.status === "resolved") {
|
|
38120
38980
|
if (activeOwnerKey !== attribution.ownerKey) {
|
|
38121
|
-
rendered.push(new
|
|
38122
|
-
|
|
38123
|
-
|
|
38981
|
+
rendered.push(new StyledText6([
|
|
38982
|
+
dim5(fg7(PALETTE.dimText)(" ")),
|
|
38983
|
+
fg7(PALETTE.bodyText)(`Subagent \xB7 ${lineagePathLabel(attribution.path)}`)
|
|
38124
38984
|
]));
|
|
38125
38985
|
}
|
|
38126
38986
|
activeOwnerKey = attribution.ownerKey;
|
|
@@ -38147,11 +39007,11 @@ function renderExpandedActivityStep({ item, count, inFlight, nested }) {
|
|
|
38147
39007
|
const disclosure = renderActivityDisclosure(item.card);
|
|
38148
39008
|
const title = expandedActivityTitle(item.card);
|
|
38149
39009
|
const indent = nested ? " " : " ";
|
|
38150
|
-
const step = new
|
|
38151
|
-
|
|
38152
|
-
|
|
38153
|
-
...count > 1 ? [
|
|
38154
|
-
...showSummary ? [
|
|
39010
|
+
const step = new StyledText6([
|
|
39011
|
+
fg7(color)(`${indent}${glyph} `),
|
|
39012
|
+
fg7(PALETTE.bodyText)(title),
|
|
39013
|
+
...count > 1 ? [dim5(fg7(PALETTE.dimText)(` \xD7${count}`))] : [],
|
|
39014
|
+
...showSummary ? [dim5(fg7(PALETTE.dimText)(`
|
|
38155
39015
|
${indent} ${item.card.summary}`))] : []
|
|
38156
39016
|
]);
|
|
38157
39017
|
return disclosure ? joinStyled([step, disclosure], `
|
|
@@ -38178,23 +39038,23 @@ function renderActivityDisclosure(card) {
|
|
|
38178
39038
|
const details = [];
|
|
38179
39039
|
if (card.detail !== undefined) {
|
|
38180
39040
|
const safeDetail = terminalSafeText(card.detail);
|
|
38181
|
-
details.push(card.detailFormat === "code" ? renderCodeSnippet({ text: safeDetail, indent: " " }) : new
|
|
39041
|
+
details.push(card.detailFormat === "code" ? renderCodeSnippet({ text: safeDetail, indent: " " }) : new StyledText6([dim5(fg7(PALETTE.dimText)(` ${safeDetail}`))]));
|
|
38182
39042
|
}
|
|
38183
39043
|
return details.length === 0 ? undefined : joinStyled(details, `
|
|
38184
39044
|
|
|
38185
39045
|
`);
|
|
38186
39046
|
}
|
|
38187
39047
|
function renderActivitySummary({ items, includeTimestamp }) {
|
|
38188
|
-
return new
|
|
39048
|
+
return new StyledText6([
|
|
38189
39049
|
...includeTimestamp ? [renderTimestampChunk(items[0].occurredAt)] : [],
|
|
38190
|
-
|
|
39050
|
+
dim5(fg7(PALETTE.dimText)(`Worked \xB7 ${items.length} step${items.length === 1 ? "" : "s"} \xB7 Ctrl+O details`))
|
|
38191
39051
|
]);
|
|
38192
39052
|
}
|
|
38193
39053
|
function renderCollapsedActivityStep({ item, inFlight }) {
|
|
38194
39054
|
const { glyph, color } = activityGlyph({ kind: item.card.kind, inFlight });
|
|
38195
|
-
return new
|
|
38196
|
-
|
|
38197
|
-
|
|
39055
|
+
return new StyledText6([
|
|
39056
|
+
fg7(color)(` ${glyph} `),
|
|
39057
|
+
dim5(fg7(PALETTE.dimText)(collapsedActivityTitle(item.card)))
|
|
38198
39058
|
]);
|
|
38199
39059
|
}
|
|
38200
39060
|
function collapsedActivityTitle(card) {
|
|
@@ -38211,15 +39071,15 @@ function collapsedActivityTitle(card) {
|
|
|
38211
39071
|
function renderSubmittedTimeline({ state, admittedSubmissions }) {
|
|
38212
39072
|
const submitted = admittedSubmissions.filter((submission) => submission.status === "submitted" && !state.transcript.some((item) => item.kind === "message" && item.messageId === submission.messageId));
|
|
38213
39073
|
const messages = submitted.map((submission) => {
|
|
38214
|
-
const attachments =
|
|
38215
|
-
const header = new
|
|
39074
|
+
const attachments = renderAttachmentSummary(submission.attachments);
|
|
39075
|
+
const header = new StyledText6([
|
|
38216
39076
|
renderRoleLabel({ label: "You", role: "human" }),
|
|
38217
|
-
|
|
39077
|
+
dim5(fg7(PALETTE.dimText)(" (CLI)"))
|
|
38218
39078
|
]);
|
|
38219
39079
|
return joinStyled([
|
|
38220
39080
|
header,
|
|
38221
39081
|
renderMessageBody({ text: submission.text, role: "human" }),
|
|
38222
|
-
...attachments ? [
|
|
39082
|
+
...attachments.chunks.length > 0 ? [attachments] : []
|
|
38223
39083
|
], `
|
|
38224
39084
|
`);
|
|
38225
39085
|
});
|
|
@@ -38229,20 +39089,20 @@ function renderSubmittedTimeline({ state, admittedSubmissions }) {
|
|
|
38229
39089
|
}
|
|
38230
39090
|
function renderWorkingIndicator({ frame, elapsedMs, mode }) {
|
|
38231
39091
|
if (mode === "stopping") {
|
|
38232
|
-
return new
|
|
38233
|
-
|
|
38234
|
-
|
|
39092
|
+
return new StyledText6([
|
|
39093
|
+
fg7(PALETTE.approvalQuestion)("\u23F9"),
|
|
39094
|
+
dim5(fg7(PALETTE.dimText)(` Stopping\u2026 ${formatElapsed(elapsedMs)}`))
|
|
38235
39095
|
]);
|
|
38236
39096
|
}
|
|
38237
39097
|
if (mode === "complete" || mode === "cancel") {
|
|
38238
|
-
return new
|
|
38239
|
-
|
|
38240
|
-
|
|
39098
|
+
return new StyledText6([
|
|
39099
|
+
fg7(PALETTE.approvalQuestion)(frame),
|
|
39100
|
+
dim5(fg7(PALETTE.dimText)(` ${lifecycleOperationPresentParticiple(mode)} the session\u2026`))
|
|
38241
39101
|
]);
|
|
38242
39102
|
}
|
|
38243
|
-
return new
|
|
38244
|
-
|
|
38245
|
-
|
|
39103
|
+
return new StyledText6([
|
|
39104
|
+
fg7(PALETTE.remyAccent)(frame),
|
|
39105
|
+
dim5(fg7(PALETTE.dimText)(` Working ${formatElapsed(elapsedMs)}`))
|
|
38246
39106
|
]);
|
|
38247
39107
|
}
|
|
38248
39108
|
function lifecycleOperationPresentParticiple(operation) {
|
|
@@ -38264,22 +39124,22 @@ function truncate2(value, width) {
|
|
|
38264
39124
|
}
|
|
38265
39125
|
function renderComposerStatus({ admittedSubmissions }) {
|
|
38266
39126
|
if (admittedSubmissions.length === 0)
|
|
38267
|
-
return new
|
|
39127
|
+
return new StyledText6([]);
|
|
38268
39128
|
const parts = admittedSubmissions.flatMap((submission) => {
|
|
38269
39129
|
if (submission.status === "submitted")
|
|
38270
39130
|
return [];
|
|
38271
|
-
const attachments = submission.attachments
|
|
38272
|
-
|
|
38273
|
-
|
|
38274
|
-
|
|
38275
|
-
|
|
38276
|
-
|
|
38277
|
-
`
|
|
38278
|
-
|
|
38279
|
-
|
|
38280
|
-
|
|
38281
|
-
|
|
38282
|
-
|
|
39131
|
+
const attachments = renderAttachmentSummary(submission.attachments);
|
|
39132
|
+
return [submission.status === "failed" ? joinStyled([
|
|
39133
|
+
new StyledText6([bold4(fg7(PALETTE.failure)("Failed: ")), fg7(PALETTE.bodyText)(submission.error ?? "")]),
|
|
39134
|
+
renderMessageBody({ text: submission.text, role: "human" }),
|
|
39135
|
+
...attachments.chunks.length > 0 ? [attachments] : [],
|
|
39136
|
+
new StyledText6([dim5(fg7(PALETTE.dimText)("Ctrl+R retries oldest failed submission."))])
|
|
39137
|
+
], `
|
|
39138
|
+
`) : joinStyled([
|
|
39139
|
+
new StyledText6([fg7(PALETTE.progress)("Admitting: "), fg7(PALETTE.bodyText)(submission.text)]),
|
|
39140
|
+
...attachments.chunks.length > 0 ? [attachments] : []
|
|
39141
|
+
], `
|
|
39142
|
+
`)];
|
|
38283
39143
|
});
|
|
38284
39144
|
return joinStyled(parts, `
|
|
38285
39145
|
|
|
@@ -38290,10 +39150,15 @@ async function createDefaultRenderer3() {
|
|
|
38290
39150
|
}
|
|
38291
39151
|
|
|
38292
39152
|
// src/tui/attachments.ts
|
|
39153
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
38293
39154
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
38294
|
-
import {
|
|
38295
|
-
import { homedir as homedir2 } from "os";
|
|
39155
|
+
import { mkdtemp, open as open4, readdir as readdir2, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
39156
|
+
import { homedir as homedir2, tmpdir } from "os";
|
|
38296
39157
|
import { basename as basename3, isAbsolute as isAbsolute2, join as join5, resolve as resolve2 } from "path";
|
|
39158
|
+
var directoryArchiveMediaType = "application/gzip";
|
|
39159
|
+
var directoryArchiveSuffix = ".tar.gz";
|
|
39160
|
+
var maxPortableFilenameBytes = 255;
|
|
39161
|
+
var attachmentSizeLimitLabel = `${fileUploadMaxBytes / (1024 * 1024)} MiB`;
|
|
38297
39162
|
async function readClipboardImage() {
|
|
38298
39163
|
if (process.platform !== "darwin")
|
|
38299
39164
|
throw new Error("Clipboard image paste is supported on macOS only.");
|
|
@@ -38319,11 +39184,13 @@ async function attachPromptPaths({
|
|
|
38319
39184
|
text,
|
|
38320
39185
|
cwd,
|
|
38321
39186
|
excludedPaths = [],
|
|
38322
|
-
|
|
39187
|
+
selectedPaths = [],
|
|
39188
|
+
reserveAndUploadFile: reserveAndUploadFile2,
|
|
39189
|
+
onUploadStart
|
|
38323
39190
|
}) {
|
|
38324
39191
|
const excluded = new Set(excludedPaths);
|
|
38325
|
-
const
|
|
38326
|
-
for (const candidate of promptFilePaths({ text, cwd })) {
|
|
39192
|
+
const pathsToUpload = [];
|
|
39193
|
+
for (const candidate of promptFilePaths({ text, cwd, selectedPaths })) {
|
|
38327
39194
|
const { filePath } = candidate;
|
|
38328
39195
|
if (excluded.has(filePath))
|
|
38329
39196
|
continue;
|
|
@@ -38335,27 +39202,168 @@ async function attachPromptPaths({
|
|
|
38335
39202
|
continue;
|
|
38336
39203
|
throw error93;
|
|
38337
39204
|
}
|
|
38338
|
-
if (!file3.isFile() && candidate.explicit)
|
|
38339
|
-
throw new Error(`Prompt attachment path is not a regular file: ${filePath}`);
|
|
38340
|
-
if (!file3.isFile())
|
|
39205
|
+
if (!file3.isFile() && !file3.isDirectory() && candidate.explicit)
|
|
39206
|
+
throw new Error(`Prompt attachment path is not a regular file or directory: ${filePath}`);
|
|
39207
|
+
if (!file3.isFile() && !file3.isDirectory())
|
|
38341
39208
|
continue;
|
|
39209
|
+
pathsToUpload.push(filePath);
|
|
39210
|
+
}
|
|
39211
|
+
if (pathsToUpload.length === 0)
|
|
39212
|
+
return [];
|
|
39213
|
+
onUploadStart?.({ filenames: pathsToUpload.map((filePath) => basename3(filePath)) });
|
|
39214
|
+
const attachments = [];
|
|
39215
|
+
for (const filePath of pathsToUpload) {
|
|
39216
|
+
const uploaded = await uploadLocalAttachment({ filePath, reserveAndUploadFile: reserveAndUploadFile2 });
|
|
38342
39217
|
attachments.push({
|
|
38343
39218
|
id: randomUUID4(),
|
|
38344
|
-
|
|
38345
|
-
filename: basename3(filePath),
|
|
38346
|
-
sourcePath: filePath
|
|
39219
|
+
...uploaded
|
|
38347
39220
|
});
|
|
38348
39221
|
}
|
|
38349
39222
|
return attachments;
|
|
38350
39223
|
}
|
|
38351
|
-
function
|
|
39224
|
+
async function uploadLocalAttachment({
|
|
39225
|
+
filePath,
|
|
39226
|
+
reserveAndUploadFile: reserveAndUploadFile2
|
|
39227
|
+
}) {
|
|
39228
|
+
const sourcePath = resolve2(filePath);
|
|
39229
|
+
const source = await stat(sourcePath);
|
|
39230
|
+
if (source.isFile()) {
|
|
39231
|
+
if (source.size > fileUploadMaxBytes)
|
|
39232
|
+
throw oversizedFileAttachmentError(sourcePath);
|
|
39233
|
+
return {
|
|
39234
|
+
fileId: await reserveAndUploadFile2({ filePath: sourcePath }),
|
|
39235
|
+
filename: basename3(sourcePath),
|
|
39236
|
+
sourcePath
|
|
39237
|
+
};
|
|
39238
|
+
}
|
|
39239
|
+
if (!source.isDirectory())
|
|
39240
|
+
throw new Error(`Attachment path is not a regular file or directory: ${sourcePath}`);
|
|
39241
|
+
const archive = await createDirectoryArchive(sourcePath);
|
|
39242
|
+
try {
|
|
39243
|
+
return {
|
|
39244
|
+
fileId: await reserveAndUploadFile2({ filePath: archive.path, mediaType: directoryArchiveMediaType }),
|
|
39245
|
+
filename: archive.filename,
|
|
39246
|
+
sourcePath
|
|
39247
|
+
};
|
|
39248
|
+
} finally {
|
|
39249
|
+
await rm(archive.temporaryDirectory, { recursive: true, force: true });
|
|
39250
|
+
}
|
|
39251
|
+
}
|
|
39252
|
+
async function createDirectoryArchive(directoryPath) {
|
|
39253
|
+
const rootName = basename3(directoryPath) || "directory";
|
|
39254
|
+
const files = Object.create(null);
|
|
39255
|
+
await collectDirectoryFiles({
|
|
39256
|
+
directoryPath,
|
|
39257
|
+
archivePath: rootName,
|
|
39258
|
+
attachmentDirectoryPath: directoryPath,
|
|
39259
|
+
collectedByteSize: 0,
|
|
39260
|
+
files
|
|
39261
|
+
});
|
|
39262
|
+
if (Object.keys(files).length === 0)
|
|
39263
|
+
throw new Error(`Directory attachment contains no regular files: ${directoryPath}`);
|
|
39264
|
+
const temporaryDirectory = await mkdtemp(join5(tmpdir(), "remy-directory-attachment-"));
|
|
39265
|
+
const filename = directoryArchiveFilename(rootName);
|
|
39266
|
+
const archivePath = join5(temporaryDirectory, filename);
|
|
39267
|
+
try {
|
|
39268
|
+
await Bun.Archive.write(archivePath, files, { compress: "gzip" });
|
|
39269
|
+
if ((await stat(archivePath)).size > fileUploadMaxBytes)
|
|
39270
|
+
throw oversizedDirectoryArchiveError(directoryPath);
|
|
39271
|
+
} catch (error93) {
|
|
39272
|
+
await rm(temporaryDirectory, { recursive: true, force: true });
|
|
39273
|
+
throw error93;
|
|
39274
|
+
}
|
|
39275
|
+
return { path: archivePath, filename, temporaryDirectory };
|
|
39276
|
+
}
|
|
39277
|
+
async function collectDirectoryFiles({
|
|
39278
|
+
directoryPath,
|
|
39279
|
+
archivePath,
|
|
39280
|
+
attachmentDirectoryPath,
|
|
39281
|
+
collectedByteSize,
|
|
39282
|
+
files
|
|
39283
|
+
}) {
|
|
39284
|
+
const entries = await readdir2(directoryPath, { withFileTypes: true });
|
|
39285
|
+
entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
39286
|
+
let byteSize = collectedByteSize;
|
|
39287
|
+
for (const entry of entries) {
|
|
39288
|
+
const entryPath = join5(directoryPath, entry.name);
|
|
39289
|
+
const entryArchivePath = `${archivePath}/${entry.name}`;
|
|
39290
|
+
if (entry.isDirectory()) {
|
|
39291
|
+
byteSize = await collectDirectoryFiles({
|
|
39292
|
+
directoryPath: entryPath,
|
|
39293
|
+
archivePath: entryArchivePath,
|
|
39294
|
+
attachmentDirectoryPath,
|
|
39295
|
+
collectedByteSize: byteSize,
|
|
39296
|
+
files
|
|
39297
|
+
});
|
|
39298
|
+
continue;
|
|
39299
|
+
}
|
|
39300
|
+
if (entry.isFile()) {
|
|
39301
|
+
const fileHandle = await open4(entryPath, "r");
|
|
39302
|
+
try {
|
|
39303
|
+
const entryStat = await fileHandle.stat();
|
|
39304
|
+
if (!entryStat.isFile())
|
|
39305
|
+
throw new Error(`Directory attachment contains a non-regular entry: ${entryPath}`);
|
|
39306
|
+
const remainingBytes = fileUploadMaxBytes - byteSize;
|
|
39307
|
+
if (entryStat.size > remainingBytes)
|
|
39308
|
+
throw oversizedDirectoryAttachmentError(attachmentDirectoryPath);
|
|
39309
|
+
const readResult = await readFileHandleWithLimit({
|
|
39310
|
+
fileHandle,
|
|
39311
|
+
initialByteSize: entryStat.size,
|
|
39312
|
+
maxBytes: remainingBytes
|
|
39313
|
+
});
|
|
39314
|
+
if (readResult.status === "limit_exceeded")
|
|
39315
|
+
throw oversizedDirectoryAttachmentError(attachmentDirectoryPath);
|
|
39316
|
+
files[entryArchivePath] = readResult.bytes;
|
|
39317
|
+
byteSize += readResult.bytes.byteLength;
|
|
39318
|
+
} finally {
|
|
39319
|
+
await fileHandle.close();
|
|
39320
|
+
}
|
|
39321
|
+
continue;
|
|
39322
|
+
}
|
|
39323
|
+
if (entry.isSymbolicLink())
|
|
39324
|
+
throw new Error(`Directory attachment contains a symbolic link: ${entryPath}`);
|
|
39325
|
+
throw new Error(`Directory attachment contains a non-regular entry: ${entryPath}`);
|
|
39326
|
+
}
|
|
39327
|
+
return byteSize;
|
|
39328
|
+
}
|
|
39329
|
+
function oversizedFileAttachmentError(filePath) {
|
|
39330
|
+
return new Error(`Attachment file ${JSON.stringify(filePath)} is larger than ${attachmentSizeLimitLabel}. Choose a file no larger than ${attachmentSizeLimitLabel}.`);
|
|
39331
|
+
}
|
|
39332
|
+
function oversizedDirectoryAttachmentError(directoryPath) {
|
|
39333
|
+
return new Error(`Attachment directory ${JSON.stringify(directoryPath)} contains more than ${attachmentSizeLimitLabel} of files. Choose fewer or smaller files.`);
|
|
39334
|
+
}
|
|
39335
|
+
function oversizedDirectoryArchiveError(directoryPath) {
|
|
39336
|
+
return new Error(`Attachment directory ${JSON.stringify(directoryPath)} produces an archive larger than ${attachmentSizeLimitLabel}. Choose fewer or smaller files.`);
|
|
39337
|
+
}
|
|
39338
|
+
function directoryArchiveFilename(directoryName) {
|
|
39339
|
+
const stem = truncateUtf8(directoryName, maxPortableFilenameBytes - Buffer2.byteLength(directoryArchiveSuffix)) || "directory";
|
|
39340
|
+
return `${stem}${directoryArchiveSuffix}`;
|
|
39341
|
+
}
|
|
39342
|
+
function truncateUtf8(value, maxBytes) {
|
|
39343
|
+
let result = "";
|
|
39344
|
+
let resultBytes = 0;
|
|
39345
|
+
for (const character of value) {
|
|
39346
|
+
const characterBytes = Buffer2.byteLength(character);
|
|
39347
|
+
if (resultBytes + characterBytes > maxBytes)
|
|
39348
|
+
break;
|
|
39349
|
+
result += character;
|
|
39350
|
+
resultBytes += characterBytes;
|
|
39351
|
+
}
|
|
39352
|
+
return result;
|
|
39353
|
+
}
|
|
39354
|
+
function promptFilePaths({ text, cwd, selectedPaths }) {
|
|
38352
39355
|
const paths = new Map;
|
|
39356
|
+
for (const selectedPath of selectedPaths)
|
|
39357
|
+
paths.set(isAbsolute2(selectedPath) ? selectedPath : resolve2(cwd, selectedPath), true);
|
|
38353
39358
|
for (const rawToken of text.split(/\s+/)) {
|
|
38354
39359
|
const token = rawToken.replace(/^[([{'"`]+/, "").replace(/[),.;:!?\]}"`]+$/, "");
|
|
38355
39360
|
const pathText = token.startsWith("@") ? token.slice(1) : token;
|
|
38356
39361
|
if (!pathText)
|
|
38357
39362
|
continue;
|
|
38358
39363
|
const explicit = token.startsWith("@") || pathText.startsWith("./") || pathText.startsWith("../") || isAbsolute2(pathText) || pathText.startsWith("~/");
|
|
39364
|
+
const pathShaped = explicit || pathText.includes("/") || pathText.includes("\\") || pathText.includes(".");
|
|
39365
|
+
if (!pathShaped)
|
|
39366
|
+
continue;
|
|
38359
39367
|
const expandedPath = pathText.startsWith("~/") ? join5(homedir2(), pathText.slice(2)) : pathText;
|
|
38360
39368
|
const filePath = isAbsolute2(expandedPath) ? expandedPath : resolve2(cwd, expandedPath);
|
|
38361
39369
|
paths.set(filePath, paths.get(filePath) || explicit);
|
|
@@ -38378,7 +39386,7 @@ function isPng(bytes) {
|
|
|
38378
39386
|
}
|
|
38379
39387
|
|
|
38380
39388
|
// src/tui/remy-splash.ts
|
|
38381
|
-
import { bold as bold5, BoxRenderable as BoxRenderable5, fg as
|
|
39389
|
+
import { bold as bold5, BoxRenderable as BoxRenderable5, fg as fg8, NativeImage, StyledText as StyledText7, TextRenderable as TextRenderable5, bg as bg5 } from "@opentui/core";
|
|
38382
39390
|
// src/tui/remy-mark.ts
|
|
38383
39391
|
var remyPixelFieldSource = new URL("../assets/remy-pixel-field.png", import.meta.url);
|
|
38384
39392
|
function shouldShowRemySplash({ width, height }) {
|
|
@@ -38397,7 +39405,7 @@ var compactMarkRows = 9;
|
|
|
38397
39405
|
var compactMinWidth = 48;
|
|
38398
39406
|
var compactMinHeight = 20;
|
|
38399
39407
|
var markBrightnessGain = 4.2;
|
|
38400
|
-
var remyCliVersion = "1.
|
|
39408
|
+
var remyCliVersion = "1.14.0";
|
|
38401
39409
|
async function showRemySplash({
|
|
38402
39410
|
createRenderer = createRemyRenderer,
|
|
38403
39411
|
durationMs = splashDurationMs,
|
|
@@ -38455,7 +39463,7 @@ async function showRemySplash({
|
|
|
38455
39463
|
});
|
|
38456
39464
|
const approvalCopy = new TextRenderable5(renderer, { content: "" });
|
|
38457
39465
|
const metadata = new TextRenderable5(renderer, {
|
|
38458
|
-
content: new
|
|
39466
|
+
content: new StyledText7([fg8(PALETTE.dimText)(`v${remyCliVersion} \xB7 Tela\xAE`)])
|
|
38459
39467
|
});
|
|
38460
39468
|
const field = await loadPixelFieldUntilAbort({ signal });
|
|
38461
39469
|
if (!field || signal?.aborted)
|
|
@@ -38553,32 +39561,32 @@ function createSplashController({
|
|
|
38553
39561
|
};
|
|
38554
39562
|
}
|
|
38555
39563
|
function renderBootstrapActions({ state, frame }) {
|
|
38556
|
-
return new
|
|
39564
|
+
return new StyledText7([
|
|
38557
39565
|
actionText({ label: state.authenticatedEmail ? `Authenticated as ${state.authenticatedEmail}` : "Checking authentication...", status: state.authentication, frame }),
|
|
38558
|
-
|
|
39566
|
+
fg8(PALETTE.dimText)(`
|
|
38559
39567
|
`),
|
|
38560
39568
|
actionText({ label: "Load sessions", status: state.loadingSessions, frame })
|
|
38561
39569
|
]);
|
|
38562
39570
|
}
|
|
38563
39571
|
function actionText({ label, status, frame }) {
|
|
38564
39572
|
if (status === "complete")
|
|
38565
|
-
return
|
|
39573
|
+
return fg8(PALETTE.statusCompleted)(`\u2713 ${label === "Load sessions" ? "Sessions loaded!" : label}`);
|
|
38566
39574
|
if (status === "needed")
|
|
38567
|
-
return
|
|
39575
|
+
return fg8(PALETTE.approvalQuestion)("\u26A0 Authentication needed");
|
|
38568
39576
|
if (status === "active")
|
|
38569
|
-
return
|
|
38570
|
-
return
|
|
39577
|
+
return fg8(PALETTE.progress)(`${splashSpinnerFrames[frame % splashSpinnerFrames.length]} ${label === "Load sessions" ? "Loading sessions..." : label}`);
|
|
39578
|
+
return fg8(PALETTE.dimText)(`\u25CB ${label}`);
|
|
38571
39579
|
}
|
|
38572
39580
|
function renderAuthenticationBand({ state, frame }) {
|
|
38573
39581
|
if (!state.waitingForAuthentication)
|
|
38574
|
-
return new
|
|
39582
|
+
return new StyledText7([]);
|
|
38575
39583
|
const spinner = splashSpinnerFrames[frame % splashSpinnerFrames.length];
|
|
38576
|
-
const url3 = state.verificationUrl ? [
|
|
38577
|
-
Verification URL: `), bold5(
|
|
38578
|
-
return new
|
|
38579
|
-
|
|
39584
|
+
const url3 = state.verificationUrl ? [fg8(PALETTE.dimText)(`
|
|
39585
|
+
Verification URL: `), bold5(fg8(PALETTE.humanAccent)(state.verificationUrl))] : [];
|
|
39586
|
+
return new StyledText7([
|
|
39587
|
+
fg8(PALETTE.bodyText)("Continue authentication in your browser."),
|
|
38580
39588
|
...url3,
|
|
38581
|
-
|
|
39589
|
+
fg8(PALETTE.dimText)(`
|
|
38582
39590
|
${spinner} Waiting for approval \xB7 q / esc cancel`)
|
|
38583
39591
|
]);
|
|
38584
39592
|
}
|
|
@@ -38653,7 +39661,7 @@ function renderMark(field, frame, rows = markRows) {
|
|
|
38653
39661
|
const flush = () => {
|
|
38654
39662
|
if (runLength === 0)
|
|
38655
39663
|
return;
|
|
38656
|
-
chunks.push(
|
|
39664
|
+
chunks.push(fg8(grey(runTop))(bg5(grey(runBottom))(upperHalfBlock.repeat(runLength))));
|
|
38657
39665
|
runLength = 0;
|
|
38658
39666
|
};
|
|
38659
39667
|
for (let column = 0;column < columns; column++) {
|
|
@@ -38670,10 +39678,10 @@ function renderMark(field, frame, rows = markRows) {
|
|
|
38670
39678
|
}
|
|
38671
39679
|
flush();
|
|
38672
39680
|
if (row < rows - 1)
|
|
38673
|
-
chunks.push(
|
|
39681
|
+
chunks.push(fg8("#000000")(`
|
|
38674
39682
|
`));
|
|
38675
39683
|
}
|
|
38676
|
-
return new
|
|
39684
|
+
return new StyledText7(chunks);
|
|
38677
39685
|
}
|
|
38678
39686
|
function shimmerBrightness(field, column, sampleRow, columns, sampleRows, center) {
|
|
38679
39687
|
const x0 = Math.floor(column / columns * field.width);
|
|
@@ -38693,8 +39701,8 @@ function grey(value) {
|
|
|
38693
39701
|
return `#${hex5}${hex5}${hex5}`;
|
|
38694
39702
|
}
|
|
38695
39703
|
function renderSplashTitle() {
|
|
38696
|
-
return new
|
|
38697
|
-
bold5(
|
|
39704
|
+
return new StyledText7([
|
|
39705
|
+
bold5(fg8(PALETTE.bodyText)("Remy CLI"))
|
|
38698
39706
|
]);
|
|
38699
39707
|
}
|
|
38700
39708
|
|
|
@@ -39029,6 +40037,9 @@ function createRepositoryLister({ client }) {
|
|
|
39029
40037
|
function createRepositorySuggester({ client }) {
|
|
39030
40038
|
return async (input) => await suggestRemoteRepositories({ client, input });
|
|
39031
40039
|
}
|
|
40040
|
+
function createBranchSuggester({ client }) {
|
|
40041
|
+
return async (input) => await suggestRemoteBranches({ client, input });
|
|
40042
|
+
}
|
|
39032
40043
|
function parseOptionalStringFlag(value, name) {
|
|
39033
40044
|
if (value === undefined)
|
|
39034
40045
|
return;
|
|
@@ -39172,48 +40183,98 @@ async function dashboard({
|
|
|
39172
40183
|
const openDashboardTui = dependencies.openDashboardTui ?? createDashboardTui;
|
|
39173
40184
|
const pages = [initialSessions];
|
|
39174
40185
|
let pageIndex = 0;
|
|
39175
|
-
let
|
|
40186
|
+
let filters = { statuses: [], creators: [] };
|
|
40187
|
+
let authors;
|
|
40188
|
+
let activeDashboardGeneration = 0;
|
|
39176
40189
|
function holdTransitionScreen() {
|
|
39177
40190
|
showRemyTransitionScreen({ write: (chunk) => dependencies.output.writeStdout(chunk) });
|
|
39178
40191
|
}
|
|
39179
|
-
|
|
39180
|
-
|
|
39181
|
-
|
|
39182
|
-
|
|
39183
|
-
|
|
39184
|
-
|
|
39185
|
-
|
|
40192
|
+
function createDashboardPageLoader({ generation }) {
|
|
40193
|
+
const loadingPages = new Map;
|
|
40194
|
+
let pageLoadTail = Promise.resolve();
|
|
40195
|
+
return async (input) => {
|
|
40196
|
+
const requestKey = "filters" in input ? `filters:${JSON.stringify(input.filters)}` : input.target;
|
|
40197
|
+
const matchingPageLoad = loadingPages.get(requestKey);
|
|
40198
|
+
if (matchingPageLoad)
|
|
40199
|
+
return await matchingPageLoad;
|
|
40200
|
+
const pendingPageLoad = pageLoadTail;
|
|
40201
|
+
const pageLoad = pendingPageLoad.then(async () => {
|
|
40202
|
+
if (generation !== activeDashboardGeneration)
|
|
40203
|
+
return { ...pages[pageIndex], canGoPrevious: pageIndex > 0 };
|
|
40204
|
+
if ("filters" in input) {
|
|
40205
|
+
const nextFilters = input.filters;
|
|
40206
|
+
const first = toDashboardPage(await operations.listSessions({
|
|
40207
|
+
client: operations.client,
|
|
40208
|
+
limit: 20,
|
|
40209
|
+
...nextFilters.statuses.length > 0 ? { statuses: nextFilters.statuses } : {},
|
|
40210
|
+
...nextFilters.creators.length > 0 ? { creatorUserIds: nextFilters.creators.map((creator) => creator.id) } : {},
|
|
40211
|
+
signal: input.signal
|
|
40212
|
+
}));
|
|
40213
|
+
if (generation !== activeDashboardGeneration)
|
|
40214
|
+
return first;
|
|
40215
|
+
filters = nextFilters;
|
|
40216
|
+
pages.splice(0, pages.length, first);
|
|
40217
|
+
pageIndex = 0;
|
|
40218
|
+
return first;
|
|
40219
|
+
}
|
|
40220
|
+
const { target, signal } = input;
|
|
40221
|
+
if (target === "current") {
|
|
40222
|
+
const after = pageIndex > 0 ? pages[pageIndex - 1]?.nextCursor ?? undefined : undefined;
|
|
40223
|
+
const refreshed = toDashboardPage(await operations.listSessions({
|
|
40224
|
+
client: operations.client,
|
|
40225
|
+
limit: 20,
|
|
40226
|
+
after,
|
|
40227
|
+
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
40228
|
+
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
40229
|
+
signal
|
|
40230
|
+
}));
|
|
40231
|
+
if (generation !== activeDashboardGeneration)
|
|
40232
|
+
return refreshed;
|
|
40233
|
+
pages.splice(pageIndex, pages.length - pageIndex, refreshed);
|
|
40234
|
+
return { ...refreshed, canGoPrevious: pageIndex > 0 };
|
|
40235
|
+
}
|
|
40236
|
+
if (target === "previous") {
|
|
40237
|
+
pageIndex = Math.max(0, pageIndex - 1);
|
|
40238
|
+
return { ...pages[pageIndex], canGoPrevious: pageIndex > 0 };
|
|
40239
|
+
}
|
|
40240
|
+
const current = pages[pageIndex];
|
|
40241
|
+
if (!current.hasMore || !current.nextCursor)
|
|
40242
|
+
return { ...current, canGoPrevious: pageIndex > 0 };
|
|
40243
|
+
const next = toDashboardPage(await operations.listSessions({
|
|
39186
40244
|
client: operations.client,
|
|
39187
40245
|
limit: 20,
|
|
39188
|
-
after,
|
|
40246
|
+
after: current.nextCursor,
|
|
40247
|
+
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
40248
|
+
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
39189
40249
|
signal
|
|
39190
40250
|
}));
|
|
39191
|
-
|
|
39192
|
-
|
|
39193
|
-
|
|
39194
|
-
|
|
39195
|
-
pageIndex
|
|
39196
|
-
return { ...
|
|
40251
|
+
if (generation !== activeDashboardGeneration)
|
|
40252
|
+
return next;
|
|
40253
|
+
pages.splice(pageIndex + 1);
|
|
40254
|
+
pages.push(next);
|
|
40255
|
+
pageIndex += 1;
|
|
40256
|
+
return { ...next, canGoPrevious: pageIndex > 0 };
|
|
40257
|
+
});
|
|
40258
|
+
loadingPages.set(requestKey, pageLoad);
|
|
40259
|
+
pageLoadTail = pageLoad.then(() => {
|
|
40260
|
+
return;
|
|
40261
|
+
}, () => {
|
|
40262
|
+
return;
|
|
40263
|
+
});
|
|
40264
|
+
try {
|
|
40265
|
+
return await pageLoad;
|
|
40266
|
+
} finally {
|
|
40267
|
+
if (loadingPages.get(requestKey) === pageLoad)
|
|
40268
|
+
loadingPages.delete(requestKey);
|
|
39197
40269
|
}
|
|
39198
|
-
|
|
39199
|
-
|
|
39200
|
-
|
|
39201
|
-
|
|
39202
|
-
|
|
39203
|
-
|
|
39204
|
-
|
|
39205
|
-
|
|
39206
|
-
}));
|
|
39207
|
-
pages.splice(pageIndex + 1);
|
|
39208
|
-
pages.push(next);
|
|
39209
|
-
pageIndex += 1;
|
|
39210
|
-
return { ...next, canGoPrevious: pageIndex > 0 };
|
|
39211
|
-
})();
|
|
39212
|
-
try {
|
|
39213
|
-
return await loadingPage;
|
|
39214
|
-
} finally {
|
|
39215
|
-
loadingPage = undefined;
|
|
39216
|
-
}
|
|
40270
|
+
};
|
|
40271
|
+
}
|
|
40272
|
+
async function loadDashboardAuthors() {
|
|
40273
|
+
if (authors)
|
|
40274
|
+
return authors;
|
|
40275
|
+
const loadedAuthors = await collectAllUsers({ listUsers: operations.listUsers, signal: dependencies.abortSignal });
|
|
40276
|
+
authors = loadedAuthors;
|
|
40277
|
+
return loadedAuthors;
|
|
39217
40278
|
}
|
|
39218
40279
|
let firstOpen = true;
|
|
39219
40280
|
holdTransitionScreen();
|
|
@@ -39222,17 +40283,30 @@ async function dashboard({
|
|
|
39222
40283
|
pages.splice(0, pages.length, toDashboardPage(await operations.listSessions({
|
|
39223
40284
|
client: operations.client,
|
|
39224
40285
|
limit: 20,
|
|
40286
|
+
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
40287
|
+
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
39225
40288
|
signal: dependencies.abortSignal
|
|
39226
40289
|
})));
|
|
39227
40290
|
pageIndex = 0;
|
|
39228
40291
|
}
|
|
39229
40292
|
firstOpen = false;
|
|
40293
|
+
const dashboardGeneration = ++activeDashboardGeneration;
|
|
40294
|
+
const loadDashboardPage = createDashboardPageLoader({ generation: dashboardGeneration });
|
|
39230
40295
|
const action = await renderRemyView({
|
|
39231
40296
|
open: async () => await openDashboardTui({
|
|
39232
40297
|
initialPage: pages[0],
|
|
40298
|
+
initialFilters: filters,
|
|
40299
|
+
loadAuthors: loadDashboardAuthors,
|
|
39233
40300
|
loadPage: loadDashboardPage
|
|
39234
40301
|
}),
|
|
39235
|
-
waitForAction: async (dashboardTui) =>
|
|
40302
|
+
waitForAction: async (dashboardTui) => {
|
|
40303
|
+
try {
|
|
40304
|
+
return await awaitWithAbort({ value: dashboardTui.waitForAction(), abortSignal: dependencies.abortSignal });
|
|
40305
|
+
} finally {
|
|
40306
|
+
if (activeDashboardGeneration === dashboardGeneration)
|
|
40307
|
+
activeDashboardGeneration += 1;
|
|
40308
|
+
}
|
|
40309
|
+
}
|
|
39236
40310
|
});
|
|
39237
40311
|
if (!action)
|
|
39238
40312
|
return 0;
|
|
@@ -39275,12 +40349,12 @@ async function prepareDashboardBootstrap({
|
|
|
39275
40349
|
return;
|
|
39276
40350
|
});
|
|
39277
40351
|
onLoadingSessions?.();
|
|
39278
|
-
const
|
|
40352
|
+
const initialSessionPage = await resolvedOperations.listSessions({
|
|
39279
40353
|
client: resolvedOperations.client,
|
|
39280
40354
|
limit: 20,
|
|
39281
40355
|
signal: dependencies.abortSignal
|
|
39282
|
-
})
|
|
39283
|
-
return { operations: resolvedOperations, repositories, initialSessions };
|
|
40356
|
+
});
|
|
40357
|
+
return { operations: resolvedOperations, repositories, initialSessions: toDashboardPage(initialSessionPage) };
|
|
39284
40358
|
}
|
|
39285
40359
|
async function prepareDashboardWithStartupSplash({
|
|
39286
40360
|
dependencies,
|
|
@@ -39471,11 +40545,15 @@ async function createNewSession({
|
|
|
39471
40545
|
throw new Error("All selected repositories must belong to the same GitHub installation.");
|
|
39472
40546
|
const fileIds = [];
|
|
39473
40547
|
for (const attachment of command.attachments) {
|
|
39474
|
-
|
|
39475
|
-
client: operations.client,
|
|
40548
|
+
const uploaded = await uploadLocalAttachment({
|
|
39476
40549
|
filePath: attachment,
|
|
39477
|
-
|
|
39478
|
-
|
|
40550
|
+
reserveAndUploadFile: async (input) => await operations.reserveAndUploadFile({
|
|
40551
|
+
...input,
|
|
40552
|
+
client: operations.client,
|
|
40553
|
+
idempotencyKey: randomUUID5()
|
|
40554
|
+
})
|
|
40555
|
+
});
|
|
40556
|
+
fileIds.push(uploaded.fileId);
|
|
39479
40557
|
}
|
|
39480
40558
|
const created = await operations.createCodexSession({
|
|
39481
40559
|
client: operations.client,
|
|
@@ -39549,6 +40627,7 @@ async function createFromTuiDraft({
|
|
|
39549
40627
|
input: {
|
|
39550
40628
|
installationId: draft.githubInstallationId,
|
|
39551
40629
|
repositoryIds: draft.repositories.map((repository) => repository.id),
|
|
40630
|
+
repositoryBranchOverrides: draft.repositoryBranchOverrides ?? [],
|
|
39552
40631
|
prompt: draft.prompt,
|
|
39553
40632
|
fileIds: draft.attachments.map((attachment) => attachment.fileId),
|
|
39554
40633
|
model: draft.model,
|
|
@@ -39571,10 +40650,11 @@ async function openTuiNewSessionWizard({
|
|
|
39571
40650
|
repositories,
|
|
39572
40651
|
reloadRepositories,
|
|
39573
40652
|
suggestRepositories: operations.suggestRepositories,
|
|
40653
|
+
suggestBranches: operations.suggestBranches,
|
|
39574
40654
|
initialDraft,
|
|
39575
40655
|
initialError,
|
|
39576
40656
|
readClipboardImage,
|
|
39577
|
-
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory:
|
|
40657
|
+
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory: tmpdir2() }),
|
|
39578
40658
|
attachPromptPaths: async (input) => await uploadPromptPaths({ operations, ...input })
|
|
39579
40659
|
}),
|
|
39580
40660
|
waitForAction: async (wizard) => await awaitWithAbort({ value: wizard.waitForDraft(), abortSignal: dependencies.abortSignal })
|
|
@@ -39583,15 +40663,20 @@ async function openTuiNewSessionWizard({
|
|
|
39583
40663
|
async function uploadPromptPaths({
|
|
39584
40664
|
operations,
|
|
39585
40665
|
text,
|
|
39586
|
-
excludedPaths
|
|
40666
|
+
excludedPaths,
|
|
40667
|
+
selectedPaths,
|
|
40668
|
+
onUploadStart
|
|
39587
40669
|
}) {
|
|
39588
40670
|
return await attachPromptPaths({
|
|
39589
40671
|
text,
|
|
39590
40672
|
excludedPaths,
|
|
40673
|
+
selectedPaths,
|
|
40674
|
+
onUploadStart,
|
|
39591
40675
|
cwd: process.cwd(),
|
|
39592
|
-
reserveAndUploadFile: async ({ filePath }) => await operations.reserveAndUploadFile({
|
|
40676
|
+
reserveAndUploadFile: async ({ filePath, mediaType }) => await operations.reserveAndUploadFile({
|
|
39593
40677
|
client: operations.client,
|
|
39594
40678
|
filePath,
|
|
40679
|
+
...mediaType ? { mediaType } : {},
|
|
39595
40680
|
idempotencyKey: randomUUID5()
|
|
39596
40681
|
})
|
|
39597
40682
|
});
|
|
@@ -39649,7 +40734,7 @@ async function runAttachedSession({
|
|
|
39649
40734
|
...activeMessageId ? { activeMessageId } : {},
|
|
39650
40735
|
environment: dependencies.environment,
|
|
39651
40736
|
getSession: async ({ sessionId: id }) => await operations.getSession({ client: operations.client, sessionId: id }),
|
|
39652
|
-
listSessionEvents: async ({ sessionId: id, limit, after }) => await operations.listSessionEvents({ client: operations.client, sessionId: id, limit, after }),
|
|
40737
|
+
listSessionEvents: async ({ sessionId: id, limit, after, signal }) => await operations.listSessionEvents({ client: operations.client, sessionId: id, limit, after, signal }),
|
|
39653
40738
|
openEventStream: ({ sessionId: id, lastRetainedEventId, signal, onSynchronized }) => operations.streamSessionEvents({ client: operations.client, sessionId: id, lastRetainedEventId, signal, onSynchronized })
|
|
39654
40739
|
});
|
|
39655
40740
|
const interactive = !noTui && !json3 && isInteractiveTerminal(dependencies);
|
|
@@ -39688,8 +40773,14 @@ async function runAttachedSession({
|
|
|
39688
40773
|
});
|
|
39689
40774
|
}
|
|
39690
40775
|
const startPromise = controller.start(start);
|
|
39691
|
-
await Promise.race([
|
|
40776
|
+
await Promise.race([
|
|
40777
|
+
controller.waitUntilReady(),
|
|
40778
|
+
startPromise,
|
|
40779
|
+
...aborted3 ? [aborted3] : []
|
|
40780
|
+
]);
|
|
39692
40781
|
if (interactive) {
|
|
40782
|
+
if (dependencies.abortSignal?.aborted)
|
|
40783
|
+
throw dependencies.abortSignal.reason ?? new Error("interrupted");
|
|
39693
40784
|
const openSessionTui = dependencies.openSessionTui ?? createSessionTui;
|
|
39694
40785
|
let composerSnapshot;
|
|
39695
40786
|
const holdTransitionScreen = () => showRemyTransitionScreen({ write: (chunk) => dependencies.output.writeStdout(chunk) });
|
|
@@ -39726,7 +40817,7 @@ async function runAttachedSession({
|
|
|
39726
40817
|
controller.updateDetail(detail);
|
|
39727
40818
|
},
|
|
39728
40819
|
readClipboardImage,
|
|
39729
|
-
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory:
|
|
40820
|
+
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory: tmpdir2() }),
|
|
39730
40821
|
attachPromptPaths: async (input) => await uploadPromptPaths({ operations, ...input })
|
|
39731
40822
|
}),
|
|
39732
40823
|
waitForAction: async (tui) => await Promise.race([tui.waitForAction(), aborted3])
|
|
@@ -39835,7 +40926,9 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
|
|
|
39835
40926
|
return {
|
|
39836
40927
|
client: dependencies.sessionClient,
|
|
39837
40928
|
listRepositories: dependencies.listRepositories ?? (async () => await Promise.reject(new Error("Missing remote repository operation."))),
|
|
40929
|
+
listUsers: dependencies.listUsers ?? (async (input) => await listRemoteUsers({ client: dependencies.sessionClient, ...input })),
|
|
39838
40930
|
suggestRepositories: dependencies.suggestRepositories ?? (async () => await Promise.reject(new Error("Missing remote repository suggestions operation."))),
|
|
40931
|
+
suggestBranches: dependencies.suggestBranches ?? (async () => await Promise.reject(new Error("Missing remote branch suggestions operation."))),
|
|
39839
40932
|
reserveAndUploadFile: dependencies.reserveAndUploadFile ?? reserveAndUploadFile,
|
|
39840
40933
|
createCodexSession: dependencies.createCodexSession ?? createCodexSession,
|
|
39841
40934
|
appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
|
|
@@ -39853,7 +40946,9 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
|
|
|
39853
40946
|
return {
|
|
39854
40947
|
client,
|
|
39855
40948
|
listRepositories: dependencies.listRepositories ?? createRepositoryLister({ client }),
|
|
40949
|
+
listUsers: dependencies.listUsers ?? (async (input) => await listRemoteUsers({ client, ...input })),
|
|
39856
40950
|
suggestRepositories: dependencies.suggestRepositories ?? createRepositorySuggester({ client }),
|
|
40951
|
+
suggestBranches: dependencies.suggestBranches ?? createBranchSuggester({ client }),
|
|
39857
40952
|
reserveAndUploadFile: dependencies.reserveAndUploadFile ?? reserveAndUploadFile,
|
|
39858
40953
|
createCodexSession: dependencies.createCodexSession ?? createCodexSession,
|
|
39859
40954
|
appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
|
|
@@ -39895,6 +40990,20 @@ async function collectAllRepositories({
|
|
|
39895
40990
|
after = page.nextCursor;
|
|
39896
40991
|
}
|
|
39897
40992
|
}
|
|
40993
|
+
async function collectAllUsers({
|
|
40994
|
+
listUsers,
|
|
40995
|
+
signal
|
|
40996
|
+
}) {
|
|
40997
|
+
const users = [];
|
|
40998
|
+
let after;
|
|
40999
|
+
while (true) {
|
|
41000
|
+
const page = await listUsers({ limit: 100, after, signal });
|
|
41001
|
+
users.push(...page.data);
|
|
41002
|
+
if (!page.hasMore || !page.nextCursor)
|
|
41003
|
+
return users;
|
|
41004
|
+
after = page.nextCursor;
|
|
41005
|
+
}
|
|
41006
|
+
}
|
|
39898
41007
|
function resolveSessionCachePathForCommand({ dependencies, sessionId }) {
|
|
39899
41008
|
const stateBase = dependencies.environment.XDG_STATE_HOME ?? (dependencies.environment.HOME ? join6(dependencies.environment.HOME, ".local/state") : undefined);
|
|
39900
41009
|
if (!stateBase)
|
|
@@ -40021,7 +41130,7 @@ Options:
|
|
|
40021
41130
|
--installation <id> Select a GitHub installation
|
|
40022
41131
|
--model <name> Select an agent model
|
|
40023
41132
|
--reasoning-effort <low|medium|high|xhigh> Select agent reasoning effort
|
|
40024
|
-
--attach <path> Upload a file; repeat
|
|
41133
|
+
--attach <path> Upload a file or directory (20 MiB max); repeat as needed
|
|
40025
41134
|
--no-tui Do not open the interactive terminal view
|
|
40026
41135
|
--json Write session updates as JSON
|
|
40027
41136
|
--help, -h Show this help
|
|
@@ -40170,7 +41279,7 @@ async function removeCommittedTokenRecoveryArtifacts({
|
|
|
40170
41279
|
const directory = dirname6(tokenPath);
|
|
40171
41280
|
let entries;
|
|
40172
41281
|
try {
|
|
40173
|
-
entries = await
|
|
41282
|
+
entries = await readdir3(directory);
|
|
40174
41283
|
} catch (error93) {
|
|
40175
41284
|
if (isMissingFileError3(error93))
|
|
40176
41285
|
return;
|