@meistrari/remy-cli 1.13.0 → 1.14.1
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 +1532 -398
- 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 });
|
|
@@ -33908,6 +34069,10 @@ var sessionMessageTurnAssociatedEventSchema = exports_external2.object({
|
|
|
33908
34069
|
type: exports_external2.literal("session.message.turn-associated"),
|
|
33909
34070
|
payload: exports_external2.object({ messageId: exports_external2.string().min(1), commandId: exports_external2.string().min(1), turnId: exports_external2.string().min(1) }).passthrough()
|
|
33910
34071
|
}).passthrough();
|
|
34072
|
+
var sessionMessageDispatchEventSchema = exports_external2.object({
|
|
34073
|
+
type: exports_external2.enum(["session.message.dispatched", "session.message.withdrawn", "session.message.failed"]),
|
|
34074
|
+
payload: exports_external2.object({ messageId: exports_external2.string().min(1), commandId: exports_external2.string().min(1) }).passthrough()
|
|
34075
|
+
}).passthrough();
|
|
33911
34076
|
var sessionWorkspaceGitInitializedEventSchema = exports_external2.object({
|
|
33912
34077
|
type: exports_external2.literal("session.workspace.git.initialized"),
|
|
33913
34078
|
payload: exports_external2.object({
|
|
@@ -33971,7 +34136,9 @@ function createSessionViewState({ detail, activeMessageId }) {
|
|
|
33971
34136
|
kind: "session-view",
|
|
33972
34137
|
sessionId: detail.id,
|
|
33973
34138
|
...detail.sessionNumber === undefined ? {} : { sessionNumber: detail.sessionNumber },
|
|
34139
|
+
title: detail.title ?? null,
|
|
33974
34140
|
aggregateStatus: detail.status,
|
|
34141
|
+
...detail.agentStatus === undefined ? {} : { agentStatus: detail.agentStatus },
|
|
33975
34142
|
connectionStatus: "connected",
|
|
33976
34143
|
pullRequest: toSessionPullRequest(detail),
|
|
33977
34144
|
...activeMessageId !== undefined ? { activeMessageId } : {},
|
|
@@ -34004,7 +34171,9 @@ function updateSessionDetail({ state, detail }) {
|
|
|
34004
34171
|
...state,
|
|
34005
34172
|
sessionId: detail.id,
|
|
34006
34173
|
...detail.sessionNumber === undefined ? {} : { sessionNumber: detail.sessionNumber },
|
|
34174
|
+
..."title" in detail ? { title: detail.title ?? null } : {},
|
|
34007
34175
|
aggregateStatus: detail.status,
|
|
34176
|
+
...detail.agentStatus === undefined ? {} : { agentStatus: detail.agentStatus },
|
|
34008
34177
|
pullRequest: toSessionPullRequest(detail),
|
|
34009
34178
|
connectionPreviews: toConnectionPreviews(detail)
|
|
34010
34179
|
};
|
|
@@ -34040,6 +34209,8 @@ function sessionTurnOutcome({ state, sessionMessageId }) {
|
|
|
34040
34209
|
const turn = state.messageTurns[sessionMessageId];
|
|
34041
34210
|
if (!turn)
|
|
34042
34211
|
return { status: "unassociated" };
|
|
34212
|
+
if (turn.turnId === null)
|
|
34213
|
+
return { status: "ended", turnId: null, outcome: turn.outcome };
|
|
34043
34214
|
if (turn.outcome)
|
|
34044
34215
|
return { status: "ended", turnId: turn.turnId, outcome: turn.outcome };
|
|
34045
34216
|
return { status: "pending", turnId: turn.turnId };
|
|
@@ -34063,10 +34234,11 @@ function projectRetainedEvent({
|
|
|
34063
34234
|
}) {
|
|
34064
34235
|
const messageCreated = sessionMessageCreatedEventSchema.safeParse(event);
|
|
34065
34236
|
const turnAssociated = sessionMessageTurnAssociatedEventSchema.safeParse(event);
|
|
34237
|
+
const messageDispatch = sessionMessageDispatchEventSchema.safeParse(event);
|
|
34066
34238
|
const workspaceGitInitialized = sessionWorkspaceGitInitializedEventSchema.safeParse(event);
|
|
34067
34239
|
const workspaceGitRevision = sessionWorkspaceGitRevisionEventSchema.safeParse(event);
|
|
34068
34240
|
const turnEnded = agentTurnEndedEventSchema.safeParse(event);
|
|
34069
|
-
let projected = messageCreated.success ? projectSessionMessageCreated({ state, event: messageCreated.data, occurredAt }) : turnAssociated.success ? projectSessionMessageTurnAssociated({ state, event: turnAssociated.data }) : workspaceGitInitialized.success ? projectSessionWorkspaceGitInitialized({ state, event: workspaceGitInitialized.data, occurredAt, retainedEventId }) : workspaceGitRevision.success ? projectSessionWorkspaceGitRevision({ state, event: workspaceGitRevision.data, occurredAt, retainedEventId }) : turnEnded.success ? projectAgentTurnEnded({ state, event: turnEnded.data, occurredAt, retainedEventId }) : projectDurableAgentEvent({ state, event, occurredAt, retainedEventId });
|
|
34241
|
+
let projected = messageCreated.success ? projectSessionMessageCreated({ state, event: messageCreated.data, occurredAt }) : turnAssociated.success ? projectSessionMessageTurnAssociated({ state, event: turnAssociated.data }) : messageDispatch.success ? projectSessionMessageDispatch({ state, event: messageDispatch.data, occurredAt, retainedEventId }) : workspaceGitInitialized.success ? projectSessionWorkspaceGitInitialized({ state, event: workspaceGitInitialized.data, occurredAt, retainedEventId }) : workspaceGitRevision.success ? projectSessionWorkspaceGitRevision({ state, event: workspaceGitRevision.data, occurredAt, retainedEventId }) : turnEnded.success ? projectAgentTurnEnded({ state, event: turnEnded.data, occurredAt, retainedEventId }) : projectDurableAgentEvent({ state, event, occurredAt, retainedEventId });
|
|
34070
34242
|
projected = recordAgentLineageEvent({ state: projected, event, retainedEventId });
|
|
34071
34243
|
if (artifact) {
|
|
34072
34244
|
const publication = artifact.kind === "file" ? { artifactId: artifact.artifact_id, kind: artifact.kind, title: artifact.filename } : { artifactId: artifact.artifact_id, kind: artifact.kind, title: artifact.title, url: artifact.url };
|
|
@@ -34076,7 +34248,9 @@ function projectRetainedEvent({
|
|
|
34076
34248
|
artifactId: artifact.artifact_id,
|
|
34077
34249
|
occurredAt,
|
|
34078
34250
|
title: artifact.filename,
|
|
34079
|
-
mediaType: artifact.media_type
|
|
34251
|
+
mediaType: artifact.media_type,
|
|
34252
|
+
...artifact.download_url === undefined ? {} : { downloadUrl: artifact.download_url },
|
|
34253
|
+
...artifact.preview_url === undefined ? {} : { previewUrl: artifact.preview_url }
|
|
34080
34254
|
} : {
|
|
34081
34255
|
kind: "artifact",
|
|
34082
34256
|
artifactKind: artifact.kind,
|
|
@@ -34152,6 +34326,24 @@ function projectSessionMessageTurnAssociated({ state, event }) {
|
|
|
34152
34326
|
};
|
|
34153
34327
|
return { ...state, messageTurns: { ...state.messageTurns, [event.payload.messageId]: nextTurn } };
|
|
34154
34328
|
}
|
|
34329
|
+
function projectSessionMessageDispatch({ state, event, occurredAt, retainedEventId }) {
|
|
34330
|
+
if (event.type === "session.message.dispatched")
|
|
34331
|
+
return state;
|
|
34332
|
+
const withdrawn = event.type === "session.message.withdrawn";
|
|
34333
|
+
const outcome = withdrawn ? "cancelled" : "failed";
|
|
34334
|
+
return appendActivity({
|
|
34335
|
+
state: {
|
|
34336
|
+
...state,
|
|
34337
|
+
messageTurns: {
|
|
34338
|
+
...state.messageTurns,
|
|
34339
|
+
[event.payload.messageId]: { commandId: event.payload.commandId, turnId: null, outcome }
|
|
34340
|
+
}
|
|
34341
|
+
},
|
|
34342
|
+
occurredAt,
|
|
34343
|
+
retainedEventId,
|
|
34344
|
+
card: withdrawn ? { kind: "lifecycle", weight: "signal", title: "Message withdrawn", summary: "Message was withdrawn before reaching Remy." } : { kind: "failure", weight: "signal", title: "Message failed", summary: "Message failed before reaching Remy." }
|
|
34345
|
+
});
|
|
34346
|
+
}
|
|
34155
34347
|
function projectSessionWorkspaceGitInitialized({ state, event, occurredAt, retainedEventId }) {
|
|
34156
34348
|
return appendActivity({
|
|
34157
34349
|
state,
|
|
@@ -35061,12 +35253,12 @@ function createRemoteSessionController(dependencies) {
|
|
|
35061
35253
|
detail: input.detail,
|
|
35062
35254
|
activeMessageId: dependencies.activeMessageId ?? cache?.activeMessageId
|
|
35063
35255
|
});
|
|
35064
|
-
resolveReady();
|
|
35065
|
-
publishState();
|
|
35066
35256
|
if (input.mode === "cold-resume") {
|
|
35067
35257
|
const hydrationGeneration = beginReasoningAttempt();
|
|
35068
35258
|
await hydrateRetainedHistoryFromBeginning(hydrationGeneration);
|
|
35069
35259
|
}
|
|
35260
|
+
resolveReady();
|
|
35261
|
+
publishState();
|
|
35070
35262
|
await streamWithControllerReconnects();
|
|
35071
35263
|
}
|
|
35072
35264
|
function stop() {
|
|
@@ -35102,7 +35294,8 @@ function createRemoteSessionController(dependencies) {
|
|
|
35102
35294
|
async function recordActiveMessage(sessionMessageId) {
|
|
35103
35295
|
state = {
|
|
35104
35296
|
...getState(),
|
|
35105
|
-
activeMessageId: sessionMessageId
|
|
35297
|
+
activeMessageId: sessionMessageId,
|
|
35298
|
+
agentStatus: undefined
|
|
35106
35299
|
};
|
|
35107
35300
|
await writeCache();
|
|
35108
35301
|
publishState();
|
|
@@ -35123,7 +35316,8 @@ function createRemoteSessionController(dependencies) {
|
|
|
35123
35316
|
const page = await dependencies.listSessionEvents({
|
|
35124
35317
|
sessionId: dependencies.sessionId,
|
|
35125
35318
|
limit: sessionHistoryPageSize,
|
|
35126
|
-
after
|
|
35319
|
+
after,
|
|
35320
|
+
signal: abortController.signal
|
|
35127
35321
|
});
|
|
35128
35322
|
for (const item of page.data) {
|
|
35129
35323
|
await reduceFrameAndPersist({
|
|
@@ -35377,7 +35571,10 @@ async function sleepMs(ms, signal) {
|
|
|
35377
35571
|
}
|
|
35378
35572
|
|
|
35379
35573
|
// 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";
|
|
35574
|
+
import { bg, BoxRenderable as BoxRenderable2, bold as bold2, CliRenderEvents as CliRenderEvents2, fg as fg3, ScrollBoxRenderable, stringToStyledText as stringToStyledText2, TextRenderable as TextRenderable2 } from "@opentui/core";
|
|
35575
|
+
|
|
35576
|
+
// src/tui/composer.ts
|
|
35577
|
+
import { BoxRenderable, TextRenderable, TextareaRenderable } from "@opentui/core";
|
|
35381
35578
|
|
|
35382
35579
|
// src/tui/composer-divider.ts
|
|
35383
35580
|
import { dim as dim2, fg as fg2, StyledText as StyledText2 } from "@opentui/core";
|
|
@@ -35572,6 +35769,98 @@ function renderComposerDivider({ width, tag, leadLabel }) {
|
|
|
35572
35769
|
]);
|
|
35573
35770
|
}
|
|
35574
35771
|
|
|
35772
|
+
// src/tui/composer-height.ts
|
|
35773
|
+
var composerMaxRows = 6;
|
|
35774
|
+
function resizeComposer(composer) {
|
|
35775
|
+
composer.height = Math.min(Math.max(composer.editorView.getTotalVirtualLineCount(), 1), composerMaxRows);
|
|
35776
|
+
}
|
|
35777
|
+
|
|
35778
|
+
// src/tui/composer.ts
|
|
35779
|
+
var defaultPlaceholder = "Message Remy\u2026 Tab paths attach \xB7 Enter/Shift+Enter send \xB7 Option+Enter newline";
|
|
35780
|
+
function createComposer({
|
|
35781
|
+
renderer,
|
|
35782
|
+
parent,
|
|
35783
|
+
tag,
|
|
35784
|
+
topDividerLabel,
|
|
35785
|
+
placeholder = defaultPlaceholder,
|
|
35786
|
+
screenPaddingX = 1,
|
|
35787
|
+
onSubmit,
|
|
35788
|
+
onContentChange
|
|
35789
|
+
}) {
|
|
35790
|
+
const root = new BoxRenderable(renderer, { width: "100%", flexDirection: "column", flexShrink: 0 });
|
|
35791
|
+
const dividerTop = new TextRenderable(renderer, { content: "", flexShrink: 0 });
|
|
35792
|
+
const textarea = new TextareaRenderable(renderer, {
|
|
35793
|
+
width: "100%",
|
|
35794
|
+
height: 1,
|
|
35795
|
+
flexShrink: 0,
|
|
35796
|
+
wrapMode: "word",
|
|
35797
|
+
placeholder,
|
|
35798
|
+
keyBindings: [
|
|
35799
|
+
{ name: "return", action: "submit" },
|
|
35800
|
+
{ name: "return", shift: true, action: "submit" },
|
|
35801
|
+
{ name: "return", meta: true, action: "newline" },
|
|
35802
|
+
{ name: "j", ctrl: true, action: "newline" }
|
|
35803
|
+
],
|
|
35804
|
+
onSubmit
|
|
35805
|
+
});
|
|
35806
|
+
const dividerBottom = new TextRenderable(renderer, { content: "", flexShrink: 0 });
|
|
35807
|
+
let mounted = false;
|
|
35808
|
+
root.add(dividerTop);
|
|
35809
|
+
root.add(textarea);
|
|
35810
|
+
root.add(dividerBottom);
|
|
35811
|
+
textarea.onContentChange = () => {
|
|
35812
|
+
resizeComposer(textarea);
|
|
35813
|
+
onContentChange?.();
|
|
35814
|
+
};
|
|
35815
|
+
function render() {
|
|
35816
|
+
const dividerWidth = renderer.width - screenPaddingX * 2;
|
|
35817
|
+
dividerTop.content = renderComposerDivider({ width: dividerWidth, tag, leadLabel: topDividerLabel?.() });
|
|
35818
|
+
dividerBottom.content = renderComposerDivider({ width: dividerWidth });
|
|
35819
|
+
}
|
|
35820
|
+
return {
|
|
35821
|
+
textarea,
|
|
35822
|
+
mount() {
|
|
35823
|
+
if (mounted)
|
|
35824
|
+
return;
|
|
35825
|
+
mounted = true;
|
|
35826
|
+
parent.add(root);
|
|
35827
|
+
resizeComposer(textarea);
|
|
35828
|
+
render();
|
|
35829
|
+
},
|
|
35830
|
+
unmount() {
|
|
35831
|
+
if (!mounted)
|
|
35832
|
+
return;
|
|
35833
|
+
mounted = false;
|
|
35834
|
+
parent.remove(root);
|
|
35835
|
+
},
|
|
35836
|
+
render,
|
|
35837
|
+
focus() {
|
|
35838
|
+
textarea.focus();
|
|
35839
|
+
},
|
|
35840
|
+
destroy() {
|
|
35841
|
+
if (mounted)
|
|
35842
|
+
parent.remove(root);
|
|
35843
|
+
mounted = false;
|
|
35844
|
+
root.destroyRecursively();
|
|
35845
|
+
}
|
|
35846
|
+
};
|
|
35847
|
+
}
|
|
35848
|
+
|
|
35849
|
+
// src/tui/fuzzy-match.ts
|
|
35850
|
+
function fuzzyMatches(value, query) {
|
|
35851
|
+
const normalizedQuery = query.replaceAll(/\s/g, "").toLowerCase();
|
|
35852
|
+
if (!normalizedQuery)
|
|
35853
|
+
return true;
|
|
35854
|
+
let queryIndex = 0;
|
|
35855
|
+
for (const character of value.toLowerCase()) {
|
|
35856
|
+
if (character === normalizedQuery[queryIndex])
|
|
35857
|
+
queryIndex += 1;
|
|
35858
|
+
if (queryIndex === normalizedQuery.length)
|
|
35859
|
+
return true;
|
|
35860
|
+
}
|
|
35861
|
+
return false;
|
|
35862
|
+
}
|
|
35863
|
+
|
|
35575
35864
|
// src/tui/renderer.ts
|
|
35576
35865
|
import { CliRenderEvents, createClipboard, createCliRenderer, createHostClipboard, createRendererClipboardAdapter } from "@opentui/core";
|
|
35577
35866
|
var rendererDestroyed = new WeakMap;
|
|
@@ -35654,8 +35943,17 @@ async function renderRemyView({
|
|
|
35654
35943
|
|
|
35655
35944
|
// src/tui/dashboard.ts
|
|
35656
35945
|
var DASHBOARD_POLL_INTERVAL_MS = 1e4;
|
|
35946
|
+
var lifecycleStatusOptions = [
|
|
35947
|
+
{ value: "open", label: "Open" },
|
|
35948
|
+
{ value: "completed", label: "Completed" },
|
|
35949
|
+
{ value: "failed", label: "Failed" },
|
|
35950
|
+
{ value: "cancelled", label: "Cancelled" }
|
|
35951
|
+
];
|
|
35657
35952
|
async function createDashboardTui({
|
|
35658
35953
|
initialPage,
|
|
35954
|
+
initialFilters = { statuses: [], creators: [] },
|
|
35955
|
+
authors: initialAuthors = [],
|
|
35956
|
+
loadAuthors,
|
|
35659
35957
|
loadPage,
|
|
35660
35958
|
createRenderer = createDefaultRenderer
|
|
35661
35959
|
}) {
|
|
@@ -35664,6 +35962,7 @@ async function createDashboardTui({
|
|
|
35664
35962
|
let keyHandler;
|
|
35665
35963
|
let resizeHandler;
|
|
35666
35964
|
let refreshTimer;
|
|
35965
|
+
let contentScrollHandler;
|
|
35667
35966
|
let rendererDestroyed2 = false;
|
|
35668
35967
|
const loadPageAbortController = new AbortController;
|
|
35669
35968
|
try {
|
|
@@ -35675,28 +35974,61 @@ async function createDashboardTui({
|
|
|
35675
35974
|
loadPageAbortController.abort();
|
|
35676
35975
|
}, render = function() {
|
|
35677
35976
|
const contentWidth = renderer.width - 4;
|
|
35678
|
-
const
|
|
35977
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
35978
|
+
const rowsBelow = rowsBelowVisibleWindow({ sessionCount: sessions.length, selectedIndex, height: renderer.height });
|
|
35679
35979
|
const isNarrow = renderer.width < 72;
|
|
35680
|
-
const selectedSession =
|
|
35681
|
-
const sessionRows =
|
|
35980
|
+
const selectedSession = filterMode ? undefined : sessions[selectedIndex];
|
|
35981
|
+
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
35982
|
session: selectedSession,
|
|
35683
35983
|
selectedIndex,
|
|
35684
|
-
sessionCount:
|
|
35984
|
+
sessionCount: sessions.length,
|
|
35685
35985
|
width: contentWidth
|
|
35686
35986
|
}) : formatSessionList({
|
|
35687
|
-
sessions
|
|
35987
|
+
sessions,
|
|
35688
35988
|
selectedIndex,
|
|
35689
35989
|
width: contentWidth,
|
|
35690
35990
|
height: renderer.height
|
|
35691
35991
|
});
|
|
35692
|
-
statusBand.content = dashboardStatusBand({ page, pageIndex });
|
|
35992
|
+
statusBand.content = dashboardStatusBand({ page, pageIndex, filters, sessionSearchQuery });
|
|
35693
35993
|
content.content = joinStyled([stringToStyledText2(""), sessionRows], `
|
|
35694
35994
|
`);
|
|
35995
|
+
desiredContentScrollY = filterMode ? filterMode.selectedIndex + 2 : 0;
|
|
35996
|
+
if (!contentScrollHandler) {
|
|
35997
|
+
contentScrollHandler = () => {
|
|
35998
|
+
contentScrollHandler = undefined;
|
|
35999
|
+
if (destroyed)
|
|
36000
|
+
return;
|
|
36001
|
+
listRegion.scrollTo({ x: 0, y: desiredContentScrollY });
|
|
36002
|
+
renderer.requestRender();
|
|
36003
|
+
};
|
|
36004
|
+
renderer.once(CliRenderEvents2.FRAME, contentScrollHandler);
|
|
36005
|
+
}
|
|
35695
36006
|
const showPreview = !isNarrow && selectedSession !== undefined;
|
|
35696
36007
|
previewBand.visible = showPreview;
|
|
35697
36008
|
previewBand.content = showPreview ? formatSessionPreview({ session: selectedSession, width: contentWidth }) : stringToStyledText2("");
|
|
35698
|
-
|
|
35699
|
-
|
|
36009
|
+
const activeSearchComposer = filterMode?.searching ? "filter" : sessionSearchOpen ? "session" : undefined;
|
|
36010
|
+
if (activeSearchComposer !== mountedSearchComposer) {
|
|
36011
|
+
if (mountedSearchComposer === "filter")
|
|
36012
|
+
filterSearchComposer.unmount();
|
|
36013
|
+
else if (mountedSearchComposer === "session")
|
|
36014
|
+
sessionSearchComposer.unmount();
|
|
36015
|
+
mountedSearchComposer = activeSearchComposer;
|
|
36016
|
+
if (activeSearchComposer === "filter") {
|
|
36017
|
+
filterSearchComposer.mount();
|
|
36018
|
+
filterSearchComposer.focus();
|
|
36019
|
+
} else if (activeSearchComposer === "session") {
|
|
36020
|
+
sessionSearchComposer.mount();
|
|
36021
|
+
sessionSearchComposer.focus();
|
|
36022
|
+
} else {
|
|
36023
|
+
listRegion.focus();
|
|
36024
|
+
}
|
|
36025
|
+
}
|
|
36026
|
+
if (activeSearchComposer === "filter")
|
|
36027
|
+
filterSearchComposer.render();
|
|
36028
|
+
else if (activeSearchComposer === "session")
|
|
36029
|
+
sessionSearchComposer.render();
|
|
36030
|
+
separator.content = activeSearchComposer ? "" : renderComposerDivider({ width: contentWidth });
|
|
36031
|
+
footer.content = filterMode ? dashboardFilterFooter({ mode: filterMode, loadingPage, loadingAuthors, pageError, authorError }) : dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen, sessionSearchOpen });
|
|
35700
36032
|
renderer.requestRender();
|
|
35701
36033
|
}, finish = function(value) {
|
|
35702
36034
|
if (settled)
|
|
@@ -35711,9 +36043,40 @@ async function createDashboardTui({
|
|
|
35711
36043
|
stopPageRefresh();
|
|
35712
36044
|
rejectDraft(error93);
|
|
35713
36045
|
}, moveSelectionTo = function(index) {
|
|
35714
|
-
|
|
36046
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
36047
|
+
if (sessions.length === 0)
|
|
36048
|
+
return;
|
|
36049
|
+
selectedIndex = Math.max(0, Math.min(index, sessions.length - 1));
|
|
36050
|
+
render();
|
|
36051
|
+
}, applyFilterSearch = function() {
|
|
36052
|
+
if (!filterMode?.searching)
|
|
36053
|
+
return;
|
|
36054
|
+
filterMode = { ...filterMode, searching: false, selectedIndex: 0 };
|
|
36055
|
+
filterSearchComposer.textarea.setText("");
|
|
36056
|
+
render();
|
|
36057
|
+
}, cancelFilterSearch = function() {
|
|
36058
|
+
if (!filterMode?.searching)
|
|
36059
|
+
return;
|
|
36060
|
+
filterMode = { ...filterMode, searching: false, query: "", selectedIndex: 0 };
|
|
36061
|
+
filterSearchComposer.textarea.setText("");
|
|
36062
|
+
render();
|
|
36063
|
+
}, openSessionSearch = function() {
|
|
36064
|
+
sessionSearchOpen = true;
|
|
36065
|
+
sessionSearchComposer.textarea.setText(sessionSearchQuery);
|
|
36066
|
+
render();
|
|
36067
|
+
}, applySessionSearch = function() {
|
|
36068
|
+
if (!sessionSearchOpen)
|
|
35715
36069
|
return;
|
|
35716
|
-
|
|
36070
|
+
sessionSearchOpen = false;
|
|
36071
|
+
selectedIndex = 0;
|
|
36072
|
+
render();
|
|
36073
|
+
}, cancelSessionSearch = function() {
|
|
36074
|
+
if (!sessionSearchOpen)
|
|
36075
|
+
return;
|
|
36076
|
+
sessionSearchOpen = false;
|
|
36077
|
+
sessionSearchQuery = "";
|
|
36078
|
+
selectedIndex = 0;
|
|
36079
|
+
sessionSearchComposer.textarea.setText("");
|
|
35717
36080
|
render();
|
|
35718
36081
|
}, handleSessionKey = function(key) {
|
|
35719
36082
|
if (logoutConfirmationOpen) {
|
|
@@ -35726,6 +36089,84 @@ async function createDashboardTui({
|
|
|
35726
36089
|
}
|
|
35727
36090
|
return;
|
|
35728
36091
|
}
|
|
36092
|
+
if (sessionSearchOpen) {
|
|
36093
|
+
if (key.name === "escape") {
|
|
36094
|
+
key.preventDefault();
|
|
36095
|
+
cancelSessionSearch();
|
|
36096
|
+
}
|
|
36097
|
+
return;
|
|
36098
|
+
}
|
|
36099
|
+
if (filterMode) {
|
|
36100
|
+
const options = visibleDashboardFilterOptions({ mode: filterMode, authors });
|
|
36101
|
+
if (key.name === "escape") {
|
|
36102
|
+
key.preventDefault();
|
|
36103
|
+
if (filterMode.searching) {
|
|
36104
|
+
cancelFilterSearch();
|
|
36105
|
+
return;
|
|
36106
|
+
}
|
|
36107
|
+
filterMode = undefined;
|
|
36108
|
+
pageError = undefined;
|
|
36109
|
+
authorError = undefined;
|
|
36110
|
+
render();
|
|
36111
|
+
return;
|
|
36112
|
+
}
|
|
36113
|
+
if (filterMode.searching)
|
|
36114
|
+
return;
|
|
36115
|
+
if (loadingAuthors) {
|
|
36116
|
+
key.preventDefault();
|
|
36117
|
+
return;
|
|
36118
|
+
}
|
|
36119
|
+
const filterListFocused = renderer.currentFocusedRenderable === listRegion;
|
|
36120
|
+
if (filterListFocused && (key.name === "G" || key.name === "g" && key.shift)) {
|
|
36121
|
+
key.preventDefault();
|
|
36122
|
+
awaitingVimGoToTop = false;
|
|
36123
|
+
if (options.length > 0)
|
|
36124
|
+
filterMode = { ...filterMode, selectedIndex: options.length - 1 };
|
|
36125
|
+
render();
|
|
36126
|
+
return;
|
|
36127
|
+
}
|
|
36128
|
+
if (filterListFocused && key.name === "g") {
|
|
36129
|
+
key.preventDefault();
|
|
36130
|
+
if (awaitingVimGoToTop && options.length > 0)
|
|
36131
|
+
filterMode = { ...filterMode, selectedIndex: 0 };
|
|
36132
|
+
awaitingVimGoToTop = !awaitingVimGoToTop;
|
|
36133
|
+
render();
|
|
36134
|
+
return;
|
|
36135
|
+
}
|
|
36136
|
+
awaitingVimGoToTop = false;
|
|
36137
|
+
if (key.name === "down" || key.name === "up" || key.name === "j" || key.name === "k") {
|
|
36138
|
+
key.preventDefault();
|
|
36139
|
+
if (options.length > 0) {
|
|
36140
|
+
const backwards = key.name === "up" || key.name === "k";
|
|
36141
|
+
filterMode = { ...filterMode, selectedIndex: (filterMode.selectedIndex + (backwards ? -1 : 1) + options.length) % options.length };
|
|
36142
|
+
}
|
|
36143
|
+
render();
|
|
36144
|
+
return;
|
|
36145
|
+
}
|
|
36146
|
+
if (key.name === "space" || key.name === "left" || key.name === "right" || key.name === "h" || key.name === "l") {
|
|
36147
|
+
key.preventDefault();
|
|
36148
|
+
const option = options[filterMode.selectedIndex];
|
|
36149
|
+
if (!option)
|
|
36150
|
+
return;
|
|
36151
|
+
const selected = key.name === "space" ? !isDashboardFilterOptionSelected({ mode: filterMode, option }) : key.name === "right" || key.name === "l";
|
|
36152
|
+
filterMode = updateDashboardFilterSelection({ mode: filterMode, option, selected, authors });
|
|
36153
|
+
render();
|
|
36154
|
+
return;
|
|
36155
|
+
}
|
|
36156
|
+
if (key.name === "s") {
|
|
36157
|
+
key.preventDefault();
|
|
36158
|
+
filterMode = { ...filterMode, query: "", searching: true, selectedIndex: 0 };
|
|
36159
|
+
filterSearchComposer.textarea.setText("");
|
|
36160
|
+
render();
|
|
36161
|
+
return;
|
|
36162
|
+
}
|
|
36163
|
+
if (key.name === "return" || key.name === "enter") {
|
|
36164
|
+
key.preventDefault();
|
|
36165
|
+
const nextFilters = { statuses: [...filterMode.statuses], creators: [...filterMode.creators] };
|
|
36166
|
+
applyFilters(nextFilters);
|
|
36167
|
+
}
|
|
36168
|
+
return;
|
|
36169
|
+
}
|
|
35729
36170
|
const listFocused = renderer.currentFocusedRenderable === listRegion;
|
|
35730
36171
|
if (key.name === "down") {
|
|
35731
36172
|
key.preventDefault();
|
|
@@ -35754,7 +36195,7 @@ async function createDashboardTui({
|
|
|
35754
36195
|
if (listFocused && (key.name === "G" || key.name === "g" && key.shift)) {
|
|
35755
36196
|
key.preventDefault();
|
|
35756
36197
|
awaitingVimGoToTop = false;
|
|
35757
|
-
moveSelectionTo(page.sessions.length - 1);
|
|
36198
|
+
moveSelectionTo(visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery }).length - 1);
|
|
35758
36199
|
return;
|
|
35759
36200
|
}
|
|
35760
36201
|
if (listFocused && key.name === "g") {
|
|
@@ -35779,7 +36220,7 @@ async function createDashboardTui({
|
|
|
35779
36220
|
awaitingVimGoToTop = false;
|
|
35780
36221
|
if (key.name === "return" || key.name === "enter") {
|
|
35781
36222
|
key.preventDefault();
|
|
35782
|
-
const session = page.sessions[selectedIndex];
|
|
36223
|
+
const session = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery })[selectedIndex];
|
|
35783
36224
|
if (session)
|
|
35784
36225
|
finish({ kind: "session", sessionId: session.id });
|
|
35785
36226
|
return;
|
|
@@ -35789,6 +36230,23 @@ async function createDashboardTui({
|
|
|
35789
36230
|
finish({ kind: "new" });
|
|
35790
36231
|
return;
|
|
35791
36232
|
}
|
|
36233
|
+
if (key.name === "s") {
|
|
36234
|
+
key.preventDefault();
|
|
36235
|
+
openSessionSearch();
|
|
36236
|
+
return;
|
|
36237
|
+
}
|
|
36238
|
+
if (key.name === "f") {
|
|
36239
|
+
key.preventDefault();
|
|
36240
|
+
authorError = undefined;
|
|
36241
|
+
filterMode = createFilterMode({ kind: "status", filters });
|
|
36242
|
+
render();
|
|
36243
|
+
return;
|
|
36244
|
+
}
|
|
36245
|
+
if (key.name === "a") {
|
|
36246
|
+
key.preventDefault();
|
|
36247
|
+
openAuthorFilter();
|
|
36248
|
+
return;
|
|
36249
|
+
}
|
|
35792
36250
|
if (key.name === "L" || key.shift && key.name === "l") {
|
|
35793
36251
|
key.preventDefault();
|
|
35794
36252
|
logoutConfirmationOpen = true;
|
|
@@ -35805,21 +36263,19 @@ async function createDashboardTui({
|
|
|
35805
36263
|
finish(null);
|
|
35806
36264
|
}
|
|
35807
36265
|
};
|
|
35808
|
-
const root = new
|
|
35809
|
-
const statusBand = new
|
|
35810
|
-
const content = new
|
|
36266
|
+
const root = new BoxRenderable2(renderer, { width: "100%", height: "100%", flexDirection: "column", paddingX: 2, overflow: "hidden" });
|
|
36267
|
+
const statusBand = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36268
|
+
const content = new TextRenderable2(renderer, { content: "" });
|
|
35811
36269
|
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
|
-
};
|
|
36270
|
+
const previewBand = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36271
|
+
const filterSearchRegion = new BoxRenderable2(renderer, { width: "100%", flexShrink: 0 });
|
|
36272
|
+
const separator = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36273
|
+
const footer = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
35819
36274
|
listRegion.add(content);
|
|
35820
36275
|
root.add(statusBand);
|
|
35821
36276
|
root.add(listRegion);
|
|
35822
36277
|
root.add(previewBand);
|
|
36278
|
+
root.add(filterSearchRegion);
|
|
35823
36279
|
root.add(separator);
|
|
35824
36280
|
root.add(footer);
|
|
35825
36281
|
renderer.root.add(root);
|
|
@@ -35829,8 +36285,22 @@ async function createDashboardTui({
|
|
|
35829
36285
|
let loadingPage = false;
|
|
35830
36286
|
let pageRequestInFlight = false;
|
|
35831
36287
|
let pageError;
|
|
36288
|
+
let authorError;
|
|
36289
|
+
let authors = initialAuthors;
|
|
36290
|
+
let authorsLoaded = loadAuthors === undefined || initialAuthors.length > 0;
|
|
36291
|
+
let loadingAuthors = false;
|
|
35832
36292
|
let awaitingVimGoToTop = false;
|
|
35833
36293
|
let logoutConfirmationOpen = false;
|
|
36294
|
+
let filters = initialFilters;
|
|
36295
|
+
let filterMode;
|
|
36296
|
+
let sessionSearchOpen = false;
|
|
36297
|
+
let sessionSearchQuery = "";
|
|
36298
|
+
root.onMouseDown = (event) => {
|
|
36299
|
+
if (filterMode?.searching || sessionSearchOpen)
|
|
36300
|
+
return;
|
|
36301
|
+
event.preventDefault();
|
|
36302
|
+
listRegion.focus();
|
|
36303
|
+
};
|
|
35834
36304
|
let destroyed = false;
|
|
35835
36305
|
let rendererDestroyPromise;
|
|
35836
36306
|
let settled = false;
|
|
@@ -35840,10 +36310,41 @@ async function createDashboardTui({
|
|
|
35840
36310
|
let rejectDraft = () => {
|
|
35841
36311
|
return;
|
|
35842
36312
|
};
|
|
36313
|
+
let desiredContentScrollY = 0;
|
|
35843
36314
|
const action = new Promise((resolve, reject) => {
|
|
35844
36315
|
resolveAction = resolve;
|
|
35845
36316
|
rejectDraft = reject;
|
|
35846
36317
|
});
|
|
36318
|
+
let mountedSearchComposer;
|
|
36319
|
+
const filterSearchComposer = createComposer({
|
|
36320
|
+
renderer,
|
|
36321
|
+
parent: filterSearchRegion,
|
|
36322
|
+
tag: "Filter",
|
|
36323
|
+
placeholder: "Filter options\u2026",
|
|
36324
|
+
screenPaddingX: 2,
|
|
36325
|
+
onSubmit: () => applyFilterSearch(),
|
|
36326
|
+
onContentChange: () => {
|
|
36327
|
+
if (!filterMode?.searching)
|
|
36328
|
+
return;
|
|
36329
|
+
filterMode = { ...filterMode, query: filterSearchComposer.textarea.plainText, selectedIndex: 0 };
|
|
36330
|
+
render();
|
|
36331
|
+
}
|
|
36332
|
+
});
|
|
36333
|
+
const sessionSearchComposer = createComposer({
|
|
36334
|
+
renderer,
|
|
36335
|
+
parent: filterSearchRegion,
|
|
36336
|
+
tag: "Search",
|
|
36337
|
+
placeholder: "Search by ID, title, or number\u2026",
|
|
36338
|
+
screenPaddingX: 2,
|
|
36339
|
+
onSubmit: () => applySessionSearch(),
|
|
36340
|
+
onContentChange: () => {
|
|
36341
|
+
if (!sessionSearchOpen)
|
|
36342
|
+
return;
|
|
36343
|
+
sessionSearchQuery = sessionSearchComposer.textarea.plainText;
|
|
36344
|
+
selectedIndex = 0;
|
|
36345
|
+
render();
|
|
36346
|
+
}
|
|
36347
|
+
});
|
|
35847
36348
|
async function loadAdjacentPage(direction) {
|
|
35848
36349
|
if (pageRequestInFlight)
|
|
35849
36350
|
return;
|
|
@@ -35860,7 +36361,8 @@ async function createDashboardTui({
|
|
|
35860
36361
|
return;
|
|
35861
36362
|
page = loadedPage;
|
|
35862
36363
|
pageIndex = Math.max(1, pageIndex + (direction === "next" ? 1 : -1));
|
|
35863
|
-
|
|
36364
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
36365
|
+
selectedIndex = direction === "next" ? 0 : Math.max(0, sessions.length - 1);
|
|
35864
36366
|
} catch (error93) {
|
|
35865
36367
|
if (!destroyed && !settled)
|
|
35866
36368
|
pageError = error93 instanceof Error ? error93.message : String(error93);
|
|
@@ -35879,7 +36381,7 @@ async function createDashboardTui({
|
|
|
35879
36381
|
const refreshedPage = await loadPage({ target: "current", signal: loadPageAbortController.signal });
|
|
35880
36382
|
if (destroyed || settled)
|
|
35881
36383
|
return;
|
|
35882
|
-
const selectedSessionId = page.sessions[selectedIndex]?.id;
|
|
36384
|
+
const selectedSessionId = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery })[selectedIndex]?.id;
|
|
35883
36385
|
page = refreshedPage;
|
|
35884
36386
|
const refreshedSelectedIndex = selectedSessionId ? page.sessions.findIndex((session) => session.id === selectedSessionId) : -1;
|
|
35885
36387
|
selectedIndex = refreshedSelectedIndex >= 0 ? refreshedSelectedIndex : Math.max(0, Math.min(selectedIndex, page.sessions.length - 1));
|
|
@@ -35894,10 +36396,53 @@ async function createDashboardTui({
|
|
|
35894
36396
|
pageRequestInFlight = false;
|
|
35895
36397
|
}
|
|
35896
36398
|
}
|
|
36399
|
+
async function applyFilters(nextFilters) {
|
|
36400
|
+
if (loadingPage)
|
|
36401
|
+
return;
|
|
36402
|
+
loadingPage = true;
|
|
36403
|
+
pageError = undefined;
|
|
36404
|
+
authorError = undefined;
|
|
36405
|
+
render();
|
|
36406
|
+
try {
|
|
36407
|
+
page = await loadPage({ filters: nextFilters, signal: loadPageAbortController.signal });
|
|
36408
|
+
filters = nextFilters;
|
|
36409
|
+
pageIndex = 1;
|
|
36410
|
+
selectedIndex = 0;
|
|
36411
|
+
filterMode = undefined;
|
|
36412
|
+
} catch (error93) {
|
|
36413
|
+
pageError = error93 instanceof Error ? error93.message : String(error93);
|
|
36414
|
+
} finally {
|
|
36415
|
+
loadingPage = false;
|
|
36416
|
+
render();
|
|
36417
|
+
}
|
|
36418
|
+
}
|
|
36419
|
+
async function openAuthorFilter() {
|
|
36420
|
+
filterMode = createFilterMode({ kind: "creator", filters });
|
|
36421
|
+
authorError = undefined;
|
|
36422
|
+
render();
|
|
36423
|
+
if (authorsLoaded || loadingAuthors || !loadAuthors)
|
|
36424
|
+
return;
|
|
36425
|
+
loadingAuthors = true;
|
|
36426
|
+
render();
|
|
36427
|
+
try {
|
|
36428
|
+
authors = await loadAuthors();
|
|
36429
|
+
authorsLoaded = true;
|
|
36430
|
+
if (filterMode?.kind === "creator") {
|
|
36431
|
+
const firstSelectedIndex = authors.findIndex((author) => filterMode?.creators.some((creator) => creator.id === author.id));
|
|
36432
|
+
filterMode = { ...filterMode, selectedIndex: Math.max(0, firstSelectedIndex) };
|
|
36433
|
+
}
|
|
36434
|
+
} catch (error93) {
|
|
36435
|
+
authorError = error93 instanceof Error ? error93.message : String(error93);
|
|
36436
|
+
} finally {
|
|
36437
|
+
loadingAuthors = false;
|
|
36438
|
+
render();
|
|
36439
|
+
}
|
|
36440
|
+
}
|
|
35897
36441
|
async function moveSelection(direction) {
|
|
35898
|
-
|
|
36442
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
36443
|
+
if (loadingPage || sessions.length === 0)
|
|
35899
36444
|
return;
|
|
35900
|
-
const isBoundary = direction === "next" ? selectedIndex ===
|
|
36445
|
+
const isBoundary = direction === "next" ? selectedIndex === sessions.length - 1 : selectedIndex === 0;
|
|
35901
36446
|
if (!isBoundary) {
|
|
35902
36447
|
selectedIndex += direction === "next" ? 1 : -1;
|
|
35903
36448
|
render();
|
|
@@ -35912,7 +36457,13 @@ async function createDashboardTui({
|
|
|
35912
36457
|
}
|
|
35913
36458
|
return false;
|
|
35914
36459
|
};
|
|
35915
|
-
|
|
36460
|
+
filterSearchComposer.textarea.onKeyDown = handleSessionKey;
|
|
36461
|
+
sessionSearchComposer.textarea.onKeyDown = handleSessionKey;
|
|
36462
|
+
keyHandler = (key) => {
|
|
36463
|
+
if (mountedSearchComposer && key.name !== "escape" && !(key.ctrl && key.name === "c"))
|
|
36464
|
+
return;
|
|
36465
|
+
handleSessionKey(key);
|
|
36466
|
+
};
|
|
35916
36467
|
renderer.addInputHandler(inputHandler);
|
|
35917
36468
|
renderer.keyInput.on("keypress", keyHandler);
|
|
35918
36469
|
renderer.on(CliRenderEvents2.RENDER_ERROR, (event) => fail(event.error));
|
|
@@ -35939,6 +36490,10 @@ async function createDashboardTui({
|
|
|
35939
36490
|
if (resizeHandler)
|
|
35940
36491
|
renderer.off(CliRenderEvents2.RESIZE, resizeHandler);
|
|
35941
36492
|
stopPageRefresh();
|
|
36493
|
+
if (contentScrollHandler)
|
|
36494
|
+
renderer.off(CliRenderEvents2.FRAME, contentScrollHandler);
|
|
36495
|
+
filterSearchComposer.destroy();
|
|
36496
|
+
sessionSearchComposer.destroy();
|
|
35942
36497
|
rendererDestroyed2 = true;
|
|
35943
36498
|
renderer.destroy();
|
|
35944
36499
|
},
|
|
@@ -35956,6 +36511,8 @@ async function createDashboardTui({
|
|
|
35956
36511
|
if (refreshTimer !== undefined)
|
|
35957
36512
|
clearInterval(refreshTimer);
|
|
35958
36513
|
loadPageAbortController.abort();
|
|
36514
|
+
if (contentScrollHandler)
|
|
36515
|
+
renderer.off(CliRenderEvents2.FRAME, contentScrollHandler);
|
|
35959
36516
|
if (!rendererDestroyed2) {
|
|
35960
36517
|
rendererDestroyed2 = true;
|
|
35961
36518
|
renderer.destroy();
|
|
@@ -35993,6 +36550,63 @@ function rowsBelowVisibleWindow({ sessionCount, selectedIndex, height }) {
|
|
|
35993
36550
|
const { firstIndex, rowCount } = visibleListWindow({ sessionCount, selectedIndex, height });
|
|
35994
36551
|
return Math.max(0, sessionCount - (firstIndex + rowCount));
|
|
35995
36552
|
}
|
|
36553
|
+
function createFilterMode({ kind, filters }) {
|
|
36554
|
+
return {
|
|
36555
|
+
kind,
|
|
36556
|
+
selectedIndex: 0,
|
|
36557
|
+
statuses: [...filters.statuses],
|
|
36558
|
+
creators: [...filters.creators],
|
|
36559
|
+
query: "",
|
|
36560
|
+
searching: false
|
|
36561
|
+
};
|
|
36562
|
+
}
|
|
36563
|
+
function formatFilterMenu({ mode, authors, width }) {
|
|
36564
|
+
const options = visibleDashboardFilterOptions({ mode, authors });
|
|
36565
|
+
const selectedCount = mode.kind === "status" ? mode.statuses.length : mode.creators.length;
|
|
36566
|
+
const heading = `${mode.kind === "status" ? "Filter by status" : "Filter by author"} \xB7 ${selectedCount} selected \xB7 empty shows all`;
|
|
36567
|
+
const rows = options.map((option, index) => {
|
|
36568
|
+
const active = isDashboardFilterOptionSelected({ mode, option });
|
|
36569
|
+
const label = `${active ? "[x]" : "[ ]"} ${truncate(option.label, Math.max(1, width - 4))}`;
|
|
36570
|
+
const chunk = index === mode.selectedIndex ? bg(PALETTE.selectionBg)(fg3(PALETTE.bodyText)(label.padEnd(width))) : active ? bold2(fg3(PALETTE.humanAccent)(label)) : fg3(PALETTE.dimText)(label);
|
|
36571
|
+
return [chunk];
|
|
36572
|
+
});
|
|
36573
|
+
return joinStyled([
|
|
36574
|
+
[bold2(fg3(PALETTE.bodyText)(truncate(heading, width)))],
|
|
36575
|
+
...rows.length > 0 ? rows : [[fg3(PALETTE.dimText)("No matching options.")]]
|
|
36576
|
+
], `
|
|
36577
|
+
`);
|
|
36578
|
+
}
|
|
36579
|
+
function dashboardFilterOptions({ kind, authors }) {
|
|
36580
|
+
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 }));
|
|
36581
|
+
}
|
|
36582
|
+
function visibleDashboardFilterOptions({ mode, authors }) {
|
|
36583
|
+
return dashboardFilterOptions({ kind: mode.kind, authors }).filter((option) => fuzzyMatches(option.label, mode.query));
|
|
36584
|
+
}
|
|
36585
|
+
function isDashboardFilterOptionSelected({ mode, option }) {
|
|
36586
|
+
return option.kind === "status" ? mode.statuses.includes(option.id) : mode.creators.some((creator) => creator.id === option.id);
|
|
36587
|
+
}
|
|
36588
|
+
function updateDashboardFilterSelection({ mode, option, selected, authors }) {
|
|
36589
|
+
if (option.kind === "status") {
|
|
36590
|
+
const selectedStatuses = new Set(mode.statuses);
|
|
36591
|
+
if (selected)
|
|
36592
|
+
selectedStatuses.add(option.id);
|
|
36593
|
+
else
|
|
36594
|
+
selectedStatuses.delete(option.id);
|
|
36595
|
+
return {
|
|
36596
|
+
...mode,
|
|
36597
|
+
statuses: lifecycleStatusOptions.map((entry) => entry.value).filter((status) => selectedStatuses.has(status))
|
|
36598
|
+
};
|
|
36599
|
+
}
|
|
36600
|
+
const selectedCreatorIds = new Set(mode.creators.map((creator) => creator.id));
|
|
36601
|
+
if (selected)
|
|
36602
|
+
selectedCreatorIds.add(option.id);
|
|
36603
|
+
else
|
|
36604
|
+
selectedCreatorIds.delete(option.id);
|
|
36605
|
+
return {
|
|
36606
|
+
...mode,
|
|
36607
|
+
creators: authors.filter((author) => selectedCreatorIds.has(author.id))
|
|
36608
|
+
};
|
|
36609
|
+
}
|
|
35996
36610
|
function formatSessionList({ sessions, selectedIndex, width, height }) {
|
|
35997
36611
|
const { firstIndex, rowCount } = visibleListWindow({ sessionCount: sessions.length, selectedIndex, height });
|
|
35998
36612
|
const visibleSessions = sessions.slice(firstIndex, firstIndex + rowCount);
|
|
@@ -36079,18 +36693,34 @@ function formatUpdatedAt(value) {
|
|
|
36079
36693
|
return `${Math.floor(seconds / (60 * 60))}h ago`;
|
|
36080
36694
|
return new Date(milliseconds).toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
36081
36695
|
}
|
|
36082
|
-
function
|
|
36083
|
-
|
|
36696
|
+
function matchesSessionSearch({ session, query }) {
|
|
36697
|
+
return fuzzyMatches(session.id, query) || fuzzyMatches(session.title ?? "", query) || fuzzyMatches(String(session.sessionNumber), query);
|
|
36698
|
+
}
|
|
36699
|
+
function visibleDashboardSessions({ sessions, query }) {
|
|
36700
|
+
return sessions.filter((session) => matchesSessionSearch({ session, query }));
|
|
36701
|
+
}
|
|
36702
|
+
function dashboardStatusBand({ page, pageIndex, filters, sessionSearchQuery }) {
|
|
36703
|
+
const openCount = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery }).filter((session) => session.status === "open").length;
|
|
36084
36704
|
const paging = [`page ${pageIndex}`];
|
|
36085
36705
|
if (page.canGoPrevious)
|
|
36086
36706
|
paging.push("\u2190 prev");
|
|
36087
36707
|
if (page.hasMore)
|
|
36088
36708
|
paging.push("more \u2192");
|
|
36089
|
-
|
|
36709
|
+
const activeFilters = [
|
|
36710
|
+
filters.statuses.length > 0 ? `status: ${filters.statuses.map(sessionStatusLabel).join(", ")}` : undefined,
|
|
36711
|
+
filters.creators.length > 0 ? `author: ${filters.creators.map((creator) => creator.name).join(", ")}` : undefined,
|
|
36712
|
+
sessionSearchQuery ? `search: ${sessionSearchQuery}` : undefined
|
|
36713
|
+
].filter((value) => value !== undefined);
|
|
36714
|
+
return `remy \xB7 sessions \xB7 ${openCount} open${activeFilters.length > 0 ? ` \xB7 ${activeFilters.join(" \xB7 ")}` : ""} \xB7 ${paging.join(" \xB7 ")}`;
|
|
36715
|
+
}
|
|
36716
|
+
function hasActiveFilters(filters) {
|
|
36717
|
+
return filters.statuses.length > 0 || filters.creators.length > 0;
|
|
36090
36718
|
}
|
|
36091
|
-
function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen }) {
|
|
36719
|
+
function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen, sessionSearchOpen }) {
|
|
36092
36720
|
if (logoutConfirmationOpen)
|
|
36093
36721
|
return "Log out of Remy? y confirms \xB7 Esc cancels";
|
|
36722
|
+
if (sessionSearchOpen)
|
|
36723
|
+
return "type to search \xB7 \u23CE apply search \xB7 Esc clear search";
|
|
36094
36724
|
const parts = [];
|
|
36095
36725
|
if (loadingPage)
|
|
36096
36726
|
parts.push("Loading sessions\u2026");
|
|
@@ -36098,9 +36728,22 @@ function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmation
|
|
|
36098
36728
|
parts.push(`Could not load sessions: ${pageError}`);
|
|
36099
36729
|
if (rowsBelow > 0)
|
|
36100
36730
|
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");
|
|
36731
|
+
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
36732
|
return parts.join(" \xB7 ");
|
|
36103
36733
|
}
|
|
36734
|
+
function dashboardFilterFooter({ mode, loadingPage, loadingAuthors, pageError, authorError }) {
|
|
36735
|
+
if (loadingAuthors)
|
|
36736
|
+
return "Loading authors\u2026 \xB7 Esc cancel";
|
|
36737
|
+
if (loadingPage)
|
|
36738
|
+
return "Applying filter\u2026";
|
|
36739
|
+
if (mode.searching)
|
|
36740
|
+
return "type to filter \xB7 \u23CE apply search \xB7 Esc cancel search";
|
|
36741
|
+
return [
|
|
36742
|
+
authorError ? `Could not load authors: ${authorError}` : undefined,
|
|
36743
|
+
pageError ? `Could not apply filter: ${pageError}` : undefined,
|
|
36744
|
+
"\u2191\u2193/jk move \xB7 space toggle \xB7 \u2190\u2192/hl mark \xB7 s search \xB7 \u23CE apply \xB7 Esc cancel"
|
|
36745
|
+
].filter((value) => value !== undefined).join(" \xB7 ");
|
|
36746
|
+
}
|
|
36104
36747
|
function truncate(value, width) {
|
|
36105
36748
|
if (value.length <= width)
|
|
36106
36749
|
return value;
|
|
@@ -36111,87 +36754,33 @@ async function createDefaultRenderer() {
|
|
|
36111
36754
|
}
|
|
36112
36755
|
|
|
36113
36756
|
// 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";
|
|
36757
|
+
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
36758
|
|
|
36119
|
-
// src/tui/
|
|
36120
|
-
|
|
36121
|
-
function
|
|
36122
|
-
|
|
36759
|
+
// src/tui/attachment-presentation.ts
|
|
36760
|
+
import { dim as dim3, fg as fg4, StyledText as StyledText3 } from "@opentui/core";
|
|
36761
|
+
function attachmentUploadMessage(filenames) {
|
|
36762
|
+
return filenames.length === 1 ? `Attaching ${filenames[0]}\u2026` : `Attaching ${filenames.length} files\u2026`;
|
|
36763
|
+
}
|
|
36764
|
+
function renderAttachmentFeedback(feedback) {
|
|
36765
|
+
const presentation = feedback.kind === "progress" ? { glyph: "\u28FF", color: PALETTE.progress } : feedback.kind === "success" ? { glyph: "\u2713", color: PALETTE.statusCompleted } : { glyph: "\u2717", color: PALETTE.failure };
|
|
36766
|
+
return new StyledText3([
|
|
36767
|
+
fg4(presentation.color)(`${presentation.glyph} `),
|
|
36768
|
+
fg4(feedback.kind === "failure" ? PALETTE.failure : PALETTE.bodyText)(feedback.message)
|
|
36769
|
+
]);
|
|
36123
36770
|
}
|
|
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
|
-
};
|
|
36771
|
+
function renderAttachmentSummary(attachments) {
|
|
36772
|
+
if (attachments.length === 0)
|
|
36773
|
+
return new StyledText3([]);
|
|
36774
|
+
const label = attachments.length === 1 ? "Attachment" : `${attachments.length} attachments`;
|
|
36775
|
+
return new StyledText3([
|
|
36776
|
+
dim3(fg4(PALETTE.dimText)("\u21B3 ")),
|
|
36777
|
+
fg4(PALETTE.tool)(label),
|
|
36778
|
+
dim3(fg4(PALETTE.dimText)(` \xB7 ${attachments.map((attachment) => attachment.filename).join(" \xB7 ")}`))
|
|
36779
|
+
]);
|
|
36191
36780
|
}
|
|
36192
36781
|
|
|
36193
36782
|
// src/tui/path-completion-menu.ts
|
|
36194
|
-
import { bg as bg2, fg as
|
|
36783
|
+
import { bg as bg2, fg as fg5, stringToStyledText as stringToStyledText3, StyledText as StyledText4 } from "@opentui/core";
|
|
36195
36784
|
var maximumVisibleCompletions = 8;
|
|
36196
36785
|
function renderPathCompletionMenu({ completions, selectedIndex }) {
|
|
36197
36786
|
if (completions.length === 0)
|
|
@@ -36202,7 +36791,7 @@ function renderPathCompletionMenu({ completions, selectedIndex }) {
|
|
|
36202
36791
|
const completionIndex = firstIndex + index;
|
|
36203
36792
|
const selected = completionIndex === selectedIndex;
|
|
36204
36793
|
const rowStyle = selected ? bg2(PALETTE.selectionBg) : (chunk) => chunk;
|
|
36205
|
-
return new
|
|
36794
|
+
return new StyledText4([rowStyle(fg5(PALETTE.bodyText)(`${selected ? "\u203A " : " "}${completion.display}`))]);
|
|
36206
36795
|
});
|
|
36207
36796
|
const overflow = completions.length > visibleCompletions.length ? stringToStyledText3(`${firstIndex + 1}-${firstIndex + visibleCompletions.length} of ${completions.length}`) : undefined;
|
|
36208
36797
|
return joinStyled([
|
|
@@ -36242,13 +36831,62 @@ async function completePathToken({ text, cwd, homeDirectory = homedir() }) {
|
|
|
36242
36831
|
const normalized = replacementPath.split(sep).join("/");
|
|
36243
36832
|
const suffix = entry.isDirectory() ? "/" : "";
|
|
36244
36833
|
const completed = `${normalized}${suffix}`;
|
|
36245
|
-
return {
|
|
36834
|
+
return {
|
|
36835
|
+
display: completed,
|
|
36836
|
+
replacement: `${token.startsWith("@") ? "@" : ""}${completed}`,
|
|
36837
|
+
...entry.isFile() ? { attachmentPath: completedPath } : {}
|
|
36838
|
+
};
|
|
36246
36839
|
});
|
|
36247
36840
|
}
|
|
36248
36841
|
function replaceActivePathToken({ text, replacement }) {
|
|
36249
36842
|
const token = activePathToken(text);
|
|
36250
36843
|
return token === undefined ? text : `${text.slice(0, text.length - token.length)}${replacement}`;
|
|
36251
36844
|
}
|
|
36845
|
+
function selectedAttachmentPaths({ text, selections }) {
|
|
36846
|
+
return selections.filter((selection) => text.slice(selection.start, selection.end) === selection.token && hasPathTokenBoundaries({ text, selection })).map((selection) => selection.attachmentPath);
|
|
36847
|
+
}
|
|
36848
|
+
function createSelectedPathCompletion({ attachmentPath, token, end }) {
|
|
36849
|
+
return { attachmentPath, token, start: end - token.length, end };
|
|
36850
|
+
}
|
|
36851
|
+
function reconcileSelectedPathCompletions({
|
|
36852
|
+
previousText,
|
|
36853
|
+
text,
|
|
36854
|
+
selections
|
|
36855
|
+
}) {
|
|
36856
|
+
if (text === previousText)
|
|
36857
|
+
return selections;
|
|
36858
|
+
let changeStart = 0;
|
|
36859
|
+
while (changeStart < previousText.length && changeStart < text.length && previousText[changeStart] === text[changeStart])
|
|
36860
|
+
changeStart += 1;
|
|
36861
|
+
let previousChangeEnd = previousText.length;
|
|
36862
|
+
let nextChangeEnd = text.length;
|
|
36863
|
+
while (previousChangeEnd > changeStart && nextChangeEnd > changeStart && previousText[previousChangeEnd - 1] === text[nextChangeEnd - 1]) {
|
|
36864
|
+
previousChangeEnd -= 1;
|
|
36865
|
+
nextChangeEnd -= 1;
|
|
36866
|
+
}
|
|
36867
|
+
const offsetChange = nextChangeEnd - previousChangeEnd;
|
|
36868
|
+
return selections.flatMap((selection) => {
|
|
36869
|
+
if (selection.end <= changeStart)
|
|
36870
|
+
return [selection];
|
|
36871
|
+
if (selection.start >= previousChangeEnd) {
|
|
36872
|
+
return [{
|
|
36873
|
+
...selection,
|
|
36874
|
+
start: selection.start + offsetChange,
|
|
36875
|
+
end: selection.end + offsetChange
|
|
36876
|
+
}];
|
|
36877
|
+
}
|
|
36878
|
+
return [];
|
|
36879
|
+
}).filter((selection) => hasPathTokenBoundaries({ text, selection }));
|
|
36880
|
+
}
|
|
36881
|
+
function hasPathTokenBoundaries({ text, selection }) {
|
|
36882
|
+
let before = selection.start;
|
|
36883
|
+
while (before > 0 && "([{'\"`".includes(text[before - 1]))
|
|
36884
|
+
before -= 1;
|
|
36885
|
+
let after = selection.end;
|
|
36886
|
+
while (after < text.length && '),.;:!?]}"`'.includes(text[after]))
|
|
36887
|
+
after += 1;
|
|
36888
|
+
return (before === 0 || /\s/.test(text[before - 1])) && (after === text.length || /\s/.test(text[after]));
|
|
36889
|
+
}
|
|
36252
36890
|
function activePathToken(text) {
|
|
36253
36891
|
const match = text.match(/(?:^|\s)(\S*)$/);
|
|
36254
36892
|
return match?.[1];
|
|
@@ -36261,6 +36899,7 @@ async function createNewSessionWizard({
|
|
|
36261
36899
|
repositories,
|
|
36262
36900
|
reloadRepositories,
|
|
36263
36901
|
suggestRepositories,
|
|
36902
|
+
suggestBranches,
|
|
36264
36903
|
readClipboardImage,
|
|
36265
36904
|
savePastedImage,
|
|
36266
36905
|
attachPromptPaths,
|
|
@@ -36297,15 +36936,19 @@ async function createNewSessionWizard({
|
|
|
36297
36936
|
else
|
|
36298
36937
|
overrides.set(repositoryId, isSelected);
|
|
36299
36938
|
}, renderRepositoryLoadingSkeleton = function() {
|
|
36300
|
-
return new
|
|
36301
|
-
|
|
36939
|
+
return new StyledText5([
|
|
36940
|
+
dim4(fg6(PALETTE.dimText)(`\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
|
|
36302
36941
|
\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
|
|
36303
36942
|
\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588`))
|
|
36304
36943
|
]);
|
|
36305
36944
|
}, render = function() {
|
|
36306
36945
|
const repositoryListVisible = step === "repositories" || step === "repositorySearch";
|
|
36307
36946
|
const composerVisible = step === "prompt" || step === "repositorySearch" || step === "recompute";
|
|
36308
|
-
const orient = (body) => joinStyled([renderStepIndicator({
|
|
36947
|
+
const orient = (body) => joinStyled([renderStepIndicator({
|
|
36948
|
+
step,
|
|
36949
|
+
hasInstallationChoice: installationIds.length > 1,
|
|
36950
|
+
hasBranchChoice: step === "loadingBranches" || branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch")
|
|
36951
|
+
}), body], `
|
|
36309
36952
|
`);
|
|
36310
36953
|
if (composerVisible && !composerMounted) {
|
|
36311
36954
|
composerMounted = true;
|
|
@@ -36323,19 +36966,22 @@ async function createNewSessionWizard({
|
|
|
36323
36966
|
repositoryRows.content = "";
|
|
36324
36967
|
repositoryDivider.content = "";
|
|
36325
36968
|
repositoryFooter.content = "";
|
|
36326
|
-
const attachmentText =
|
|
36969
|
+
const attachmentText = renderAttachmentSummary(attachments);
|
|
36970
|
+
const attachmentStatus = attachmentFeedback ? renderAttachmentFeedback(attachmentFeedback) : undefined;
|
|
36327
36971
|
if (step === "prompt") {
|
|
36328
36972
|
const parts = [
|
|
36329
|
-
new
|
|
36973
|
+
new StyledText5([stepHeader("New remote Codex session")]),
|
|
36330
36974
|
stringToStyledText4("Describe the work you want Remy to do. Press Enter to continue; Esc back.")
|
|
36331
36975
|
];
|
|
36332
|
-
if (attachmentText)
|
|
36976
|
+
if (attachmentText.chunks.length > 0)
|
|
36333
36977
|
parts.push(attachmentText);
|
|
36334
36978
|
const completionMenu = pathCompletionMenuOpen ? renderPathCompletionMenu({ completions: pathCompletions, selectedIndex: completionIndex }) : undefined;
|
|
36335
36979
|
if (completionMenu)
|
|
36336
36980
|
parts.push(completionMenu);
|
|
36337
36981
|
if (status)
|
|
36338
36982
|
parts.push(stringToStyledText4(status));
|
|
36983
|
+
if (attachmentStatus)
|
|
36984
|
+
parts.push(attachmentStatus);
|
|
36339
36985
|
content.content = orient(joinStyled(parts, `
|
|
36340
36986
|
|
|
36341
36987
|
`));
|
|
@@ -36343,7 +36989,7 @@ async function createNewSessionWizard({
|
|
|
36343
36989
|
const rows = joinStyled(installationIds.map((id, index) => renderSelectableRow({ label: id, isCursor: index === installationIndex })), `
|
|
36344
36990
|
`);
|
|
36345
36991
|
content.content = orient(joinStyled([
|
|
36346
|
-
new
|
|
36992
|
+
new StyledText5([stepHeader("Select GitHub installation")]),
|
|
36347
36993
|
rows,
|
|
36348
36994
|
stringToStyledText4("\u2191\u2193 move \xB7 \u23CE continue \xB7 esc back")
|
|
36349
36995
|
], `
|
|
@@ -36352,11 +36998,11 @@ async function createNewSessionWizard({
|
|
|
36352
36998
|
} else if (step === "loadingRepositories") {
|
|
36353
36999
|
const failed = repositoryLoadState === "failed";
|
|
36354
37000
|
const parts = [
|
|
36355
|
-
new
|
|
37001
|
+
new StyledText5([stepHeader("Select repositories")]),
|
|
36356
37002
|
stringToStyledText4(failed ? "Could not load your repositories." : "Loading your repos\u2026")
|
|
36357
37003
|
];
|
|
36358
37004
|
if (failed) {
|
|
36359
|
-
parts.push(new
|
|
37005
|
+
parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(`(${status})`))]));
|
|
36360
37006
|
parts.push(stringToStyledText4("r retry \xB7 esc back"));
|
|
36361
37007
|
} else {
|
|
36362
37008
|
parts.push(renderRepositoryLoadingSkeleton());
|
|
@@ -36367,12 +37013,41 @@ async function createNewSessionWizard({
|
|
|
36367
37013
|
`));
|
|
36368
37014
|
} else if (step === "loadingSuggestions") {
|
|
36369
37015
|
content.content = orient(joinStyled([
|
|
36370
|
-
new
|
|
36371
|
-
new
|
|
36372
|
-
new
|
|
37016
|
+
new StyledText5([stepHeader("Select repositories")]),
|
|
37017
|
+
new StyledText5([fg6(PALETTE.progress)(`${suggestionSpinnerFrames[suggestionSpinnerFrame]} Remy is matching your prompt to repositories\u2026`)]),
|
|
37018
|
+
new StyledText5([dim4(fg6(PALETTE.dimText)("This can take a few seconds."))]),
|
|
36373
37019
|
stringToStyledText4("s select repositories yourself \xB7 esc back")
|
|
36374
37020
|
], `
|
|
36375
37021
|
|
|
37022
|
+
`));
|
|
37023
|
+
} else if (step === "loadingBranches") {
|
|
37024
|
+
content.content = orient(joinStyled([
|
|
37025
|
+
new StyledText5([stepHeader("Choose branches")]),
|
|
37026
|
+
new StyledText5([fg6(PALETTE.progress)(`${suggestionSpinnerFrames[suggestionSpinnerFrame]} Remy is checking for related open pull requests\u2026`)]),
|
|
37027
|
+
new StyledText5([dim4(fg6(PALETTE.dimText)("This can take a few seconds."))]),
|
|
37028
|
+
stringToStyledText4("esc back")
|
|
37029
|
+
], `
|
|
37030
|
+
|
|
37031
|
+
`));
|
|
37032
|
+
} else if (step === "branches") {
|
|
37033
|
+
const existing = branchSuggestions.filter((suggestion) => suggestion.suggestionKind !== "createSessionBranch");
|
|
37034
|
+
const parts = [new StyledText5([stepHeader("Choose branches")])];
|
|
37035
|
+
if (existing.length > 0) {
|
|
37036
|
+
parts.push(stringToStyledText4("Remy found related open pull-request work:"));
|
|
37037
|
+
for (const suggestion of existing) {
|
|
37038
|
+
const repository = installationRepositories().find((candidate) => candidate.id === suggestion.repositoryId);
|
|
37039
|
+
parts.push(stringToStyledText4(`${repository?.fullName ?? suggestion.repositoryId} \u2014 ${suggestion.branchName}${suggestion.pullRequest ? ` (PR #${suggestion.pullRequest.number}: ${suggestion.pullRequest.title})` : ""}`));
|
|
37040
|
+
}
|
|
37041
|
+
parts.push(new StyledText5(renderSelectableRow({ label: "Continue from the suggested branch", isCursor: branchChoice === "existing" })));
|
|
37042
|
+
parts.push(new StyledText5(renderSelectableRow({ label: "Create a new session branch", isCursor: branchChoice === "new" })));
|
|
37043
|
+
parts.push(stringToStyledText4("\u2191\u2193 choose \xB7 \u23CE continue \xB7 esc back"));
|
|
37044
|
+
} else {
|
|
37045
|
+
parts.push(stringToStyledText4("Remy recommends creating a new session branch."));
|
|
37046
|
+
parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(status || "No related open pull request was identified from your request."))]));
|
|
37047
|
+
parts.push(stringToStyledText4("\u23CE continue \xB7 esc back"));
|
|
37048
|
+
}
|
|
37049
|
+
content.content = orient(joinStyled(parts, `
|
|
37050
|
+
|
|
36376
37051
|
`));
|
|
36377
37052
|
} else if (repositoryListVisible) {
|
|
36378
37053
|
const selectionOverrides = searchSelections ?? manualSelections;
|
|
@@ -36387,15 +37062,17 @@ async function createNewSessionWizard({
|
|
|
36387
37062
|
`) : step === "repositorySearch" ? "No matching repositories." : "No repositories available for this installation.";
|
|
36388
37063
|
repositoryDivider.content = renderComposerDivider({ width: renderer.width - 2 });
|
|
36389
37064
|
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
|
|
37065
|
+
const parts = step === "repositorySearch" ? [new StyledText5([stepHeader("Search repositories")]), stringToStyledText4("Fuzzy matching repository names.")] : [
|
|
37066
|
+
new StyledText5([stepHeader("Select repositories")]),
|
|
36392
37067
|
stringToStyledText4(`${selected.size} selected \xB7 ${suggestedIds.size} suggested by Remy${suggestionConfidence ? ` (${suggestionConfidence} confidence)` : ""}`),
|
|
36393
37068
|
stringToStyledText4("Suggestions are based on your prompt and recompute instruction.")
|
|
36394
37069
|
];
|
|
36395
|
-
if (attachmentText)
|
|
37070
|
+
if (attachmentText.chunks.length > 0)
|
|
36396
37071
|
parts.push(attachmentText);
|
|
36397
37072
|
if (status && step === "repositories")
|
|
36398
37073
|
parts.push(stringToStyledText4(status));
|
|
37074
|
+
if (attachmentStatus)
|
|
37075
|
+
parts.push(attachmentStatus);
|
|
36399
37076
|
content.content = orient(joinStyled(parts, `
|
|
36400
37077
|
|
|
36401
37078
|
`));
|
|
@@ -36403,11 +37080,11 @@ async function createNewSessionWizard({
|
|
|
36403
37080
|
editor.placeholder = "Filter repositories\u2026";
|
|
36404
37081
|
} else if (step === "recompute") {
|
|
36405
37082
|
const parts = [
|
|
36406
|
-
new
|
|
37083
|
+
new StyledText5([stepHeader("Recompute suggestions")]),
|
|
36407
37084
|
stringToStyledText4("Suggestions use your prompt and instruction. Existing manual toggles stay selected or unselected."),
|
|
36408
37085
|
stringToStyledText4("Enter instruction, then press Enter to continue. Esc back.")
|
|
36409
37086
|
];
|
|
36410
|
-
if (attachmentText)
|
|
37087
|
+
if (attachmentText.chunks.length > 0)
|
|
36411
37088
|
parts.push(attachmentText);
|
|
36412
37089
|
content.content = orient(joinStyled(parts, `
|
|
36413
37090
|
|
|
@@ -36415,26 +37092,31 @@ async function createNewSessionWizard({
|
|
|
36415
37092
|
editor.placeholder = "e.g. worker, migration, dashboard";
|
|
36416
37093
|
} else {
|
|
36417
37094
|
const selected = installationRepositories().filter((repository) => selectedRepositoryIds().has(repository.id));
|
|
36418
|
-
const
|
|
36419
|
-
|
|
37095
|
+
const branchOverrides = branchChoice === "existing" ? branchSuggestions.filter((suggestion) => suggestion.suggestionKind !== "createSessionBranch") : [];
|
|
37096
|
+
const meta5 = new StyledText5([
|
|
37097
|
+
fg6(PALETTE.bodyText)(`Installation: ${selectedInstallationId() ?? "(missing)"}
|
|
37098
|
+
`),
|
|
37099
|
+
fg6(PALETTE.bodyText)(`Repositories: ${selected.map((repository) => repository.fullName).join(", ") || "(none)"}
|
|
36420
37100
|
`),
|
|
36421
|
-
|
|
37101
|
+
fg6(PALETTE.bodyText)(`Branches: ${branchOverrides.length > 0 ? branchOverrides.map((override) => override.branchName).join(", ") : "new session branches"}
|
|
36422
37102
|
`),
|
|
36423
|
-
|
|
36424
|
-
|
|
37103
|
+
fg6(PALETTE.bodyText)(`Model: ${newSessionModelLabel(model)} \xB7 ${newSessionReasoningEffortLabel(reasoningEffort)} reasoning`),
|
|
37104
|
+
dim4(fg6(PALETTE.dimText)(" \u25B8 m model \xB7 \u2190\u2192 reasoning"))
|
|
36425
37105
|
]);
|
|
36426
37106
|
const parts = [
|
|
36427
|
-
new
|
|
36428
|
-
new
|
|
37107
|
+
new StyledText5([stepHeader("Confirm new session")]),
|
|
37108
|
+
new StyledText5([renderRoleLabel({ label: "You", role: "human" })]),
|
|
36429
37109
|
renderMessageBody({ text: prompt, role: "human" }),
|
|
36430
37110
|
meta5
|
|
36431
37111
|
];
|
|
36432
|
-
if (attachmentText)
|
|
37112
|
+
if (attachmentText.chunks.length > 0)
|
|
36433
37113
|
parts.push(attachmentText);
|
|
36434
|
-
parts.push(new
|
|
36435
|
-
parts.push(stringToStyledText4("\u23CE create \xB7 e edit prompt \xB7 r repositories \xB7 esc back"));
|
|
37114
|
+
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."))]));
|
|
37115
|
+
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
37116
|
if (status)
|
|
36437
37117
|
parts.push(stringToStyledText4(status));
|
|
37118
|
+
if (attachmentStatus)
|
|
37119
|
+
parts.push(attachmentStatus);
|
|
36438
37120
|
content.content = orient(joinStyled(parts, `
|
|
36439
37121
|
|
|
36440
37122
|
`));
|
|
@@ -36459,7 +37141,7 @@ async function createNewSessionWizard({
|
|
|
36459
37141
|
syncSuggestionLoadingIndicator();
|
|
36460
37142
|
renderer.requestRender();
|
|
36461
37143
|
}, syncSuggestionLoadingIndicator = function() {
|
|
36462
|
-
if (destroyed || settled || step !== "loadingSuggestions") {
|
|
37144
|
+
if (destroyed || settled || step !== "loadingSuggestions" && step !== "loadingBranches") {
|
|
36463
37145
|
if (suggestionSpinnerTimer) {
|
|
36464
37146
|
clearInterval(suggestionSpinnerTimer);
|
|
36465
37147
|
suggestionSpinnerTimer = undefined;
|
|
@@ -36470,22 +37152,28 @@ async function createNewSessionWizard({
|
|
|
36470
37152
|
if (suggestionSpinnerTimer)
|
|
36471
37153
|
return;
|
|
36472
37154
|
suggestionSpinnerTimer = setInterval(() => {
|
|
36473
|
-
if (destroyed || settled || step !== "loadingSuggestions") {
|
|
37155
|
+
if (destroyed || settled || step !== "loadingSuggestions" && step !== "loadingBranches") {
|
|
36474
37156
|
syncSuggestionLoadingIndicator();
|
|
36475
37157
|
return;
|
|
36476
37158
|
}
|
|
36477
37159
|
suggestionSpinnerFrame = (suggestionSpinnerFrame + 1) % suggestionSpinnerFrames.length;
|
|
36478
37160
|
render();
|
|
36479
37161
|
}, suggestionSpinnerIntervalMs);
|
|
37162
|
+
}, invalidatePendingConfirmation = function() {
|
|
37163
|
+
pendingConfirmation = undefined;
|
|
37164
|
+
if (attachmentFeedback?.kind === "progress")
|
|
37165
|
+
attachmentFeedback = undefined;
|
|
36480
37166
|
}, finish = function(value) {
|
|
36481
37167
|
if (settled)
|
|
36482
37168
|
return;
|
|
37169
|
+
invalidatePendingConfirmation();
|
|
36483
37170
|
settled = true;
|
|
36484
37171
|
syncSuggestionLoadingIndicator();
|
|
36485
37172
|
resolveDraft(value);
|
|
36486
37173
|
}, fail = function(error93) {
|
|
36487
37174
|
if (settled)
|
|
36488
37175
|
return;
|
|
37176
|
+
invalidatePendingConfirmation();
|
|
36489
37177
|
settled = true;
|
|
36490
37178
|
syncSuggestionLoadingIndicator();
|
|
36491
37179
|
rejectDraft(error93);
|
|
@@ -36556,7 +37244,15 @@ async function createNewSessionWizard({
|
|
|
36556
37244
|
returnToPromptFromRepositoryLoading();
|
|
36557
37245
|
return;
|
|
36558
37246
|
}
|
|
37247
|
+
if (step === "loadingBranches" || step === "branches") {
|
|
37248
|
+
branchRequestGeneration += 1;
|
|
37249
|
+
step = "repositories";
|
|
37250
|
+
render();
|
|
37251
|
+
return;
|
|
37252
|
+
}
|
|
36559
37253
|
if (step === "recompute" || step === "confirmation") {
|
|
37254
|
+
if (step === "confirmation")
|
|
37255
|
+
invalidatePendingConfirmation();
|
|
36560
37256
|
step = "repositories";
|
|
36561
37257
|
editor.setText("");
|
|
36562
37258
|
render();
|
|
@@ -36584,6 +37280,15 @@ async function createNewSessionWizard({
|
|
|
36584
37280
|
const completedText = replaceActivePathToken({ text: editor.plainText, replacement: completion.replacement });
|
|
36585
37281
|
editor.setText(completedText);
|
|
36586
37282
|
editor.cursorOffset = completedText.length;
|
|
37283
|
+
if (completion.attachmentPath) {
|
|
37284
|
+
const selectedCompletion = createSelectedPathCompletion({
|
|
37285
|
+
attachmentPath: completion.attachmentPath,
|
|
37286
|
+
token: completion.replacement,
|
|
37287
|
+
end: completedText.length
|
|
37288
|
+
});
|
|
37289
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
37290
|
+
}
|
|
37291
|
+
previousPromptEditorText = completedText;
|
|
36587
37292
|
}
|
|
36588
37293
|
pathCompletions = [];
|
|
36589
37294
|
pathCompletionMenuOpen = false;
|
|
@@ -36609,12 +37314,14 @@ async function createNewSessionWizard({
|
|
|
36609
37314
|
if (step === "confirmation") {
|
|
36610
37315
|
if (key.name === "m") {
|
|
36611
37316
|
key.preventDefault();
|
|
37317
|
+
invalidatePendingConfirmation();
|
|
36612
37318
|
model = cycle({ values: newSessionModels, value: model, direction: 1 });
|
|
36613
37319
|
render();
|
|
36614
37320
|
return;
|
|
36615
37321
|
}
|
|
36616
37322
|
if (key.name === "left" || key.name === "right") {
|
|
36617
37323
|
key.preventDefault();
|
|
37324
|
+
invalidatePendingConfirmation();
|
|
36618
37325
|
reasoningEffort = clampedStep({
|
|
36619
37326
|
values: newSessionReasoningEfforts,
|
|
36620
37327
|
value: reasoningEffort,
|
|
@@ -36625,6 +37332,7 @@ async function createNewSessionWizard({
|
|
|
36625
37332
|
}
|
|
36626
37333
|
if (key.name === "e") {
|
|
36627
37334
|
key.preventDefault();
|
|
37335
|
+
invalidatePendingConfirmation();
|
|
36628
37336
|
step = "prompt";
|
|
36629
37337
|
editor.setText(prompt);
|
|
36630
37338
|
render();
|
|
@@ -36632,11 +37340,19 @@ async function createNewSessionWizard({
|
|
|
36632
37340
|
}
|
|
36633
37341
|
if (key.name === "r") {
|
|
36634
37342
|
key.preventDefault();
|
|
37343
|
+
invalidatePendingConfirmation();
|
|
36635
37344
|
step = "repositories";
|
|
36636
37345
|
editor.setText("");
|
|
36637
37346
|
render();
|
|
36638
37347
|
return;
|
|
36639
37348
|
}
|
|
37349
|
+
if (key.name === "b" && branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch")) {
|
|
37350
|
+
key.preventDefault();
|
|
37351
|
+
invalidatePendingConfirmation();
|
|
37352
|
+
step = "branches";
|
|
37353
|
+
render();
|
|
37354
|
+
return;
|
|
37355
|
+
}
|
|
36640
37356
|
}
|
|
36641
37357
|
if (step === "repositorySearch")
|
|
36642
37358
|
return;
|
|
@@ -36657,8 +37373,16 @@ async function createNewSessionWizard({
|
|
|
36657
37373
|
return;
|
|
36658
37374
|
}
|
|
36659
37375
|
}
|
|
37376
|
+
if (step === "branches" && branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch") && (key.name === "up" || key.name === "down" || key.name === "left" || key.name === "right")) {
|
|
37377
|
+
key.preventDefault();
|
|
37378
|
+
branchChoice = branchChoice === "existing" ? "new" : "existing";
|
|
37379
|
+
render();
|
|
37380
|
+
return;
|
|
37381
|
+
}
|
|
36660
37382
|
if (!composerMounted && (key.name === "return" || key.name === "enter")) {
|
|
36661
37383
|
key.preventDefault();
|
|
37384
|
+
if (step === "loadingBranches")
|
|
37385
|
+
return;
|
|
36662
37386
|
submitEditor();
|
|
36663
37387
|
return;
|
|
36664
37388
|
}
|
|
@@ -36747,6 +37471,7 @@ async function createNewSessionWizard({
|
|
|
36747
37471
|
let repositoryLoadState = Array.isArray(repositories) ? "ready" : "loading";
|
|
36748
37472
|
let repositoryRequestGeneration = 0;
|
|
36749
37473
|
let suggestionRequestGeneration = 0;
|
|
37474
|
+
let branchRequestGeneration = 0;
|
|
36750
37475
|
let installationIds = [...new Set(loadedRepositories.map((repository) => repository.githubInstallationId))].sort((left, right) => left.localeCompare(right));
|
|
36751
37476
|
let step = initialDraft ? "confirmation" : "prompt";
|
|
36752
37477
|
let prompt = initialDraft?.prompt ?? "";
|
|
@@ -36758,7 +37483,9 @@ async function createNewSessionWizard({
|
|
|
36758
37483
|
let recomputeInstruction = "";
|
|
36759
37484
|
let suggestionSpinnerFrame = 0;
|
|
36760
37485
|
let attachments = [...initialDraft?.attachments ?? []];
|
|
37486
|
+
let previousPromptEditorText = "";
|
|
36761
37487
|
let pathCompletions = [];
|
|
37488
|
+
let selectedPathCompletions = [];
|
|
36762
37489
|
let completionIndex = 0;
|
|
36763
37490
|
let pathCompletionMenuOpen = false;
|
|
36764
37491
|
let repositoryHeaderHeight;
|
|
@@ -36767,8 +37494,17 @@ async function createNewSessionWizard({
|
|
|
36767
37494
|
let searchSelections;
|
|
36768
37495
|
let suggestedIds = new Set;
|
|
36769
37496
|
let suggestionConfidence;
|
|
37497
|
+
let branchSuggestions = initialDraft?.repositoryBranchOverrides?.map((override) => ({
|
|
37498
|
+
suggestionKind: "continueExistingBranch",
|
|
37499
|
+
repositoryId: override.repositoryId,
|
|
37500
|
+
branchName: override.branchName,
|
|
37501
|
+
pullRequest: null
|
|
37502
|
+
})) ?? [];
|
|
37503
|
+
let branchChoice = initialDraft?.repositoryBranchOverrides?.length ? "existing" : "new";
|
|
36770
37504
|
let status = initialError ?? "";
|
|
37505
|
+
let attachmentFeedback;
|
|
36771
37506
|
let pendingPastedImageWrites = 0;
|
|
37507
|
+
let pendingConfirmation;
|
|
36772
37508
|
let destroyed = false;
|
|
36773
37509
|
let rendererDestroyPromise;
|
|
36774
37510
|
let settled = false;
|
|
@@ -36791,6 +37527,14 @@ async function createNewSessionWizard({
|
|
|
36791
37527
|
submitEditor();
|
|
36792
37528
|
},
|
|
36793
37529
|
onContentChange: () => {
|
|
37530
|
+
if (step === "prompt") {
|
|
37531
|
+
selectedPathCompletions = reconcileSelectedPathCompletions({
|
|
37532
|
+
previousText: previousPromptEditorText,
|
|
37533
|
+
text: editor.plainText,
|
|
37534
|
+
selections: selectedPathCompletions
|
|
37535
|
+
});
|
|
37536
|
+
previousPromptEditorText = editor.plainText;
|
|
37537
|
+
}
|
|
36794
37538
|
pathCompletionMenuOpen = false;
|
|
36795
37539
|
if (step === "repositorySearch") {
|
|
36796
37540
|
repositoryQuery = editor.plainText;
|
|
@@ -36826,6 +37570,35 @@ async function createNewSessionWizard({
|
|
|
36826
37570
|
status = error93 instanceof Error ? error93.message : String(error93);
|
|
36827
37571
|
}
|
|
36828
37572
|
}
|
|
37573
|
+
async function startBranchReview() {
|
|
37574
|
+
const installationId = selectedInstallationId();
|
|
37575
|
+
if (!installationId)
|
|
37576
|
+
return;
|
|
37577
|
+
const repositoryIds = [...selectedRepositoryIds()];
|
|
37578
|
+
const requestGeneration = ++branchRequestGeneration;
|
|
37579
|
+
branchSuggestions = [];
|
|
37580
|
+
branchChoice = "new";
|
|
37581
|
+
step = "loadingBranches";
|
|
37582
|
+
status = "";
|
|
37583
|
+
render();
|
|
37584
|
+
try {
|
|
37585
|
+
const suggestions = await suggestBranches({ installationId, prompt, repositoryIds });
|
|
37586
|
+
if (destroyed || requestGeneration !== branchRequestGeneration || step !== "loadingBranches")
|
|
37587
|
+
return;
|
|
37588
|
+
branchSuggestions = suggestions;
|
|
37589
|
+
const hasExistingBranch = branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch");
|
|
37590
|
+
branchChoice = hasExistingBranch ? "existing" : "new";
|
|
37591
|
+
step = hasExistingBranch ? "branches" : "confirmation";
|
|
37592
|
+
} catch (error93) {
|
|
37593
|
+
if (destroyed || requestGeneration !== branchRequestGeneration || step !== "loadingBranches")
|
|
37594
|
+
return;
|
|
37595
|
+
branchSuggestions = [];
|
|
37596
|
+
branchChoice = "new";
|
|
37597
|
+
status = error93 instanceof Error ? error93.message : String(error93);
|
|
37598
|
+
step = "confirmation";
|
|
37599
|
+
}
|
|
37600
|
+
render();
|
|
37601
|
+
}
|
|
36829
37602
|
async function openPathCompletionMenu() {
|
|
36830
37603
|
if (destroyed)
|
|
36831
37604
|
return;
|
|
@@ -36857,28 +37630,33 @@ async function createNewSessionWizard({
|
|
|
36857
37630
|
}
|
|
36858
37631
|
async function pasteClipboardImage() {
|
|
36859
37632
|
if (!readClipboardImage || !savePastedImage) {
|
|
36860
|
-
|
|
37633
|
+
attachmentFeedback = { kind: "failure", message: "Clipboard image paste is unavailable." };
|
|
36861
37634
|
render();
|
|
36862
37635
|
return;
|
|
36863
37636
|
}
|
|
36864
37637
|
pendingPastedImageWrites += 1;
|
|
36865
|
-
|
|
37638
|
+
attachmentFeedback = { kind: "progress", message: "Saving clipboard image\u2026" };
|
|
36866
37639
|
render();
|
|
36867
37640
|
try {
|
|
36868
37641
|
const image = await readClipboardImage();
|
|
36869
37642
|
const path = await savePastedImage(image);
|
|
36870
37643
|
if (!destroyed) {
|
|
36871
37644
|
editor.insertText(path);
|
|
36872
|
-
|
|
37645
|
+
const selectedCompletion = createSelectedPathCompletion({ attachmentPath: path, token: path, end: editor.cursorOffset });
|
|
37646
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
37647
|
+
previousPromptEditorText = editor.plainText;
|
|
37648
|
+
attachmentFeedback = { kind: "success", message: "Clipboard image ready to attach." };
|
|
36873
37649
|
}
|
|
36874
37650
|
} catch (error93) {
|
|
36875
|
-
|
|
37651
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
36876
37652
|
} finally {
|
|
36877
37653
|
pendingPastedImageWrites -= 1;
|
|
36878
37654
|
}
|
|
36879
37655
|
render();
|
|
36880
37656
|
}
|
|
36881
37657
|
async function confirm() {
|
|
37658
|
+
if (destroyed || settled || step !== "confirmation" || pendingConfirmation)
|
|
37659
|
+
return;
|
|
36882
37660
|
const installationId = selectedInstallationId();
|
|
36883
37661
|
if (!installationId) {
|
|
36884
37662
|
status = "GitHub installation selection is required.";
|
|
@@ -36890,22 +37668,55 @@ async function createNewSessionWizard({
|
|
|
36890
37668
|
fail(new Error("Selected repositories must belong to the chosen GitHub installation."));
|
|
36891
37669
|
return;
|
|
36892
37670
|
}
|
|
37671
|
+
const repositoryBranchOverrides = branchChoice === "existing" ? branchSuggestions.flatMap((suggestion) => suggestion.suggestionKind === "createSessionBranch" ? [] : [{ repositoryId: suggestion.repositoryId, branchName: suggestion.branchName }]) : [];
|
|
37672
|
+
const confirmation = Symbol("confirmation");
|
|
37673
|
+
const confirmedAttachments = [...attachments];
|
|
37674
|
+
const confirmedPrompt = prompt;
|
|
37675
|
+
const confirmedModel = model;
|
|
37676
|
+
const confirmedReasoningEffort = reasoningEffort;
|
|
37677
|
+
const confirmedSelectedPaths = selectedAttachmentPaths({ text: confirmedPrompt, selections: selectedPathCompletions });
|
|
37678
|
+
pendingConfirmation = confirmation;
|
|
37679
|
+
const ownsConfirmation = () => !destroyed && !settled && step === "confirmation" && pendingConfirmation === confirmation;
|
|
36893
37680
|
if (attachPromptPaths) {
|
|
36894
|
-
|
|
36895
|
-
render();
|
|
37681
|
+
attachmentFeedback = undefined;
|
|
36896
37682
|
try {
|
|
36897
37683
|
const promptAttachments = await attachPromptPaths({
|
|
36898
|
-
text:
|
|
36899
|
-
excludedPaths:
|
|
37684
|
+
text: confirmedPrompt,
|
|
37685
|
+
excludedPaths: confirmedAttachments.flatMap((attachment) => attachment.sourcePath ? [attachment.sourcePath] : []),
|
|
37686
|
+
selectedPaths: confirmedSelectedPaths,
|
|
37687
|
+
onUploadStart: ({ filenames }) => {
|
|
37688
|
+
if (!ownsConfirmation())
|
|
37689
|
+
return;
|
|
37690
|
+
attachmentFeedback = { kind: "progress", message: attachmentUploadMessage(filenames) };
|
|
37691
|
+
render();
|
|
37692
|
+
}
|
|
36900
37693
|
});
|
|
36901
|
-
|
|
37694
|
+
if (!ownsConfirmation())
|
|
37695
|
+
return;
|
|
37696
|
+
confirmedAttachments.push(...promptAttachments);
|
|
37697
|
+
attachments = confirmedAttachments;
|
|
37698
|
+
selectedPathCompletions = [];
|
|
37699
|
+
attachmentFeedback = undefined;
|
|
36902
37700
|
} catch (error93) {
|
|
36903
|
-
|
|
37701
|
+
if (!ownsConfirmation())
|
|
37702
|
+
return;
|
|
37703
|
+
pendingConfirmation = undefined;
|
|
37704
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
36904
37705
|
render();
|
|
36905
37706
|
return;
|
|
36906
37707
|
}
|
|
36907
37708
|
}
|
|
36908
|
-
|
|
37709
|
+
if (!ownsConfirmation())
|
|
37710
|
+
return;
|
|
37711
|
+
finish({
|
|
37712
|
+
prompt: confirmedPrompt,
|
|
37713
|
+
githubInstallationId: installationId,
|
|
37714
|
+
repositories: selected,
|
|
37715
|
+
...repositoryBranchOverrides.length > 0 ? { repositoryBranchOverrides } : {},
|
|
37716
|
+
attachments: confirmedAttachments,
|
|
37717
|
+
model: confirmedModel,
|
|
37718
|
+
reasoningEffort: confirmedReasoningEffort
|
|
37719
|
+
});
|
|
36909
37720
|
}
|
|
36910
37721
|
async function submitEditor() {
|
|
36911
37722
|
const value = editor.plainText.trim();
|
|
@@ -36939,8 +37750,12 @@ async function createNewSessionWizard({
|
|
|
36939
37750
|
return;
|
|
36940
37751
|
}
|
|
36941
37752
|
if (step === "repositories") {
|
|
36942
|
-
step = "confirmation";
|
|
36943
37753
|
editor.setText("");
|
|
37754
|
+
startBranchReview();
|
|
37755
|
+
return;
|
|
37756
|
+
}
|
|
37757
|
+
if (step === "branches") {
|
|
37758
|
+
step = "confirmation";
|
|
36944
37759
|
render();
|
|
36945
37760
|
return;
|
|
36946
37761
|
}
|
|
@@ -37004,6 +37819,7 @@ async function createNewSessionWizard({
|
|
|
37004
37819
|
destroy() {
|
|
37005
37820
|
if (rendererDestroyed2)
|
|
37006
37821
|
return;
|
|
37822
|
+
invalidatePendingConfirmation();
|
|
37007
37823
|
destroyed = true;
|
|
37008
37824
|
if (inputHandler)
|
|
37009
37825
|
renderer.removeInputHandler(inputHandler);
|
|
@@ -37040,12 +37856,13 @@ async function createNewSessionWizard({
|
|
|
37040
37856
|
}
|
|
37041
37857
|
}
|
|
37042
37858
|
function stepHeader(title) {
|
|
37043
|
-
return bold3(
|
|
37859
|
+
return bold3(fg6(PALETTE.remyAccent)(title));
|
|
37044
37860
|
}
|
|
37045
37861
|
var WIZARD_STEPS = [
|
|
37046
37862
|
{ key: "describe", label: "Describe" },
|
|
37047
37863
|
{ key: "installation", label: "Installation" },
|
|
37048
37864
|
{ key: "repositories", label: "Repositories" },
|
|
37865
|
+
{ key: "branches", label: "Branches" },
|
|
37049
37866
|
{ key: "confirm", label: "Confirm" }
|
|
37050
37867
|
];
|
|
37051
37868
|
function wizardStepKey(step) {
|
|
@@ -37053,52 +37870,41 @@ function wizardStepKey(step) {
|
|
|
37053
37870
|
return "describe";
|
|
37054
37871
|
if (step === "installation")
|
|
37055
37872
|
return "installation";
|
|
37873
|
+
if (step === "loadingBranches" || step === "branches")
|
|
37874
|
+
return "branches";
|
|
37056
37875
|
if (step === "confirmation")
|
|
37057
37876
|
return "confirm";
|
|
37058
37877
|
return "repositories";
|
|
37059
37878
|
}
|
|
37060
|
-
function renderStepIndicator({ step, hasInstallationChoice }) {
|
|
37061
|
-
const steps = WIZARD_STEPS.filter((entry) => entry.key !== "installation" || hasInstallationChoice);
|
|
37879
|
+
function renderStepIndicator({ step, hasInstallationChoice, hasBranchChoice = false }) {
|
|
37880
|
+
const steps = WIZARD_STEPS.filter((entry) => (entry.key !== "installation" || hasInstallationChoice) && (entry.key !== "branches" || hasBranchChoice));
|
|
37062
37881
|
const currentKey = wizardStepKey(step);
|
|
37063
37882
|
const currentIndex = steps.findIndex((entry) => entry.key === currentKey);
|
|
37064
37883
|
const position = currentIndex >= 0 ? currentIndex + 1 : steps.length;
|
|
37065
37884
|
const crumbs = [];
|
|
37066
37885
|
steps.forEach((entry, index) => {
|
|
37067
37886
|
if (index > 0)
|
|
37068
|
-
crumbs.push(
|
|
37069
|
-
crumbs.push(entry.key === currentKey ? bold3(
|
|
37887
|
+
crumbs.push(fg6(PALETTE.dimText)(" \u203A "));
|
|
37888
|
+
crumbs.push(entry.key === currentKey ? bold3(fg6(PALETTE.bodyText)(entry.label)) : fg6(PALETTE.dimText)(entry.label));
|
|
37070
37889
|
});
|
|
37071
|
-
return new
|
|
37072
|
-
bold3(
|
|
37073
|
-
|
|
37890
|
+
return new StyledText5([
|
|
37891
|
+
bold3(fg6(PALETTE.remyAccent)(`Step ${position} of ${steps.length}`)),
|
|
37892
|
+
fg6(PALETTE.dimText)(" "),
|
|
37074
37893
|
...crumbs
|
|
37075
37894
|
]);
|
|
37076
37895
|
}
|
|
37077
37896
|
function renderSelectableRow({ label, isCursor }) {
|
|
37078
37897
|
const line = `${isCursor ? "> " : " "}${label}`;
|
|
37079
|
-
const styled =
|
|
37898
|
+
const styled = fg6(PALETTE.bodyText)(line);
|
|
37080
37899
|
return [isCursor ? bg3(PALETTE.selectionBg)(styled) : styled];
|
|
37081
37900
|
}
|
|
37082
37901
|
function renderRepositoryRow({ repository, isCursor, isChecked, source }) {
|
|
37083
37902
|
const marker = isChecked ? "[x]" : "[ ]";
|
|
37084
37903
|
const line = `${isCursor ? "> " : " "}${marker} ${repository.fullName}`;
|
|
37085
|
-
const styled = isChecked ? bold3(
|
|
37086
|
-
const tag = source === "suggested" && isChecked ?
|
|
37904
|
+
const styled = isChecked ? bold3(fg6(PALETTE.humanAccent)(line)) : fg6(PALETTE.dimText)(line);
|
|
37905
|
+
const tag = source === "suggested" && isChecked ? dim4(fg6(PALETTE.tool)(" \u25C6 suggested by Remy")) : undefined;
|
|
37087
37906
|
return [isCursor ? bg3(PALETTE.selectionBg)(styled) : styled, ...tag ? [tag] : []];
|
|
37088
37907
|
}
|
|
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
37908
|
function cycle({ values, value, direction }) {
|
|
37103
37909
|
const currentIndex = values.indexOf(value);
|
|
37104
37910
|
const nextIndex = (currentIndex + direction + values.length) % values.length;
|
|
@@ -37114,7 +37920,7 @@ async function createDefaultRenderer2() {
|
|
|
37114
37920
|
}
|
|
37115
37921
|
|
|
37116
37922
|
// src/tui/session-view.ts
|
|
37117
|
-
import { CliRenderEvents as CliRenderEvents4, BoxRenderable as BoxRenderable4, bg as bg4, bold as bold4, dim as
|
|
37923
|
+
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
37924
|
var composerSlashCommands = [
|
|
37119
37925
|
{ value: "/sessions", description: "Back to the session list" },
|
|
37120
37926
|
{ value: "/complete", description: "Finish this session" },
|
|
@@ -37195,6 +38001,7 @@ async function createSessionTui({
|
|
|
37195
38001
|
}
|
|
37196
38002
|
promptHistoryIndex = direction === "up" ? Math.max(0, promptHistoryIndex - 1) : Math.min(promptHistory.length, promptHistoryIndex + 1);
|
|
37197
38003
|
const text = promptHistoryIndex === promptHistory.length ? promptHistoryDraft : promptHistory[promptHistoryIndex];
|
|
38004
|
+
selectedPathCompletions = [];
|
|
37198
38005
|
historyReplacement = text;
|
|
37199
38006
|
composer.setText(text);
|
|
37200
38007
|
composer.cursorOffset = text.length;
|
|
@@ -37217,10 +38024,10 @@ async function createSessionTui({
|
|
|
37217
38024
|
syncTerminalSessionControls();
|
|
37218
38025
|
syncWorkingIndicator();
|
|
37219
38026
|
renderRetainedTranscript();
|
|
37220
|
-
const assistantPreview = latestState.previews.assistantText ? new
|
|
37221
|
-
`)),
|
|
37222
|
-
const reasoningPreview = latestState.previews.reasoningText ? new
|
|
37223
|
-
`)),
|
|
38027
|
+
const assistantPreview = latestState.previews.assistantText ? new StyledText6([dim5(fg7(PALETTE.dimText)(`Live assistant preview
|
|
38028
|
+
`)), fg7(PALETTE.remyAccent)(latestState.previews.assistantText)]) : new StyledText6([]);
|
|
38029
|
+
const reasoningPreview = latestState.previews.reasoningText ? new StyledText6([dim5(fg7(PALETTE.dimText)(`Live reasoning preview
|
|
38030
|
+
`)), dim5(fg7(PALETTE.dimText)(latestState.previews.reasoningText))]) : new StyledText6([]);
|
|
37224
38031
|
const working = isRemyWorking(latestState);
|
|
37225
38032
|
const startedAt = workingTurnStartedAt(latestState);
|
|
37226
38033
|
const elapsedMs = startedAt === undefined ? 0 : Math.max(0, now3() - Date.parse(startedAt));
|
|
@@ -37229,7 +38036,7 @@ async function createSessionTui({
|
|
|
37229
38036
|
frame: workingSpinnerFrames[spinnerFrameIndex],
|
|
37230
38037
|
elapsedMs,
|
|
37231
38038
|
mode: stopState.kind === "stopping" ? "stopping" : "working"
|
|
37232
|
-
}) : new
|
|
38039
|
+
}) : new StyledText6([]);
|
|
37233
38040
|
const liveContent = [assistantPreview, reasoningPreview, liveIndicator].filter((part) => part.chunks.length > 0);
|
|
37234
38041
|
liveTranscript.content = liveContent.length === 0 ? "" : joinStyled([stringToStyledText5(`
|
|
37235
38042
|
|
|
@@ -37245,6 +38052,7 @@ async function createSessionTui({
|
|
|
37245
38052
|
const commandMenu = renderSlashCommandMenu({ commands: slashCompletions, selectedIndex: slashCompletionIndex });
|
|
37246
38053
|
const composerStatus = [
|
|
37247
38054
|
helpVisible ? renderHelpPanel() : undefined,
|
|
38055
|
+
attachmentFeedback ? renderAttachmentFeedback(attachmentFeedback) : undefined,
|
|
37248
38056
|
composerFeedback ? stringToStyledText5(composerFeedback) : undefined,
|
|
37249
38057
|
submissionStatus,
|
|
37250
38058
|
commandMenu,
|
|
@@ -37317,10 +38125,17 @@ async function createSessionTui({
|
|
|
37317
38125
|
}
|
|
37318
38126
|
slashCompletions = composerSlashCommands.filter((command) => command.value.startsWith(value) && command.value !== value && (!["/complete", "/cancel"].includes(command.value) || latestState.aggregateStatus === "open"));
|
|
37319
38127
|
slashCompletionIndex = 0;
|
|
38128
|
+
}, invalidateAttachmentPreparation = function() {
|
|
38129
|
+
attachmentPreparationOwner = undefined;
|
|
38130
|
+
}, ownsAttachmentPreparation = function(owner) {
|
|
38131
|
+
return attachmentPreparationOwner === owner && !destroyed && !settled;
|
|
38132
|
+
}, viewIsActive = function() {
|
|
38133
|
+
return !destroyed && !settled;
|
|
37320
38134
|
}, finish = function(result, error93) {
|
|
37321
38135
|
if (settled)
|
|
37322
38136
|
return;
|
|
37323
38137
|
settled = true;
|
|
38138
|
+
invalidateAttachmentPreparation();
|
|
37324
38139
|
if (error93)
|
|
37325
38140
|
rejectAction(error93);
|
|
37326
38141
|
else
|
|
@@ -37344,17 +38159,21 @@ async function createSessionTui({
|
|
|
37344
38159
|
let settled = false;
|
|
37345
38160
|
let activityExpanded = false;
|
|
37346
38161
|
let composerDraft = initialComposer ?? { text: "", attachments: [] };
|
|
38162
|
+
let previousComposerText = composerDraft.text;
|
|
37347
38163
|
let pathCompletions = [];
|
|
38164
|
+
let selectedPathCompletions = [];
|
|
37348
38165
|
let completionIndex = 0;
|
|
37349
38166
|
let pathCompletionMenuOpen = false;
|
|
37350
38167
|
let slashCompletions = [];
|
|
37351
38168
|
let slashCompletionIndex = 0;
|
|
37352
38169
|
let composerFeedback = initialFeedback;
|
|
38170
|
+
let attachmentFeedback;
|
|
37353
38171
|
const localPromptHistory = [];
|
|
37354
38172
|
let promptHistoryIndex;
|
|
37355
38173
|
let promptHistoryDraft = "";
|
|
37356
38174
|
let historyReplacement;
|
|
37357
38175
|
let pendingPastedImageWrites = 0;
|
|
38176
|
+
let attachmentPreparationOwner;
|
|
37358
38177
|
let admittedSubmissions = [];
|
|
37359
38178
|
let stopState = { kind: "idle" };
|
|
37360
38179
|
let lifecycleRequestState = { kind: "idle" };
|
|
@@ -37393,6 +38212,12 @@ async function createSessionTui({
|
|
|
37393
38212
|
submitComposer();
|
|
37394
38213
|
},
|
|
37395
38214
|
onContentChange: () => {
|
|
38215
|
+
selectedPathCompletions = reconcileSelectedPathCompletions({
|
|
38216
|
+
previousText: previousComposerText,
|
|
38217
|
+
text: composer.plainText,
|
|
38218
|
+
selections: selectedPathCompletions
|
|
38219
|
+
});
|
|
38220
|
+
previousComposerText = composer.plainText;
|
|
37396
38221
|
if (historyReplacement !== composer.plainText)
|
|
37397
38222
|
promptHistoryIndex = undefined;
|
|
37398
38223
|
historyReplacement = undefined;
|
|
@@ -37473,12 +38298,12 @@ async function createSessionTui({
|
|
|
37473
38298
|
};
|
|
37474
38299
|
async function pasteClipboardImage() {
|
|
37475
38300
|
if (!readClipboardImage || !savePastedImage) {
|
|
37476
|
-
|
|
38301
|
+
attachmentFeedback = { kind: "failure", message: "Clipboard image paste is unavailable." };
|
|
37477
38302
|
render();
|
|
37478
38303
|
return;
|
|
37479
38304
|
}
|
|
37480
38305
|
pendingPastedImageWrites += 1;
|
|
37481
|
-
|
|
38306
|
+
attachmentFeedback = { kind: "progress", message: "Saving clipboard image\u2026" };
|
|
37482
38307
|
render();
|
|
37483
38308
|
try {
|
|
37484
38309
|
const image = await readClipboardImage();
|
|
@@ -37486,10 +38311,13 @@ async function createSessionTui({
|
|
|
37486
38311
|
if (destroyed)
|
|
37487
38312
|
return;
|
|
37488
38313
|
composer.insertText(path);
|
|
37489
|
-
|
|
38314
|
+
const selectedCompletion = createSelectedPathCompletion({ attachmentPath: path, token: path, end: composer.cursorOffset });
|
|
38315
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
38316
|
+
previousComposerText = composer.plainText;
|
|
38317
|
+
attachmentFeedback = { kind: "success", message: "Clipboard image ready to attach." };
|
|
37490
38318
|
} catch (error93) {
|
|
37491
38319
|
if (!destroyed)
|
|
37492
|
-
|
|
38320
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
37493
38321
|
} finally {
|
|
37494
38322
|
pendingPastedImageWrites -= 1;
|
|
37495
38323
|
if (!destroyed)
|
|
@@ -37549,6 +38377,15 @@ async function createSessionTui({
|
|
|
37549
38377
|
const completedText = replaceActivePathToken({ text: composer.plainText, replacement: completion.replacement });
|
|
37550
38378
|
composer.setText(completedText);
|
|
37551
38379
|
composer.cursorOffset = completedText.length;
|
|
38380
|
+
if (completion.attachmentPath) {
|
|
38381
|
+
const selectedCompletion = createSelectedPathCompletion({
|
|
38382
|
+
attachmentPath: completion.attachmentPath,
|
|
38383
|
+
token: completion.replacement,
|
|
38384
|
+
end: completedText.length
|
|
38385
|
+
});
|
|
38386
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
38387
|
+
}
|
|
38388
|
+
previousComposerText = completedText;
|
|
37552
38389
|
}
|
|
37553
38390
|
pathCompletions = [];
|
|
37554
38391
|
pathCompletionMenuOpen = false;
|
|
@@ -37579,8 +38416,10 @@ async function createSessionTui({
|
|
|
37579
38416
|
render();
|
|
37580
38417
|
}
|
|
37581
38418
|
async function submitComposer() {
|
|
38419
|
+
if (attachmentPreparationOwner)
|
|
38420
|
+
return;
|
|
37582
38421
|
if (pendingPastedImageWrites > 0) {
|
|
37583
|
-
|
|
38422
|
+
attachmentFeedback = { kind: "progress", message: "Saving pasted image\u2026" };
|
|
37584
38423
|
render();
|
|
37585
38424
|
return;
|
|
37586
38425
|
}
|
|
@@ -37588,33 +38427,61 @@ async function createSessionTui({
|
|
|
37588
38427
|
const value = composerDraft.text;
|
|
37589
38428
|
if (value.trim() === "")
|
|
37590
38429
|
return;
|
|
37591
|
-
|
|
38430
|
+
const preparationOwner = Symbol("attachment preparation");
|
|
38431
|
+
attachmentPreparationOwner = preparationOwner;
|
|
38432
|
+
if (await handleComposerCommand(value)) {
|
|
38433
|
+
if (attachmentPreparationOwner === preparationOwner)
|
|
38434
|
+
invalidateAttachmentPreparation();
|
|
38435
|
+
return;
|
|
38436
|
+
}
|
|
38437
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
37592
38438
|
return;
|
|
37593
38439
|
if (lifecycleRequestState.kind === "pending") {
|
|
37594
38440
|
composerFeedback = `${lifecycleOperationPresentParticiple(lifecycleRequestState.operation)} the session \u2014 messages are paused.`;
|
|
37595
38441
|
render();
|
|
38442
|
+
invalidateAttachmentPreparation();
|
|
37596
38443
|
return;
|
|
37597
38444
|
}
|
|
37598
38445
|
if (latestState.aggregateStatus !== "open") {
|
|
37599
38446
|
composerFeedback = "This session is already terminal. Run /sessions to return to the session list.";
|
|
37600
38447
|
render();
|
|
38448
|
+
invalidateAttachmentPreparation();
|
|
37601
38449
|
return;
|
|
37602
38450
|
}
|
|
37603
38451
|
if (attachPromptPaths) {
|
|
37604
|
-
|
|
37605
|
-
render();
|
|
38452
|
+
let handedOffToAdmission = false;
|
|
37606
38453
|
try {
|
|
37607
38454
|
const attachments = await attachPromptPaths({
|
|
37608
38455
|
text: composerDraft.text,
|
|
37609
|
-
excludedPaths: composerDraft.attachments.flatMap((attachment) => attachment.sourcePath ? [attachment.sourcePath] : [])
|
|
38456
|
+
excludedPaths: composerDraft.attachments.flatMap((attachment) => attachment.sourcePath ? [attachment.sourcePath] : []),
|
|
38457
|
+
selectedPaths: selectedAttachmentPaths({ text: composerDraft.text, selections: selectedPathCompletions }),
|
|
38458
|
+
onUploadStart: ({ filenames }) => {
|
|
38459
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38460
|
+
return;
|
|
38461
|
+
attachmentFeedback = { kind: "progress", message: attachmentUploadMessage(filenames) };
|
|
38462
|
+
render();
|
|
38463
|
+
}
|
|
37610
38464
|
});
|
|
38465
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38466
|
+
return;
|
|
37611
38467
|
composerDraft = { ...composerDraft, attachments: [...composerDraft.attachments, ...attachments] };
|
|
38468
|
+
selectedPathCompletions = [];
|
|
38469
|
+
attachmentFeedback = undefined;
|
|
38470
|
+
handedOffToAdmission = true;
|
|
37612
38471
|
} catch (error93) {
|
|
37613
|
-
|
|
38472
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38473
|
+
return;
|
|
38474
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
37614
38475
|
render();
|
|
37615
38476
|
return;
|
|
38477
|
+
} finally {
|
|
38478
|
+
if (!handedOffToAdmission && attachmentPreparationOwner === preparationOwner)
|
|
38479
|
+
invalidateAttachmentPreparation();
|
|
37616
38480
|
}
|
|
37617
38481
|
}
|
|
38482
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38483
|
+
return;
|
|
38484
|
+
invalidateAttachmentPreparation();
|
|
37618
38485
|
const submission = {
|
|
37619
38486
|
id: crypto.randomUUID(),
|
|
37620
38487
|
idempotencyKey: crypto.randomUUID(),
|
|
@@ -37651,13 +38518,13 @@ async function createSessionTui({
|
|
|
37651
38518
|
if (command === "/complete") {
|
|
37652
38519
|
composer.setText("");
|
|
37653
38520
|
composerDraft = { ...composerDraft, text: "" };
|
|
37654
|
-
|
|
38521
|
+
handleLifecycleRequest("complete");
|
|
37655
38522
|
return true;
|
|
37656
38523
|
}
|
|
37657
38524
|
if (command === "/cancel") {
|
|
37658
38525
|
composer.setText("");
|
|
37659
38526
|
composerDraft = { ...composerDraft, text: "" };
|
|
37660
|
-
|
|
38527
|
+
handleLifecycleRequest("cancel");
|
|
37661
38528
|
return true;
|
|
37662
38529
|
}
|
|
37663
38530
|
if (command === "/logout") {
|
|
@@ -37685,16 +38552,22 @@ async function createSessionTui({
|
|
|
37685
38552
|
attachments: submission.attachments,
|
|
37686
38553
|
idempotencyKey: submission.idempotencyKey
|
|
37687
38554
|
});
|
|
38555
|
+
if (!viewIsActive())
|
|
38556
|
+
return;
|
|
37688
38557
|
localPromptHistory.push(submission.text);
|
|
37689
38558
|
const messageId = latestState.activeMessageId;
|
|
37690
38559
|
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
38560
|
composerFeedback = undefined;
|
|
37692
38561
|
} catch (error93) {
|
|
38562
|
+
if (!viewIsActive())
|
|
38563
|
+
return;
|
|
37693
38564
|
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
38565
|
}
|
|
37695
38566
|
render();
|
|
37696
38567
|
}
|
|
37697
38568
|
async function retryOldestFailedSubmission() {
|
|
38569
|
+
if (!viewIsActive())
|
|
38570
|
+
return;
|
|
37698
38571
|
const failed = admittedSubmissions.find((submission) => submission.status === "failed");
|
|
37699
38572
|
if (!failed)
|
|
37700
38573
|
return;
|
|
@@ -37810,6 +38683,7 @@ async function createSessionTui({
|
|
|
37810
38683
|
if (destroyed)
|
|
37811
38684
|
return;
|
|
37812
38685
|
destroyed = true;
|
|
38686
|
+
invalidateAttachmentPreparation();
|
|
37813
38687
|
if (workingSpinnerTimer) {
|
|
37814
38688
|
clearInterval(workingSpinnerTimer);
|
|
37815
38689
|
workingSpinnerTimer = undefined;
|
|
@@ -37851,9 +38725,9 @@ function renderSlashCommandMenu({ commands, selectedIndex }) {
|
|
|
37851
38725
|
const rows = commands.map((command, index) => {
|
|
37852
38726
|
const selected = index === selectedIndex;
|
|
37853
38727
|
const rowStyle = selected ? bg4(PALETTE.selectionBg) : (chunk) => chunk;
|
|
37854
|
-
return new
|
|
37855
|
-
rowStyle(
|
|
37856
|
-
rowStyle(
|
|
38728
|
+
return new StyledText6([
|
|
38729
|
+
rowStyle(fg7(PALETTE.remyAccent)(`${selected ? "\u203A " : " "}${command.value.padEnd(12)}`)),
|
|
38730
|
+
rowStyle(fg7(PALETTE.dimText)(command.description))
|
|
37857
38731
|
]);
|
|
37858
38732
|
});
|
|
37859
38733
|
return joinStyled([joinStyled(rows, `
|
|
@@ -37874,15 +38748,15 @@ var helpEntries = [
|
|
|
37874
38748
|
];
|
|
37875
38749
|
function renderHelpPanel() {
|
|
37876
38750
|
const width = helpEntries.reduce((max, entry) => Math.max(max, entry.token.length), 0) + 2;
|
|
37877
|
-
const rows = [new
|
|
38751
|
+
const rows = [new StyledText6([dim5(fg7(PALETTE.dimText)("What you can do here"))])];
|
|
37878
38752
|
let lastGroup;
|
|
37879
38753
|
for (const entry of helpEntries) {
|
|
37880
38754
|
if (lastGroup !== undefined && entry.group !== lastGroup)
|
|
37881
|
-
rows.push(new
|
|
38755
|
+
rows.push(new StyledText6([fg7(PALETTE.dimText)(" ")]));
|
|
37882
38756
|
lastGroup = entry.group;
|
|
37883
|
-
rows.push(new
|
|
37884
|
-
|
|
37885
|
-
|
|
38757
|
+
rows.push(new StyledText6([
|
|
38758
|
+
fg7(PALETTE.remyAccent)(` ${entry.token.padEnd(width)}`),
|
|
38759
|
+
dim5(fg7(PALETTE.dimText)(entry.description))
|
|
37886
38760
|
]));
|
|
37887
38761
|
}
|
|
37888
38762
|
return joinStyled(rows, `
|
|
@@ -37896,23 +38770,22 @@ function renderStatusBand({ state, working, elapsedMs, stopState, lifecycleReque
|
|
|
37896
38770
|
const pendingOperation = lifecycleRequestState?.kind === "pending" ? lifecycleRequestState.operation : undefined;
|
|
37897
38771
|
const presentation = pendingOperation ? { label: lifecycleOperationPresentParticiple(pendingOperation), color: PALETTE.approvalQuestion } : stopping ? { label: "Stopping", color: PALETTE.approvalQuestion } : sessionViewStatePresentation({ aggregateStatus: state.aggregateStatus, isWorking: working });
|
|
37898
38772
|
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);
|
|
38773
|
+
const connection = state.connectionStatus === "connected" ? new StyledText6([fg7(PALETTE.statusCompleted)("\u25CF "), dim5(fg7(PALETTE.dimText)("connected"))]) : new StyledText6([fg7(PALETTE.approvalQuestion)("\u25CF reconnecting\u2026")]);
|
|
38774
|
+
const sessionLabel = state.sessionNumber !== undefined ? `#${state.sessionNumber}` : state.sessionId;
|
|
37902
38775
|
const pullRequest = state.pullRequest;
|
|
37903
|
-
|
|
37904
|
-
|
|
37905
|
-
...repositoryLabel ? [
|
|
37906
|
-
|
|
37907
|
-
bold4(
|
|
37908
|
-
|
|
37909
|
-
|
|
37910
|
-
|
|
37911
|
-
|
|
37912
|
-
|
|
37913
|
-
|
|
37914
|
-
|
|
37915
|
-
|
|
38776
|
+
const contextStatus = renderContextStatus(state.context.snapshot);
|
|
38777
|
+
const headerContext = [
|
|
38778
|
+
...repositoryLabel ? [[dim5(fg7(PALETTE.dimText)(repositoryLabel))]] : [],
|
|
38779
|
+
...pullRequest ? [[bold4(fg7(statusColor(pullRequest.status))(`PR#${pullRequest.number}`))]] : [],
|
|
38780
|
+
[bold4(fg7(presentation.color)(`${presentation.label}${elapsed}`))],
|
|
38781
|
+
connection.chunks,
|
|
38782
|
+
...contextStatus ? [[dim5(fg7(PALETTE.dimText)(contextStatus))]] : []
|
|
38783
|
+
];
|
|
38784
|
+
return joinStyled([
|
|
38785
|
+
[bold4(fg7(PALETTE.bodyText)(`${sessionLabel} ${state.title ?? "Untitled session"}`))],
|
|
38786
|
+
joinStyled(headerContext, " \xB7 ")
|
|
38787
|
+
], `
|
|
38788
|
+
`);
|
|
37916
38789
|
}
|
|
37917
38790
|
function renderContextStatus(snapshot) {
|
|
37918
38791
|
if (snapshot.status === "unknown")
|
|
@@ -37923,33 +38796,39 @@ function renderContextStatus(snapshot) {
|
|
|
37923
38796
|
function renderTerminalSessionBand({ aggregateStatus }) {
|
|
37924
38797
|
const status = sessionStatusLabel(aggregateStatus).toLowerCase();
|
|
37925
38798
|
return joinStyled([
|
|
37926
|
-
new
|
|
37927
|
-
bg4(PALETTE.selectionBg)(bold4(
|
|
37928
|
-
bg4(PALETTE.selectionBg)(
|
|
38799
|
+
new StyledText6([
|
|
38800
|
+
bg4(PALETTE.selectionBg)(bold4(fg7(PALETTE.systemAccent)(` Session ${status} `))),
|
|
38801
|
+
bg4(PALETTE.selectionBg)(fg7(PALETTE.bodyText)("\u2014 this conversation is closed. "))
|
|
37929
38802
|
]),
|
|
37930
|
-
new
|
|
37931
|
-
bg4(PALETTE.selectionBg)(
|
|
37932
|
-
bg4(PALETTE.selectionBg)(
|
|
37933
|
-
bg4(PALETTE.selectionBg)(
|
|
37934
|
-
bg4(PALETTE.selectionBg)(
|
|
38803
|
+
new StyledText6([
|
|
38804
|
+
bg4(PALETTE.selectionBg)(fg7(PALETTE.remyAccent)(" esc: ")),
|
|
38805
|
+
bg4(PALETTE.selectionBg)(dim5(fg7(PALETTE.dimText)("return to dashboard \xB7 "))),
|
|
38806
|
+
bg4(PALETTE.selectionBg)(fg7(PALETTE.remyAccent)("n: ")),
|
|
38807
|
+
bg4(PALETTE.selectionBg)(dim5(fg7(PALETTE.dimText)("new session ")))
|
|
37935
38808
|
])
|
|
37936
38809
|
], `
|
|
37937
38810
|
`);
|
|
37938
38811
|
}
|
|
37939
38812
|
function renderActionBar({ state, working, stopState, lifecycleRequestState }) {
|
|
37940
38813
|
if (lifecycleRequestState.kind === "pending")
|
|
37941
|
-
return new
|
|
38814
|
+
return new StyledText6([dim5(fg7(PALETTE.dimText)(`${lifecycleOperationPresentParticiple(lifecycleRequestState.operation).toLowerCase()} the session\u2026`))]);
|
|
37942
38815
|
if (state.aggregateStatus !== "open")
|
|
37943
|
-
return new
|
|
38816
|
+
return new StyledText6([]);
|
|
37944
38817
|
const stopToken = working && stopState.kind === "failed" ? ["esc retry stop"] : [];
|
|
37945
38818
|
const idleGroup = lifecycleRequestState.kind === "failed" ? `/${lifecycleRequestState.operation} retries \xB7 /new \xB7 /sessions` : "/new \xB7 /sessions \xB7 /complete \xB7 /cancel";
|
|
37946
38819
|
const tokens = working ? ["/ commands", ...stopToken, "ctrl+o activity"] : ["/ commands", idleGroup, "ctrl+o activity"];
|
|
37947
|
-
return new
|
|
38820
|
+
return new StyledText6([dim5(fg7(PALETTE.dimText)(tokens.join(" \xB7 ")))]);
|
|
37948
38821
|
}
|
|
37949
38822
|
function isRemyWorking(state) {
|
|
37950
|
-
if (state.aggregateStatus !== "open" || state.connectionStatus !== "connected")
|
|
38823
|
+
if (state.aggregateStatus !== "open" || state.connectionStatus !== "connected") {
|
|
37951
38824
|
return false;
|
|
37952
|
-
|
|
38825
|
+
}
|
|
38826
|
+
if (state.activeMessageId !== undefined && state.messageTurns[state.activeMessageId]?.outcome === undefined)
|
|
38827
|
+
return true;
|
|
38828
|
+
return isWorkingAgentStatus(state.agentStatus) && hasRunningTurn(state);
|
|
38829
|
+
}
|
|
38830
|
+
function isWorkingAgentStatus(agentStatus) {
|
|
38831
|
+
return agentStatus === undefined || agentStatus === "pending" || agentStatus === "queued" || agentStatus === "running";
|
|
37953
38832
|
}
|
|
37954
38833
|
function workingTurnStartedAt(state) {
|
|
37955
38834
|
const activeTurn = state.activeMessageId === undefined ? undefined : state.messageTurns[state.activeMessageId];
|
|
@@ -37990,58 +38869,64 @@ function renderTimeline({ state, activityExpanded }) {
|
|
|
37990
38869
|
}
|
|
37991
38870
|
function renderTimelineItem(item, activityExpanded) {
|
|
37992
38871
|
if (item.kind === "message") {
|
|
37993
|
-
const header = new
|
|
38872
|
+
const header = new StyledText6([
|
|
37994
38873
|
renderTimestampChunk(item.occurredAt),
|
|
37995
38874
|
renderRoleLabel({ label: item.author.label, role: item.author.role }),
|
|
37996
|
-
...item.author.source ? [
|
|
38875
|
+
...item.author.source ? [dim5(fg7(PALETTE.dimText)(` (${item.author.source})`))] : []
|
|
37997
38876
|
]);
|
|
37998
|
-
|
|
38877
|
+
const attachments = renderAttachmentSummary(item.attachments);
|
|
38878
|
+
return joinStyled([
|
|
38879
|
+
header,
|
|
38880
|
+
renderMessageBody({ text: item.text, role: item.author.role }),
|
|
38881
|
+
...attachments.chunks.length > 0 ? [attachments] : []
|
|
38882
|
+
], `
|
|
37999
38883
|
`);
|
|
38000
38884
|
}
|
|
38001
38885
|
if (item.kind === "plan")
|
|
38002
38886
|
return renderPlan({ item, expanded: activityExpanded });
|
|
38003
38887
|
if (item.artifactKind === "tela_page") {
|
|
38004
|
-
return new
|
|
38888
|
+
return new StyledText6([
|
|
38005
38889
|
renderTimestampChunk(item.occurredAt),
|
|
38006
|
-
bold4(
|
|
38007
|
-
|
|
38008
|
-
|
|
38890
|
+
bold4(fg7(PALETTE.tool)("Tela Page: ")),
|
|
38891
|
+
fg7(PALETTE.bodyText)(item.title),
|
|
38892
|
+
dim5(fg7(PALETTE.dimText)(` \xB7 ${item.url}`))
|
|
38009
38893
|
]);
|
|
38010
38894
|
}
|
|
38011
|
-
return new
|
|
38895
|
+
return new StyledText6([
|
|
38012
38896
|
renderTimestampChunk(item.occurredAt),
|
|
38013
|
-
bold4(
|
|
38014
|
-
|
|
38897
|
+
bold4(fg7(PALETTE.tool)("Artifact: ")),
|
|
38898
|
+
fg7(PALETTE.bodyText)(item.title),
|
|
38899
|
+
...item.previewUrl ? [dim5(fg7(PALETTE.dimText)(` (${item.previewUrl})`))] : []
|
|
38015
38900
|
]);
|
|
38016
38901
|
}
|
|
38017
38902
|
var collapsedPlanItemLimit = 5;
|
|
38018
38903
|
function renderPlan({ item, expanded }) {
|
|
38019
38904
|
if (item.items.length === 0)
|
|
38020
|
-
return new
|
|
38905
|
+
return new StyledText6([]);
|
|
38021
38906
|
const completed = item.items.filter((workItem) => workItem.status === "completed").length;
|
|
38022
38907
|
const visibleItems = expanded ? item.items : collapsedPlanItems(item.items);
|
|
38023
38908
|
const rows = visibleItems.map((workItem) => {
|
|
38024
38909
|
if (workItem.status === "in_progress") {
|
|
38025
|
-
return new
|
|
38026
|
-
|
|
38027
|
-
bold4(
|
|
38910
|
+
return new StyledText6([
|
|
38911
|
+
fg7(PALETTE.remyAccent)(" \u25B8 "),
|
|
38912
|
+
bold4(fg7(PALETTE.bodyText)(workItem.title))
|
|
38028
38913
|
]);
|
|
38029
38914
|
}
|
|
38030
38915
|
const glyph = workItem.status === "completed" ? "\u2713" : "\u25CB";
|
|
38031
|
-
return new
|
|
38032
|
-
|
|
38033
|
-
|
|
38916
|
+
return new StyledText6([
|
|
38917
|
+
fg7(PALETTE.dimText)(` ${glyph} `),
|
|
38918
|
+
dim5(fg7(PALETTE.dimText)(workItem.title))
|
|
38034
38919
|
]);
|
|
38035
38920
|
});
|
|
38036
38921
|
const hidden = item.items.length - visibleItems.length;
|
|
38037
38922
|
return joinStyled([
|
|
38038
|
-
new
|
|
38923
|
+
new StyledText6([
|
|
38039
38924
|
renderTimestampChunk(item.occurredAt),
|
|
38040
|
-
bold4(
|
|
38041
|
-
|
|
38925
|
+
bold4(fg7(PALETTE.remyAccent)("Plan")),
|
|
38926
|
+
dim5(fg7(PALETTE.dimText)(` \xB7 ${completed}/${item.items.length} done`))
|
|
38042
38927
|
]),
|
|
38043
38928
|
...rows,
|
|
38044
|
-
...hidden > 0 ? [new
|
|
38929
|
+
...hidden > 0 ? [new StyledText6([dim5(fg7(PALETTE.dimText)(` \u2026 ${hidden} more task${hidden === 1 ? "" : "s"} \xB7 Ctrl+O details`))])] : []
|
|
38045
38930
|
], `
|
|
38046
38931
|
`);
|
|
38047
38932
|
}
|
|
@@ -38088,7 +38973,7 @@ function canonicalValuesEqual(left, right) {
|
|
|
38088
38973
|
}
|
|
38089
38974
|
function renderActivityGroup({ items, activityExpanded }) {
|
|
38090
38975
|
if (items.length === 0)
|
|
38091
|
-
return new
|
|
38976
|
+
return new StyledText6([]);
|
|
38092
38977
|
const summary = renderActivitySummary({ items, includeTimestamp: activityExpanded });
|
|
38093
38978
|
const lastInFlightIndex = items.length - 1;
|
|
38094
38979
|
if (!activityExpanded) {
|
|
@@ -38097,9 +38982,9 @@ function renderActivityGroup({ items, activityExpanded }) {
|
|
|
38097
38982
|
item,
|
|
38098
38983
|
inFlight: item.card.kind === "tool" && tailStart + index === lastInFlightIndex
|
|
38099
38984
|
}));
|
|
38100
|
-
const elision = tailStart > 0 ? new
|
|
38985
|
+
const elision = tailStart > 0 ? new StyledText6([dim5(fg7(PALETTE.dimText)(` \u2026 ${tailStart} earlier step${tailStart === 1 ? "" : "s"}`))]) : undefined;
|
|
38101
38986
|
return joinStyled([
|
|
38102
|
-
new
|
|
38987
|
+
new StyledText6([renderTimestampChunk(items[0].occurredAt), dim5(fg7(PALETTE.dimText)("Worked"))]),
|
|
38103
38988
|
...elision ? [elision] : [],
|
|
38104
38989
|
...tail,
|
|
38105
38990
|
summary
|
|
@@ -38118,9 +39003,9 @@ function renderExpandedActivityHierarchy({ items, lastInFlightIndex }) {
|
|
|
38118
39003
|
const attribution = item.card.attribution;
|
|
38119
39004
|
if (attribution?.status === "resolved") {
|
|
38120
39005
|
if (activeOwnerKey !== attribution.ownerKey) {
|
|
38121
|
-
rendered.push(new
|
|
38122
|
-
|
|
38123
|
-
|
|
39006
|
+
rendered.push(new StyledText6([
|
|
39007
|
+
dim5(fg7(PALETTE.dimText)(" ")),
|
|
39008
|
+
fg7(PALETTE.bodyText)(`Subagent \xB7 ${lineagePathLabel(attribution.path)}`)
|
|
38124
39009
|
]));
|
|
38125
39010
|
}
|
|
38126
39011
|
activeOwnerKey = attribution.ownerKey;
|
|
@@ -38147,11 +39032,11 @@ function renderExpandedActivityStep({ item, count, inFlight, nested }) {
|
|
|
38147
39032
|
const disclosure = renderActivityDisclosure(item.card);
|
|
38148
39033
|
const title = expandedActivityTitle(item.card);
|
|
38149
39034
|
const indent = nested ? " " : " ";
|
|
38150
|
-
const step = new
|
|
38151
|
-
|
|
38152
|
-
|
|
38153
|
-
...count > 1 ? [
|
|
38154
|
-
...showSummary ? [
|
|
39035
|
+
const step = new StyledText6([
|
|
39036
|
+
fg7(color)(`${indent}${glyph} `),
|
|
39037
|
+
fg7(PALETTE.bodyText)(title),
|
|
39038
|
+
...count > 1 ? [dim5(fg7(PALETTE.dimText)(` \xD7${count}`))] : [],
|
|
39039
|
+
...showSummary ? [dim5(fg7(PALETTE.dimText)(`
|
|
38155
39040
|
${indent} ${item.card.summary}`))] : []
|
|
38156
39041
|
]);
|
|
38157
39042
|
return disclosure ? joinStyled([step, disclosure], `
|
|
@@ -38178,23 +39063,23 @@ function renderActivityDisclosure(card) {
|
|
|
38178
39063
|
const details = [];
|
|
38179
39064
|
if (card.detail !== undefined) {
|
|
38180
39065
|
const safeDetail = terminalSafeText(card.detail);
|
|
38181
|
-
details.push(card.detailFormat === "code" ? renderCodeSnippet({ text: safeDetail, indent: " " }) : new
|
|
39066
|
+
details.push(card.detailFormat === "code" ? renderCodeSnippet({ text: safeDetail, indent: " " }) : new StyledText6([dim5(fg7(PALETTE.dimText)(` ${safeDetail}`))]));
|
|
38182
39067
|
}
|
|
38183
39068
|
return details.length === 0 ? undefined : joinStyled(details, `
|
|
38184
39069
|
|
|
38185
39070
|
`);
|
|
38186
39071
|
}
|
|
38187
39072
|
function renderActivitySummary({ items, includeTimestamp }) {
|
|
38188
|
-
return new
|
|
39073
|
+
return new StyledText6([
|
|
38189
39074
|
...includeTimestamp ? [renderTimestampChunk(items[0].occurredAt)] : [],
|
|
38190
|
-
|
|
39075
|
+
dim5(fg7(PALETTE.dimText)(`Worked \xB7 ${items.length} step${items.length === 1 ? "" : "s"} \xB7 Ctrl+O details`))
|
|
38191
39076
|
]);
|
|
38192
39077
|
}
|
|
38193
39078
|
function renderCollapsedActivityStep({ item, inFlight }) {
|
|
38194
39079
|
const { glyph, color } = activityGlyph({ kind: item.card.kind, inFlight });
|
|
38195
|
-
return new
|
|
38196
|
-
|
|
38197
|
-
|
|
39080
|
+
return new StyledText6([
|
|
39081
|
+
fg7(color)(` ${glyph} `),
|
|
39082
|
+
dim5(fg7(PALETTE.dimText)(collapsedActivityTitle(item.card)))
|
|
38198
39083
|
]);
|
|
38199
39084
|
}
|
|
38200
39085
|
function collapsedActivityTitle(card) {
|
|
@@ -38211,15 +39096,15 @@ function collapsedActivityTitle(card) {
|
|
|
38211
39096
|
function renderSubmittedTimeline({ state, admittedSubmissions }) {
|
|
38212
39097
|
const submitted = admittedSubmissions.filter((submission) => submission.status === "submitted" && !state.transcript.some((item) => item.kind === "message" && item.messageId === submission.messageId));
|
|
38213
39098
|
const messages = submitted.map((submission) => {
|
|
38214
|
-
const attachments =
|
|
38215
|
-
const header = new
|
|
39099
|
+
const attachments = renderAttachmentSummary(submission.attachments);
|
|
39100
|
+
const header = new StyledText6([
|
|
38216
39101
|
renderRoleLabel({ label: "You", role: "human" }),
|
|
38217
|
-
|
|
39102
|
+
dim5(fg7(PALETTE.dimText)(" (CLI)"))
|
|
38218
39103
|
]);
|
|
38219
39104
|
return joinStyled([
|
|
38220
39105
|
header,
|
|
38221
39106
|
renderMessageBody({ text: submission.text, role: "human" }),
|
|
38222
|
-
...attachments ? [
|
|
39107
|
+
...attachments.chunks.length > 0 ? [attachments] : []
|
|
38223
39108
|
], `
|
|
38224
39109
|
`);
|
|
38225
39110
|
});
|
|
@@ -38229,20 +39114,20 @@ function renderSubmittedTimeline({ state, admittedSubmissions }) {
|
|
|
38229
39114
|
}
|
|
38230
39115
|
function renderWorkingIndicator({ frame, elapsedMs, mode }) {
|
|
38231
39116
|
if (mode === "stopping") {
|
|
38232
|
-
return new
|
|
38233
|
-
|
|
38234
|
-
|
|
39117
|
+
return new StyledText6([
|
|
39118
|
+
fg7(PALETTE.approvalQuestion)("\u23F9"),
|
|
39119
|
+
dim5(fg7(PALETTE.dimText)(` Stopping\u2026 ${formatElapsed(elapsedMs)}`))
|
|
38235
39120
|
]);
|
|
38236
39121
|
}
|
|
38237
39122
|
if (mode === "complete" || mode === "cancel") {
|
|
38238
|
-
return new
|
|
38239
|
-
|
|
38240
|
-
|
|
39123
|
+
return new StyledText6([
|
|
39124
|
+
fg7(PALETTE.approvalQuestion)(frame),
|
|
39125
|
+
dim5(fg7(PALETTE.dimText)(` ${lifecycleOperationPresentParticiple(mode)} the session\u2026`))
|
|
38241
39126
|
]);
|
|
38242
39127
|
}
|
|
38243
|
-
return new
|
|
38244
|
-
|
|
38245
|
-
|
|
39128
|
+
return new StyledText6([
|
|
39129
|
+
fg7(PALETTE.remyAccent)(frame),
|
|
39130
|
+
dim5(fg7(PALETTE.dimText)(` Working ${formatElapsed(elapsedMs)}`))
|
|
38246
39131
|
]);
|
|
38247
39132
|
}
|
|
38248
39133
|
function lifecycleOperationPresentParticiple(operation) {
|
|
@@ -38264,22 +39149,22 @@ function truncate2(value, width) {
|
|
|
38264
39149
|
}
|
|
38265
39150
|
function renderComposerStatus({ admittedSubmissions }) {
|
|
38266
39151
|
if (admittedSubmissions.length === 0)
|
|
38267
|
-
return new
|
|
39152
|
+
return new StyledText6([]);
|
|
38268
39153
|
const parts = admittedSubmissions.flatMap((submission) => {
|
|
38269
39154
|
if (submission.status === "submitted")
|
|
38270
39155
|
return [];
|
|
38271
|
-
const attachments = submission.attachments
|
|
38272
|
-
|
|
38273
|
-
|
|
38274
|
-
|
|
38275
|
-
|
|
38276
|
-
|
|
38277
|
-
`
|
|
38278
|
-
|
|
38279
|
-
|
|
38280
|
-
|
|
38281
|
-
|
|
38282
|
-
|
|
39156
|
+
const attachments = renderAttachmentSummary(submission.attachments);
|
|
39157
|
+
return [submission.status === "failed" ? joinStyled([
|
|
39158
|
+
new StyledText6([bold4(fg7(PALETTE.failure)("Failed: ")), fg7(PALETTE.bodyText)(submission.error ?? "")]),
|
|
39159
|
+
renderMessageBody({ text: submission.text, role: "human" }),
|
|
39160
|
+
...attachments.chunks.length > 0 ? [attachments] : [],
|
|
39161
|
+
new StyledText6([dim5(fg7(PALETTE.dimText)("Ctrl+R retries oldest failed submission."))])
|
|
39162
|
+
], `
|
|
39163
|
+
`) : joinStyled([
|
|
39164
|
+
new StyledText6([fg7(PALETTE.progress)("Admitting: "), fg7(PALETTE.bodyText)(submission.text)]),
|
|
39165
|
+
...attachments.chunks.length > 0 ? [attachments] : []
|
|
39166
|
+
], `
|
|
39167
|
+
`)];
|
|
38283
39168
|
});
|
|
38284
39169
|
return joinStyled(parts, `
|
|
38285
39170
|
|
|
@@ -38290,10 +39175,15 @@ async function createDefaultRenderer3() {
|
|
|
38290
39175
|
}
|
|
38291
39176
|
|
|
38292
39177
|
// src/tui/attachments.ts
|
|
39178
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
38293
39179
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
38294
|
-
import {
|
|
38295
|
-
import { homedir as homedir2 } from "os";
|
|
39180
|
+
import { mkdtemp, open as open4, readdir as readdir2, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
39181
|
+
import { homedir as homedir2, tmpdir } from "os";
|
|
38296
39182
|
import { basename as basename3, isAbsolute as isAbsolute2, join as join5, resolve as resolve2 } from "path";
|
|
39183
|
+
var directoryArchiveMediaType = "application/gzip";
|
|
39184
|
+
var directoryArchiveSuffix = ".tar.gz";
|
|
39185
|
+
var maxPortableFilenameBytes = 255;
|
|
39186
|
+
var attachmentSizeLimitLabel = `${fileUploadMaxBytes / (1024 * 1024)} MiB`;
|
|
38297
39187
|
async function readClipboardImage() {
|
|
38298
39188
|
if (process.platform !== "darwin")
|
|
38299
39189
|
throw new Error("Clipboard image paste is supported on macOS only.");
|
|
@@ -38319,11 +39209,13 @@ async function attachPromptPaths({
|
|
|
38319
39209
|
text,
|
|
38320
39210
|
cwd,
|
|
38321
39211
|
excludedPaths = [],
|
|
38322
|
-
|
|
39212
|
+
selectedPaths = [],
|
|
39213
|
+
reserveAndUploadFile: reserveAndUploadFile2,
|
|
39214
|
+
onUploadStart
|
|
38323
39215
|
}) {
|
|
38324
39216
|
const excluded = new Set(excludedPaths);
|
|
38325
|
-
const
|
|
38326
|
-
for (const candidate of promptFilePaths({ text, cwd })) {
|
|
39217
|
+
const pathsToUpload = [];
|
|
39218
|
+
for (const candidate of promptFilePaths({ text, cwd, selectedPaths })) {
|
|
38327
39219
|
const { filePath } = candidate;
|
|
38328
39220
|
if (excluded.has(filePath))
|
|
38329
39221
|
continue;
|
|
@@ -38335,27 +39227,168 @@ async function attachPromptPaths({
|
|
|
38335
39227
|
continue;
|
|
38336
39228
|
throw error93;
|
|
38337
39229
|
}
|
|
38338
|
-
if (!file3.isFile() && candidate.explicit)
|
|
38339
|
-
throw new Error(`Prompt attachment path is not a regular file: ${filePath}`);
|
|
38340
|
-
if (!file3.isFile())
|
|
39230
|
+
if (!file3.isFile() && !file3.isDirectory() && candidate.explicit)
|
|
39231
|
+
throw new Error(`Prompt attachment path is not a regular file or directory: ${filePath}`);
|
|
39232
|
+
if (!file3.isFile() && !file3.isDirectory())
|
|
38341
39233
|
continue;
|
|
39234
|
+
pathsToUpload.push(filePath);
|
|
39235
|
+
}
|
|
39236
|
+
if (pathsToUpload.length === 0)
|
|
39237
|
+
return [];
|
|
39238
|
+
onUploadStart?.({ filenames: pathsToUpload.map((filePath) => basename3(filePath)) });
|
|
39239
|
+
const attachments = [];
|
|
39240
|
+
for (const filePath of pathsToUpload) {
|
|
39241
|
+
const uploaded = await uploadLocalAttachment({ filePath, reserveAndUploadFile: reserveAndUploadFile2 });
|
|
38342
39242
|
attachments.push({
|
|
38343
39243
|
id: randomUUID4(),
|
|
38344
|
-
|
|
38345
|
-
filename: basename3(filePath),
|
|
38346
|
-
sourcePath: filePath
|
|
39244
|
+
...uploaded
|
|
38347
39245
|
});
|
|
38348
39246
|
}
|
|
38349
39247
|
return attachments;
|
|
38350
39248
|
}
|
|
38351
|
-
function
|
|
39249
|
+
async function uploadLocalAttachment({
|
|
39250
|
+
filePath,
|
|
39251
|
+
reserveAndUploadFile: reserveAndUploadFile2
|
|
39252
|
+
}) {
|
|
39253
|
+
const sourcePath = resolve2(filePath);
|
|
39254
|
+
const source = await stat(sourcePath);
|
|
39255
|
+
if (source.isFile()) {
|
|
39256
|
+
if (source.size > fileUploadMaxBytes)
|
|
39257
|
+
throw oversizedFileAttachmentError(sourcePath);
|
|
39258
|
+
return {
|
|
39259
|
+
fileId: await reserveAndUploadFile2({ filePath: sourcePath }),
|
|
39260
|
+
filename: basename3(sourcePath),
|
|
39261
|
+
sourcePath
|
|
39262
|
+
};
|
|
39263
|
+
}
|
|
39264
|
+
if (!source.isDirectory())
|
|
39265
|
+
throw new Error(`Attachment path is not a regular file or directory: ${sourcePath}`);
|
|
39266
|
+
const archive = await createDirectoryArchive(sourcePath);
|
|
39267
|
+
try {
|
|
39268
|
+
return {
|
|
39269
|
+
fileId: await reserveAndUploadFile2({ filePath: archive.path, mediaType: directoryArchiveMediaType }),
|
|
39270
|
+
filename: archive.filename,
|
|
39271
|
+
sourcePath
|
|
39272
|
+
};
|
|
39273
|
+
} finally {
|
|
39274
|
+
await rm(archive.temporaryDirectory, { recursive: true, force: true });
|
|
39275
|
+
}
|
|
39276
|
+
}
|
|
39277
|
+
async function createDirectoryArchive(directoryPath) {
|
|
39278
|
+
const rootName = basename3(directoryPath) || "directory";
|
|
39279
|
+
const files = Object.create(null);
|
|
39280
|
+
await collectDirectoryFiles({
|
|
39281
|
+
directoryPath,
|
|
39282
|
+
archivePath: rootName,
|
|
39283
|
+
attachmentDirectoryPath: directoryPath,
|
|
39284
|
+
collectedByteSize: 0,
|
|
39285
|
+
files
|
|
39286
|
+
});
|
|
39287
|
+
if (Object.keys(files).length === 0)
|
|
39288
|
+
throw new Error(`Directory attachment contains no regular files: ${directoryPath}`);
|
|
39289
|
+
const temporaryDirectory = await mkdtemp(join5(tmpdir(), "remy-directory-attachment-"));
|
|
39290
|
+
const filename = directoryArchiveFilename(rootName);
|
|
39291
|
+
const archivePath = join5(temporaryDirectory, filename);
|
|
39292
|
+
try {
|
|
39293
|
+
await Bun.Archive.write(archivePath, files, { compress: "gzip" });
|
|
39294
|
+
if ((await stat(archivePath)).size > fileUploadMaxBytes)
|
|
39295
|
+
throw oversizedDirectoryArchiveError(directoryPath);
|
|
39296
|
+
} catch (error93) {
|
|
39297
|
+
await rm(temporaryDirectory, { recursive: true, force: true });
|
|
39298
|
+
throw error93;
|
|
39299
|
+
}
|
|
39300
|
+
return { path: archivePath, filename, temporaryDirectory };
|
|
39301
|
+
}
|
|
39302
|
+
async function collectDirectoryFiles({
|
|
39303
|
+
directoryPath,
|
|
39304
|
+
archivePath,
|
|
39305
|
+
attachmentDirectoryPath,
|
|
39306
|
+
collectedByteSize,
|
|
39307
|
+
files
|
|
39308
|
+
}) {
|
|
39309
|
+
const entries = await readdir2(directoryPath, { withFileTypes: true });
|
|
39310
|
+
entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
39311
|
+
let byteSize = collectedByteSize;
|
|
39312
|
+
for (const entry of entries) {
|
|
39313
|
+
const entryPath = join5(directoryPath, entry.name);
|
|
39314
|
+
const entryArchivePath = `${archivePath}/${entry.name}`;
|
|
39315
|
+
if (entry.isDirectory()) {
|
|
39316
|
+
byteSize = await collectDirectoryFiles({
|
|
39317
|
+
directoryPath: entryPath,
|
|
39318
|
+
archivePath: entryArchivePath,
|
|
39319
|
+
attachmentDirectoryPath,
|
|
39320
|
+
collectedByteSize: byteSize,
|
|
39321
|
+
files
|
|
39322
|
+
});
|
|
39323
|
+
continue;
|
|
39324
|
+
}
|
|
39325
|
+
if (entry.isFile()) {
|
|
39326
|
+
const fileHandle = await open4(entryPath, "r");
|
|
39327
|
+
try {
|
|
39328
|
+
const entryStat = await fileHandle.stat();
|
|
39329
|
+
if (!entryStat.isFile())
|
|
39330
|
+
throw new Error(`Directory attachment contains a non-regular entry: ${entryPath}`);
|
|
39331
|
+
const remainingBytes = fileUploadMaxBytes - byteSize;
|
|
39332
|
+
if (entryStat.size > remainingBytes)
|
|
39333
|
+
throw oversizedDirectoryAttachmentError(attachmentDirectoryPath);
|
|
39334
|
+
const readResult = await readFileHandleWithLimit({
|
|
39335
|
+
fileHandle,
|
|
39336
|
+
initialByteSize: entryStat.size,
|
|
39337
|
+
maxBytes: remainingBytes
|
|
39338
|
+
});
|
|
39339
|
+
if (readResult.status === "limit_exceeded")
|
|
39340
|
+
throw oversizedDirectoryAttachmentError(attachmentDirectoryPath);
|
|
39341
|
+
files[entryArchivePath] = readResult.bytes;
|
|
39342
|
+
byteSize += readResult.bytes.byteLength;
|
|
39343
|
+
} finally {
|
|
39344
|
+
await fileHandle.close();
|
|
39345
|
+
}
|
|
39346
|
+
continue;
|
|
39347
|
+
}
|
|
39348
|
+
if (entry.isSymbolicLink())
|
|
39349
|
+
throw new Error(`Directory attachment contains a symbolic link: ${entryPath}`);
|
|
39350
|
+
throw new Error(`Directory attachment contains a non-regular entry: ${entryPath}`);
|
|
39351
|
+
}
|
|
39352
|
+
return byteSize;
|
|
39353
|
+
}
|
|
39354
|
+
function oversizedFileAttachmentError(filePath) {
|
|
39355
|
+
return new Error(`Attachment file ${JSON.stringify(filePath)} is larger than ${attachmentSizeLimitLabel}. Choose a file no larger than ${attachmentSizeLimitLabel}.`);
|
|
39356
|
+
}
|
|
39357
|
+
function oversizedDirectoryAttachmentError(directoryPath) {
|
|
39358
|
+
return new Error(`Attachment directory ${JSON.stringify(directoryPath)} contains more than ${attachmentSizeLimitLabel} of files. Choose fewer or smaller files.`);
|
|
39359
|
+
}
|
|
39360
|
+
function oversizedDirectoryArchiveError(directoryPath) {
|
|
39361
|
+
return new Error(`Attachment directory ${JSON.stringify(directoryPath)} produces an archive larger than ${attachmentSizeLimitLabel}. Choose fewer or smaller files.`);
|
|
39362
|
+
}
|
|
39363
|
+
function directoryArchiveFilename(directoryName) {
|
|
39364
|
+
const stem = truncateUtf8(directoryName, maxPortableFilenameBytes - Buffer2.byteLength(directoryArchiveSuffix)) || "directory";
|
|
39365
|
+
return `${stem}${directoryArchiveSuffix}`;
|
|
39366
|
+
}
|
|
39367
|
+
function truncateUtf8(value, maxBytes) {
|
|
39368
|
+
let result = "";
|
|
39369
|
+
let resultBytes = 0;
|
|
39370
|
+
for (const character of value) {
|
|
39371
|
+
const characterBytes = Buffer2.byteLength(character);
|
|
39372
|
+
if (resultBytes + characterBytes > maxBytes)
|
|
39373
|
+
break;
|
|
39374
|
+
result += character;
|
|
39375
|
+
resultBytes += characterBytes;
|
|
39376
|
+
}
|
|
39377
|
+
return result;
|
|
39378
|
+
}
|
|
39379
|
+
function promptFilePaths({ text, cwd, selectedPaths }) {
|
|
38352
39380
|
const paths = new Map;
|
|
39381
|
+
for (const selectedPath of selectedPaths)
|
|
39382
|
+
paths.set(isAbsolute2(selectedPath) ? selectedPath : resolve2(cwd, selectedPath), true);
|
|
38353
39383
|
for (const rawToken of text.split(/\s+/)) {
|
|
38354
39384
|
const token = rawToken.replace(/^[([{'"`]+/, "").replace(/[),.;:!?\]}"`]+$/, "");
|
|
38355
39385
|
const pathText = token.startsWith("@") ? token.slice(1) : token;
|
|
38356
39386
|
if (!pathText)
|
|
38357
39387
|
continue;
|
|
38358
39388
|
const explicit = token.startsWith("@") || pathText.startsWith("./") || pathText.startsWith("../") || isAbsolute2(pathText) || pathText.startsWith("~/");
|
|
39389
|
+
const pathShaped = explicit || pathText.includes("/") || pathText.includes("\\") || pathText.includes(".");
|
|
39390
|
+
if (!pathShaped)
|
|
39391
|
+
continue;
|
|
38359
39392
|
const expandedPath = pathText.startsWith("~/") ? join5(homedir2(), pathText.slice(2)) : pathText;
|
|
38360
39393
|
const filePath = isAbsolute2(expandedPath) ? expandedPath : resolve2(cwd, expandedPath);
|
|
38361
39394
|
paths.set(filePath, paths.get(filePath) || explicit);
|
|
@@ -38378,7 +39411,7 @@ function isPng(bytes) {
|
|
|
38378
39411
|
}
|
|
38379
39412
|
|
|
38380
39413
|
// src/tui/remy-splash.ts
|
|
38381
|
-
import { bold as bold5, BoxRenderable as BoxRenderable5, fg as
|
|
39414
|
+
import { bold as bold5, BoxRenderable as BoxRenderable5, fg as fg8, NativeImage, StyledText as StyledText7, TextRenderable as TextRenderable5, bg as bg5 } from "@opentui/core";
|
|
38382
39415
|
// src/tui/remy-mark.ts
|
|
38383
39416
|
var remyPixelFieldSource = new URL("../assets/remy-pixel-field.png", import.meta.url);
|
|
38384
39417
|
function shouldShowRemySplash({ width, height }) {
|
|
@@ -38397,7 +39430,7 @@ var compactMarkRows = 9;
|
|
|
38397
39430
|
var compactMinWidth = 48;
|
|
38398
39431
|
var compactMinHeight = 20;
|
|
38399
39432
|
var markBrightnessGain = 4.2;
|
|
38400
|
-
var remyCliVersion = "1.
|
|
39433
|
+
var remyCliVersion = "1.14.1";
|
|
38401
39434
|
async function showRemySplash({
|
|
38402
39435
|
createRenderer = createRemyRenderer,
|
|
38403
39436
|
durationMs = splashDurationMs,
|
|
@@ -38455,7 +39488,7 @@ async function showRemySplash({
|
|
|
38455
39488
|
});
|
|
38456
39489
|
const approvalCopy = new TextRenderable5(renderer, { content: "" });
|
|
38457
39490
|
const metadata = new TextRenderable5(renderer, {
|
|
38458
|
-
content: new
|
|
39491
|
+
content: new StyledText7([fg8(PALETTE.dimText)(`v${remyCliVersion} \xB7 Tela\xAE`)])
|
|
38459
39492
|
});
|
|
38460
39493
|
const field = await loadPixelFieldUntilAbort({ signal });
|
|
38461
39494
|
if (!field || signal?.aborted)
|
|
@@ -38553,32 +39586,32 @@ function createSplashController({
|
|
|
38553
39586
|
};
|
|
38554
39587
|
}
|
|
38555
39588
|
function renderBootstrapActions({ state, frame }) {
|
|
38556
|
-
return new
|
|
39589
|
+
return new StyledText7([
|
|
38557
39590
|
actionText({ label: state.authenticatedEmail ? `Authenticated as ${state.authenticatedEmail}` : "Checking authentication...", status: state.authentication, frame }),
|
|
38558
|
-
|
|
39591
|
+
fg8(PALETTE.dimText)(`
|
|
38559
39592
|
`),
|
|
38560
39593
|
actionText({ label: "Load sessions", status: state.loadingSessions, frame })
|
|
38561
39594
|
]);
|
|
38562
39595
|
}
|
|
38563
39596
|
function actionText({ label, status, frame }) {
|
|
38564
39597
|
if (status === "complete")
|
|
38565
|
-
return
|
|
39598
|
+
return fg8(PALETTE.statusCompleted)(`\u2713 ${label === "Load sessions" ? "Sessions loaded!" : label}`);
|
|
38566
39599
|
if (status === "needed")
|
|
38567
|
-
return
|
|
39600
|
+
return fg8(PALETTE.approvalQuestion)("\u26A0 Authentication needed");
|
|
38568
39601
|
if (status === "active")
|
|
38569
|
-
return
|
|
38570
|
-
return
|
|
39602
|
+
return fg8(PALETTE.progress)(`${splashSpinnerFrames[frame % splashSpinnerFrames.length]} ${label === "Load sessions" ? "Loading sessions..." : label}`);
|
|
39603
|
+
return fg8(PALETTE.dimText)(`\u25CB ${label}`);
|
|
38571
39604
|
}
|
|
38572
39605
|
function renderAuthenticationBand({ state, frame }) {
|
|
38573
39606
|
if (!state.waitingForAuthentication)
|
|
38574
|
-
return new
|
|
39607
|
+
return new StyledText7([]);
|
|
38575
39608
|
const spinner = splashSpinnerFrames[frame % splashSpinnerFrames.length];
|
|
38576
|
-
const url3 = state.verificationUrl ? [
|
|
38577
|
-
Verification URL: `), bold5(
|
|
38578
|
-
return new
|
|
38579
|
-
|
|
39609
|
+
const url3 = state.verificationUrl ? [fg8(PALETTE.dimText)(`
|
|
39610
|
+
Verification URL: `), bold5(fg8(PALETTE.humanAccent)(state.verificationUrl))] : [];
|
|
39611
|
+
return new StyledText7([
|
|
39612
|
+
fg8(PALETTE.bodyText)("Continue authentication in your browser."),
|
|
38580
39613
|
...url3,
|
|
38581
|
-
|
|
39614
|
+
fg8(PALETTE.dimText)(`
|
|
38582
39615
|
${spinner} Waiting for approval \xB7 q / esc cancel`)
|
|
38583
39616
|
]);
|
|
38584
39617
|
}
|
|
@@ -38653,7 +39686,7 @@ function renderMark(field, frame, rows = markRows) {
|
|
|
38653
39686
|
const flush = () => {
|
|
38654
39687
|
if (runLength === 0)
|
|
38655
39688
|
return;
|
|
38656
|
-
chunks.push(
|
|
39689
|
+
chunks.push(fg8(grey(runTop))(bg5(grey(runBottom))(upperHalfBlock.repeat(runLength))));
|
|
38657
39690
|
runLength = 0;
|
|
38658
39691
|
};
|
|
38659
39692
|
for (let column = 0;column < columns; column++) {
|
|
@@ -38670,10 +39703,10 @@ function renderMark(field, frame, rows = markRows) {
|
|
|
38670
39703
|
}
|
|
38671
39704
|
flush();
|
|
38672
39705
|
if (row < rows - 1)
|
|
38673
|
-
chunks.push(
|
|
39706
|
+
chunks.push(fg8("#000000")(`
|
|
38674
39707
|
`));
|
|
38675
39708
|
}
|
|
38676
|
-
return new
|
|
39709
|
+
return new StyledText7(chunks);
|
|
38677
39710
|
}
|
|
38678
39711
|
function shimmerBrightness(field, column, sampleRow, columns, sampleRows, center) {
|
|
38679
39712
|
const x0 = Math.floor(column / columns * field.width);
|
|
@@ -38693,8 +39726,8 @@ function grey(value) {
|
|
|
38693
39726
|
return `#${hex5}${hex5}${hex5}`;
|
|
38694
39727
|
}
|
|
38695
39728
|
function renderSplashTitle() {
|
|
38696
|
-
return new
|
|
38697
|
-
bold5(
|
|
39729
|
+
return new StyledText7([
|
|
39730
|
+
bold5(fg8(PALETTE.bodyText)("Remy CLI"))
|
|
38698
39731
|
]);
|
|
38699
39732
|
}
|
|
38700
39733
|
|
|
@@ -39029,6 +40062,9 @@ function createRepositoryLister({ client }) {
|
|
|
39029
40062
|
function createRepositorySuggester({ client }) {
|
|
39030
40063
|
return async (input) => await suggestRemoteRepositories({ client, input });
|
|
39031
40064
|
}
|
|
40065
|
+
function createBranchSuggester({ client }) {
|
|
40066
|
+
return async (input) => await suggestRemoteBranches({ client, input });
|
|
40067
|
+
}
|
|
39032
40068
|
function parseOptionalStringFlag(value, name) {
|
|
39033
40069
|
if (value === undefined)
|
|
39034
40070
|
return;
|
|
@@ -39172,48 +40208,98 @@ async function dashboard({
|
|
|
39172
40208
|
const openDashboardTui = dependencies.openDashboardTui ?? createDashboardTui;
|
|
39173
40209
|
const pages = [initialSessions];
|
|
39174
40210
|
let pageIndex = 0;
|
|
39175
|
-
let
|
|
40211
|
+
let filters = { statuses: [], creators: [] };
|
|
40212
|
+
let authors;
|
|
40213
|
+
let activeDashboardGeneration = 0;
|
|
39176
40214
|
function holdTransitionScreen() {
|
|
39177
40215
|
showRemyTransitionScreen({ write: (chunk) => dependencies.output.writeStdout(chunk) });
|
|
39178
40216
|
}
|
|
39179
|
-
|
|
39180
|
-
|
|
39181
|
-
|
|
39182
|
-
|
|
39183
|
-
|
|
39184
|
-
|
|
39185
|
-
|
|
40217
|
+
function createDashboardPageLoader({ generation }) {
|
|
40218
|
+
const loadingPages = new Map;
|
|
40219
|
+
let pageLoadTail = Promise.resolve();
|
|
40220
|
+
return async (input) => {
|
|
40221
|
+
const requestKey = "filters" in input ? `filters:${JSON.stringify(input.filters)}` : input.target;
|
|
40222
|
+
const matchingPageLoad = loadingPages.get(requestKey);
|
|
40223
|
+
if (matchingPageLoad)
|
|
40224
|
+
return await matchingPageLoad;
|
|
40225
|
+
const pendingPageLoad = pageLoadTail;
|
|
40226
|
+
const pageLoad = pendingPageLoad.then(async () => {
|
|
40227
|
+
if (generation !== activeDashboardGeneration)
|
|
40228
|
+
return { ...pages[pageIndex], canGoPrevious: pageIndex > 0 };
|
|
40229
|
+
if ("filters" in input) {
|
|
40230
|
+
const nextFilters = input.filters;
|
|
40231
|
+
const first = toDashboardPage(await operations.listSessions({
|
|
40232
|
+
client: operations.client,
|
|
40233
|
+
limit: 20,
|
|
40234
|
+
...nextFilters.statuses.length > 0 ? { statuses: nextFilters.statuses } : {},
|
|
40235
|
+
...nextFilters.creators.length > 0 ? { creatorUserIds: nextFilters.creators.map((creator) => creator.id) } : {},
|
|
40236
|
+
signal: input.signal
|
|
40237
|
+
}));
|
|
40238
|
+
if (generation !== activeDashboardGeneration)
|
|
40239
|
+
return first;
|
|
40240
|
+
filters = nextFilters;
|
|
40241
|
+
pages.splice(0, pages.length, first);
|
|
40242
|
+
pageIndex = 0;
|
|
40243
|
+
return first;
|
|
40244
|
+
}
|
|
40245
|
+
const { target, signal } = input;
|
|
40246
|
+
if (target === "current") {
|
|
40247
|
+
const after = pageIndex > 0 ? pages[pageIndex - 1]?.nextCursor ?? undefined : undefined;
|
|
40248
|
+
const refreshed = toDashboardPage(await operations.listSessions({
|
|
40249
|
+
client: operations.client,
|
|
40250
|
+
limit: 20,
|
|
40251
|
+
after,
|
|
40252
|
+
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
40253
|
+
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
40254
|
+
signal
|
|
40255
|
+
}));
|
|
40256
|
+
if (generation !== activeDashboardGeneration)
|
|
40257
|
+
return refreshed;
|
|
40258
|
+
pages.splice(pageIndex, pages.length - pageIndex, refreshed);
|
|
40259
|
+
return { ...refreshed, canGoPrevious: pageIndex > 0 };
|
|
40260
|
+
}
|
|
40261
|
+
if (target === "previous") {
|
|
40262
|
+
pageIndex = Math.max(0, pageIndex - 1);
|
|
40263
|
+
return { ...pages[pageIndex], canGoPrevious: pageIndex > 0 };
|
|
40264
|
+
}
|
|
40265
|
+
const current = pages[pageIndex];
|
|
40266
|
+
if (!current.hasMore || !current.nextCursor)
|
|
40267
|
+
return { ...current, canGoPrevious: pageIndex > 0 };
|
|
40268
|
+
const next = toDashboardPage(await operations.listSessions({
|
|
39186
40269
|
client: operations.client,
|
|
39187
40270
|
limit: 20,
|
|
39188
|
-
after,
|
|
40271
|
+
after: current.nextCursor,
|
|
40272
|
+
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
40273
|
+
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
39189
40274
|
signal
|
|
39190
40275
|
}));
|
|
39191
|
-
|
|
39192
|
-
|
|
39193
|
-
|
|
39194
|
-
|
|
39195
|
-
pageIndex
|
|
39196
|
-
return { ...
|
|
40276
|
+
if (generation !== activeDashboardGeneration)
|
|
40277
|
+
return next;
|
|
40278
|
+
pages.splice(pageIndex + 1);
|
|
40279
|
+
pages.push(next);
|
|
40280
|
+
pageIndex += 1;
|
|
40281
|
+
return { ...next, canGoPrevious: pageIndex > 0 };
|
|
40282
|
+
});
|
|
40283
|
+
loadingPages.set(requestKey, pageLoad);
|
|
40284
|
+
pageLoadTail = pageLoad.then(() => {
|
|
40285
|
+
return;
|
|
40286
|
+
}, () => {
|
|
40287
|
+
return;
|
|
40288
|
+
});
|
|
40289
|
+
try {
|
|
40290
|
+
return await pageLoad;
|
|
40291
|
+
} finally {
|
|
40292
|
+
if (loadingPages.get(requestKey) === pageLoad)
|
|
40293
|
+
loadingPages.delete(requestKey);
|
|
39197
40294
|
}
|
|
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
|
-
}
|
|
40295
|
+
};
|
|
40296
|
+
}
|
|
40297
|
+
async function loadDashboardAuthors() {
|
|
40298
|
+
if (authors)
|
|
40299
|
+
return authors;
|
|
40300
|
+
const loadedAuthors = await collectAllUsers({ listUsers: operations.listUsers, signal: dependencies.abortSignal });
|
|
40301
|
+
authors = loadedAuthors;
|
|
40302
|
+
return loadedAuthors;
|
|
39217
40303
|
}
|
|
39218
40304
|
let firstOpen = true;
|
|
39219
40305
|
holdTransitionScreen();
|
|
@@ -39222,17 +40308,30 @@ async function dashboard({
|
|
|
39222
40308
|
pages.splice(0, pages.length, toDashboardPage(await operations.listSessions({
|
|
39223
40309
|
client: operations.client,
|
|
39224
40310
|
limit: 20,
|
|
40311
|
+
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
40312
|
+
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
39225
40313
|
signal: dependencies.abortSignal
|
|
39226
40314
|
})));
|
|
39227
40315
|
pageIndex = 0;
|
|
39228
40316
|
}
|
|
39229
40317
|
firstOpen = false;
|
|
40318
|
+
const dashboardGeneration = ++activeDashboardGeneration;
|
|
40319
|
+
const loadDashboardPage = createDashboardPageLoader({ generation: dashboardGeneration });
|
|
39230
40320
|
const action = await renderRemyView({
|
|
39231
40321
|
open: async () => await openDashboardTui({
|
|
39232
40322
|
initialPage: pages[0],
|
|
40323
|
+
initialFilters: filters,
|
|
40324
|
+
loadAuthors: loadDashboardAuthors,
|
|
39233
40325
|
loadPage: loadDashboardPage
|
|
39234
40326
|
}),
|
|
39235
|
-
waitForAction: async (dashboardTui) =>
|
|
40327
|
+
waitForAction: async (dashboardTui) => {
|
|
40328
|
+
try {
|
|
40329
|
+
return await awaitWithAbort({ value: dashboardTui.waitForAction(), abortSignal: dependencies.abortSignal });
|
|
40330
|
+
} finally {
|
|
40331
|
+
if (activeDashboardGeneration === dashboardGeneration)
|
|
40332
|
+
activeDashboardGeneration += 1;
|
|
40333
|
+
}
|
|
40334
|
+
}
|
|
39236
40335
|
});
|
|
39237
40336
|
if (!action)
|
|
39238
40337
|
return 0;
|
|
@@ -39275,12 +40374,12 @@ async function prepareDashboardBootstrap({
|
|
|
39275
40374
|
return;
|
|
39276
40375
|
});
|
|
39277
40376
|
onLoadingSessions?.();
|
|
39278
|
-
const
|
|
40377
|
+
const initialSessionPage = await resolvedOperations.listSessions({
|
|
39279
40378
|
client: resolvedOperations.client,
|
|
39280
40379
|
limit: 20,
|
|
39281
40380
|
signal: dependencies.abortSignal
|
|
39282
|
-
})
|
|
39283
|
-
return { operations: resolvedOperations, repositories, initialSessions };
|
|
40381
|
+
});
|
|
40382
|
+
return { operations: resolvedOperations, repositories, initialSessions: toDashboardPage(initialSessionPage) };
|
|
39284
40383
|
}
|
|
39285
40384
|
async function prepareDashboardWithStartupSplash({
|
|
39286
40385
|
dependencies,
|
|
@@ -39471,11 +40570,15 @@ async function createNewSession({
|
|
|
39471
40570
|
throw new Error("All selected repositories must belong to the same GitHub installation.");
|
|
39472
40571
|
const fileIds = [];
|
|
39473
40572
|
for (const attachment of command.attachments) {
|
|
39474
|
-
|
|
39475
|
-
client: operations.client,
|
|
40573
|
+
const uploaded = await uploadLocalAttachment({
|
|
39476
40574
|
filePath: attachment,
|
|
39477
|
-
|
|
39478
|
-
|
|
40575
|
+
reserveAndUploadFile: async (input) => await operations.reserveAndUploadFile({
|
|
40576
|
+
...input,
|
|
40577
|
+
client: operations.client,
|
|
40578
|
+
idempotencyKey: randomUUID5()
|
|
40579
|
+
})
|
|
40580
|
+
});
|
|
40581
|
+
fileIds.push(uploaded.fileId);
|
|
39479
40582
|
}
|
|
39480
40583
|
const created = await operations.createCodexSession({
|
|
39481
40584
|
client: operations.client,
|
|
@@ -39549,6 +40652,7 @@ async function createFromTuiDraft({
|
|
|
39549
40652
|
input: {
|
|
39550
40653
|
installationId: draft.githubInstallationId,
|
|
39551
40654
|
repositoryIds: draft.repositories.map((repository) => repository.id),
|
|
40655
|
+
repositoryBranchOverrides: draft.repositoryBranchOverrides ?? [],
|
|
39552
40656
|
prompt: draft.prompt,
|
|
39553
40657
|
fileIds: draft.attachments.map((attachment) => attachment.fileId),
|
|
39554
40658
|
model: draft.model,
|
|
@@ -39571,10 +40675,11 @@ async function openTuiNewSessionWizard({
|
|
|
39571
40675
|
repositories,
|
|
39572
40676
|
reloadRepositories,
|
|
39573
40677
|
suggestRepositories: operations.suggestRepositories,
|
|
40678
|
+
suggestBranches: operations.suggestBranches,
|
|
39574
40679
|
initialDraft,
|
|
39575
40680
|
initialError,
|
|
39576
40681
|
readClipboardImage,
|
|
39577
|
-
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory:
|
|
40682
|
+
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory: tmpdir2() }),
|
|
39578
40683
|
attachPromptPaths: async (input) => await uploadPromptPaths({ operations, ...input })
|
|
39579
40684
|
}),
|
|
39580
40685
|
waitForAction: async (wizard) => await awaitWithAbort({ value: wizard.waitForDraft(), abortSignal: dependencies.abortSignal })
|
|
@@ -39583,15 +40688,20 @@ async function openTuiNewSessionWizard({
|
|
|
39583
40688
|
async function uploadPromptPaths({
|
|
39584
40689
|
operations,
|
|
39585
40690
|
text,
|
|
39586
|
-
excludedPaths
|
|
40691
|
+
excludedPaths,
|
|
40692
|
+
selectedPaths,
|
|
40693
|
+
onUploadStart
|
|
39587
40694
|
}) {
|
|
39588
40695
|
return await attachPromptPaths({
|
|
39589
40696
|
text,
|
|
39590
40697
|
excludedPaths,
|
|
40698
|
+
selectedPaths,
|
|
40699
|
+
onUploadStart,
|
|
39591
40700
|
cwd: process.cwd(),
|
|
39592
|
-
reserveAndUploadFile: async ({ filePath }) => await operations.reserveAndUploadFile({
|
|
40701
|
+
reserveAndUploadFile: async ({ filePath, mediaType }) => await operations.reserveAndUploadFile({
|
|
39593
40702
|
client: operations.client,
|
|
39594
40703
|
filePath,
|
|
40704
|
+
...mediaType ? { mediaType } : {},
|
|
39595
40705
|
idempotencyKey: randomUUID5()
|
|
39596
40706
|
})
|
|
39597
40707
|
});
|
|
@@ -39649,7 +40759,7 @@ async function runAttachedSession({
|
|
|
39649
40759
|
...activeMessageId ? { activeMessageId } : {},
|
|
39650
40760
|
environment: dependencies.environment,
|
|
39651
40761
|
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 }),
|
|
40762
|
+
listSessionEvents: async ({ sessionId: id, limit, after, signal }) => await operations.listSessionEvents({ client: operations.client, sessionId: id, limit, after, signal }),
|
|
39653
40763
|
openEventStream: ({ sessionId: id, lastRetainedEventId, signal, onSynchronized }) => operations.streamSessionEvents({ client: operations.client, sessionId: id, lastRetainedEventId, signal, onSynchronized })
|
|
39654
40764
|
});
|
|
39655
40765
|
const interactive = !noTui && !json3 && isInteractiveTerminal(dependencies);
|
|
@@ -39688,8 +40798,14 @@ async function runAttachedSession({
|
|
|
39688
40798
|
});
|
|
39689
40799
|
}
|
|
39690
40800
|
const startPromise = controller.start(start);
|
|
39691
|
-
await Promise.race([
|
|
40801
|
+
await Promise.race([
|
|
40802
|
+
controller.waitUntilReady(),
|
|
40803
|
+
startPromise,
|
|
40804
|
+
...aborted3 ? [aborted3] : []
|
|
40805
|
+
]);
|
|
39692
40806
|
if (interactive) {
|
|
40807
|
+
if (dependencies.abortSignal?.aborted)
|
|
40808
|
+
throw dependencies.abortSignal.reason ?? new Error("interrupted");
|
|
39693
40809
|
const openSessionTui = dependencies.openSessionTui ?? createSessionTui;
|
|
39694
40810
|
let composerSnapshot;
|
|
39695
40811
|
const holdTransitionScreen = () => showRemyTransitionScreen({ write: (chunk) => dependencies.output.writeStdout(chunk) });
|
|
@@ -39726,7 +40842,7 @@ async function runAttachedSession({
|
|
|
39726
40842
|
controller.updateDetail(detail);
|
|
39727
40843
|
},
|
|
39728
40844
|
readClipboardImage,
|
|
39729
|
-
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory:
|
|
40845
|
+
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory: tmpdir2() }),
|
|
39730
40846
|
attachPromptPaths: async (input) => await uploadPromptPaths({ operations, ...input })
|
|
39731
40847
|
}),
|
|
39732
40848
|
waitForAction: async (tui) => await Promise.race([tui.waitForAction(), aborted3])
|
|
@@ -39835,7 +40951,9 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
|
|
|
39835
40951
|
return {
|
|
39836
40952
|
client: dependencies.sessionClient,
|
|
39837
40953
|
listRepositories: dependencies.listRepositories ?? (async () => await Promise.reject(new Error("Missing remote repository operation."))),
|
|
40954
|
+
listUsers: dependencies.listUsers ?? (async (input) => await listRemoteUsers({ client: dependencies.sessionClient, ...input })),
|
|
39838
40955
|
suggestRepositories: dependencies.suggestRepositories ?? (async () => await Promise.reject(new Error("Missing remote repository suggestions operation."))),
|
|
40956
|
+
suggestBranches: dependencies.suggestBranches ?? (async () => await Promise.reject(new Error("Missing remote branch suggestions operation."))),
|
|
39839
40957
|
reserveAndUploadFile: dependencies.reserveAndUploadFile ?? reserveAndUploadFile,
|
|
39840
40958
|
createCodexSession: dependencies.createCodexSession ?? createCodexSession,
|
|
39841
40959
|
appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
|
|
@@ -39853,7 +40971,9 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
|
|
|
39853
40971
|
return {
|
|
39854
40972
|
client,
|
|
39855
40973
|
listRepositories: dependencies.listRepositories ?? createRepositoryLister({ client }),
|
|
40974
|
+
listUsers: dependencies.listUsers ?? (async (input) => await listRemoteUsers({ client, ...input })),
|
|
39856
40975
|
suggestRepositories: dependencies.suggestRepositories ?? createRepositorySuggester({ client }),
|
|
40976
|
+
suggestBranches: dependencies.suggestBranches ?? createBranchSuggester({ client }),
|
|
39857
40977
|
reserveAndUploadFile: dependencies.reserveAndUploadFile ?? reserveAndUploadFile,
|
|
39858
40978
|
createCodexSession: dependencies.createCodexSession ?? createCodexSession,
|
|
39859
40979
|
appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
|
|
@@ -39895,6 +41015,20 @@ async function collectAllRepositories({
|
|
|
39895
41015
|
after = page.nextCursor;
|
|
39896
41016
|
}
|
|
39897
41017
|
}
|
|
41018
|
+
async function collectAllUsers({
|
|
41019
|
+
listUsers,
|
|
41020
|
+
signal
|
|
41021
|
+
}) {
|
|
41022
|
+
const users = [];
|
|
41023
|
+
let after;
|
|
41024
|
+
while (true) {
|
|
41025
|
+
const page = await listUsers({ limit: 100, after, signal });
|
|
41026
|
+
users.push(...page.data);
|
|
41027
|
+
if (!page.hasMore || !page.nextCursor)
|
|
41028
|
+
return users;
|
|
41029
|
+
after = page.nextCursor;
|
|
41030
|
+
}
|
|
41031
|
+
}
|
|
39898
41032
|
function resolveSessionCachePathForCommand({ dependencies, sessionId }) {
|
|
39899
41033
|
const stateBase = dependencies.environment.XDG_STATE_HOME ?? (dependencies.environment.HOME ? join6(dependencies.environment.HOME, ".local/state") : undefined);
|
|
39900
41034
|
if (!stateBase)
|
|
@@ -40021,7 +41155,7 @@ Options:
|
|
|
40021
41155
|
--installation <id> Select a GitHub installation
|
|
40022
41156
|
--model <name> Select an agent model
|
|
40023
41157
|
--reasoning-effort <low|medium|high|xhigh> Select agent reasoning effort
|
|
40024
|
-
--attach <path> Upload a file; repeat
|
|
41158
|
+
--attach <path> Upload a file or directory (20 MiB max); repeat as needed
|
|
40025
41159
|
--no-tui Do not open the interactive terminal view
|
|
40026
41160
|
--json Write session updates as JSON
|
|
40027
41161
|
--help, -h Show this help
|
|
@@ -40170,7 +41304,7 @@ async function removeCommittedTokenRecoveryArtifacts({
|
|
|
40170
41304
|
const directory = dirname6(tokenPath);
|
|
40171
41305
|
let entries;
|
|
40172
41306
|
try {
|
|
40173
|
-
entries = await
|
|
41307
|
+
entries = await readdir3(directory);
|
|
40174
41308
|
} catch (error93) {
|
|
40175
41309
|
if (isMissingFileError3(error93))
|
|
40176
41310
|
return;
|