topchester-ai 0.45.0 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs
CHANGED
|
@@ -8341,6 +8341,8 @@ const sessionMetadataSchema = z.object({
|
|
|
8341
8341
|
rootSessionId: z.string().optional(),
|
|
8342
8342
|
parentSessionId: z.string().optional(),
|
|
8343
8343
|
parentToolCallId: z.string().optional(),
|
|
8344
|
+
forkedFromSessionId: z.string().optional(),
|
|
8345
|
+
forkedFromRootSessionId: z.string().optional(),
|
|
8344
8346
|
source: z.enum(["user", "subagent"]).optional(),
|
|
8345
8347
|
agentProfileId: z.string().optional(),
|
|
8346
8348
|
title: z.string().optional(),
|
|
@@ -8553,6 +8555,45 @@ async function createChildSession(workspaceRoot, options) {
|
|
|
8553
8555
|
}));
|
|
8554
8556
|
return child;
|
|
8555
8557
|
}
|
|
8558
|
+
async function forkSession(workspaceRoot, sourceSessionIdOrLatest, options = {}) {
|
|
8559
|
+
const source = await loadSession(workspaceRoot, sourceSessionIdOrLatest);
|
|
8560
|
+
const copiedEvents = await readFile(join(source.sessionDir, "events.jsonl"), "utf8");
|
|
8561
|
+
const sessionsPath = getTopchesterSessionsPath(workspaceRoot);
|
|
8562
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8563
|
+
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
8564
|
+
const sessionId = generateSessionId();
|
|
8565
|
+
const sessionDir = join(sessionsPath, sessionId);
|
|
8566
|
+
const metadataPath = join(sessionDir, "metadata.json");
|
|
8567
|
+
const eventsPath = join(sessionDir, "events.jsonl");
|
|
8568
|
+
const metadata = {
|
|
8569
|
+
version: 1,
|
|
8570
|
+
sessionId,
|
|
8571
|
+
rootSessionId: sessionId,
|
|
8572
|
+
forkedFromSessionId: source.sessionId,
|
|
8573
|
+
forkedFromRootSessionId: source.metadata.rootSessionId,
|
|
8574
|
+
source: "user",
|
|
8575
|
+
...options.title === void 0 ? {} : { title: options.title },
|
|
8576
|
+
workspaceRoot,
|
|
8577
|
+
createdAt,
|
|
8578
|
+
updatedAt: createdAt,
|
|
8579
|
+
lastEventId: source.metadata.lastEventId
|
|
8580
|
+
};
|
|
8581
|
+
try {
|
|
8582
|
+
await mkdir(sessionDir);
|
|
8583
|
+
await writeMetadata(metadataPath, metadata);
|
|
8584
|
+
await writeFile(eventsPath, copiedEvents, { flag: "wx" });
|
|
8585
|
+
return buildHandle(sessionDir, metadata);
|
|
8586
|
+
} catch (error) {
|
|
8587
|
+
if (isFileExistsError(error)) continue;
|
|
8588
|
+
await rm(sessionDir, {
|
|
8589
|
+
recursive: true,
|
|
8590
|
+
force: true
|
|
8591
|
+
});
|
|
8592
|
+
throw error;
|
|
8593
|
+
}
|
|
8594
|
+
}
|
|
8595
|
+
throw new Error("Could not create forked session after repeated session id collisions");
|
|
8596
|
+
}
|
|
8556
8597
|
async function loadSessionForAppend(workspaceRoot, sessionId) {
|
|
8557
8598
|
const loaded = await loadSession(workspaceRoot, sessionId);
|
|
8558
8599
|
return buildHandle(loaded.sessionDir, loaded.metadata);
|
|
@@ -8758,6 +8799,9 @@ function isVisibleOnlyMessage(meta) {
|
|
|
8758
8799
|
function isFileNotFoundError(error) {
|
|
8759
8800
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
8760
8801
|
}
|
|
8802
|
+
function isFileExistsError(error) {
|
|
8803
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
8804
|
+
}
|
|
8761
8805
|
//#endregion
|
|
8762
8806
|
//#region src/tui/banner.ts
|
|
8763
8807
|
const ASCII_BANNERS = [
|
|
@@ -9045,6 +9089,10 @@ const slashCommandSuggestions = [
|
|
|
9045
9089
|
{
|
|
9046
9090
|
value: "/new",
|
|
9047
9091
|
description: "start a fresh session"
|
|
9092
|
+
},
|
|
9093
|
+
{
|
|
9094
|
+
value: "/fork",
|
|
9095
|
+
description: "fork the current session"
|
|
9048
9096
|
}
|
|
9049
9097
|
];
|
|
9050
9098
|
const slashCommands = [
|
|
@@ -9092,6 +9140,11 @@ const slashCommands = [
|
|
|
9092
9140
|
name: "new",
|
|
9093
9141
|
description: "start a fresh interactive TUI session",
|
|
9094
9142
|
execute: executeNewCommand
|
|
9143
|
+
},
|
|
9144
|
+
{
|
|
9145
|
+
name: "fork",
|
|
9146
|
+
description: "fork the current interactive TUI session",
|
|
9147
|
+
execute: executeForkCommand
|
|
9095
9148
|
}
|
|
9096
9149
|
];
|
|
9097
9150
|
function parseSlashCommand(input) {
|
|
@@ -9112,7 +9165,7 @@ async function executeSlashCommand(input, context) {
|
|
|
9112
9165
|
if (!command) {
|
|
9113
9166
|
const shortcutResult = await executeSkillShortcutCommand(parsed.name, parsed.args, context);
|
|
9114
9167
|
if (shortcutResult) return shortcutResult;
|
|
9115
|
-
return { messages: [`Unknown command: /${parsed.name}`, "Try /kb status or /
|
|
9168
|
+
return { messages: [`Unknown command: /${parsed.name}`, "Try /kb status, /new, or /fork."] };
|
|
9116
9169
|
}
|
|
9117
9170
|
return command.execute(parsed.args, context);
|
|
9118
9171
|
}
|
|
@@ -9209,6 +9262,9 @@ async function executeKbCommand(args, context) {
|
|
|
9209
9262
|
function executeNewCommand() {
|
|
9210
9263
|
return { messages: ["/new starts a fresh session in the interactive TUI."] };
|
|
9211
9264
|
}
|
|
9265
|
+
function executeForkCommand() {
|
|
9266
|
+
return { messages: ["/fork clones the current session in the interactive TUI."] };
|
|
9267
|
+
}
|
|
9212
9268
|
function executeInteractiveOnlyCommand(command) {
|
|
9213
9269
|
return () => ({ messages: [`${command} is available in the interactive TUI.`] });
|
|
9214
9270
|
}
|
|
@@ -12933,6 +12989,9 @@ function getSlashCommandActivities(command) {
|
|
|
12933
12989
|
function isNewSessionCommand(command) {
|
|
12934
12990
|
return command.trim() === "/new";
|
|
12935
12991
|
}
|
|
12992
|
+
function isForkSessionCommand(command) {
|
|
12993
|
+
return command.trim() === "/fork";
|
|
12994
|
+
}
|
|
12936
12995
|
function isConnectCommand(command) {
|
|
12937
12996
|
const name = getSlashCommandName(command);
|
|
12938
12997
|
return name === "connect" || name === "provider" || name === "providers";
|
|
@@ -13298,6 +13357,10 @@ var TopchesterTuiShell = class {
|
|
|
13298
13357
|
await this.startNewSession(app, tui);
|
|
13299
13358
|
return;
|
|
13300
13359
|
}
|
|
13360
|
+
if (isForkSessionCommand(command)) {
|
|
13361
|
+
await this.forkCurrentSession(app, tui);
|
|
13362
|
+
return;
|
|
13363
|
+
}
|
|
13301
13364
|
if (isConnectCommand(command)) {
|
|
13302
13365
|
await this.submitConnectCommand(app, tui, command);
|
|
13303
13366
|
return;
|
|
@@ -13709,6 +13772,39 @@ var TopchesterTuiShell = class {
|
|
|
13709
13772
|
tui.requestRender();
|
|
13710
13773
|
await this.checkAgent(app, tui);
|
|
13711
13774
|
}
|
|
13775
|
+
async forkCurrentSession(app, tui) {
|
|
13776
|
+
if (this.taskPlanNoticeTimer) {
|
|
13777
|
+
clearTimeout(this.taskPlanNoticeTimer);
|
|
13778
|
+
this.taskPlanNoticeTimer = void 0;
|
|
13779
|
+
}
|
|
13780
|
+
const sourceSession = this.session;
|
|
13781
|
+
if (!sourceSession) {
|
|
13782
|
+
app.addMessage(systemMessage("Fork failed: no active session."));
|
|
13783
|
+
tui.requestRender();
|
|
13784
|
+
return;
|
|
13785
|
+
}
|
|
13786
|
+
const fork = await forkSession(this.context.workspaceRoot, sourceSession.sessionId);
|
|
13787
|
+
const rehydrated = rehydrateSession((await loadSession(this.context.workspaceRoot, fork.sessionId)).events);
|
|
13788
|
+
const forkNoticeText = `Forked session from ${sourceSession.sessionId.slice(0, 8)}.`;
|
|
13789
|
+
const forkNotice = systemMessage(forkNoticeText);
|
|
13790
|
+
const resetMessages = [...rehydrated.messages, forkNotice];
|
|
13791
|
+
this.session = fork;
|
|
13792
|
+
this.sessionStartedAt = Date.now();
|
|
13793
|
+
this.pendingSkillActivations = [];
|
|
13794
|
+
try {
|
|
13795
|
+
await fork.append({
|
|
13796
|
+
kind: "message",
|
|
13797
|
+
role: "system",
|
|
13798
|
+
text: forkNoticeText
|
|
13799
|
+
});
|
|
13800
|
+
} catch (error) {
|
|
13801
|
+
resetMessages.push(systemMessage(`Session save failed: ${formatPlainError(error)}`));
|
|
13802
|
+
}
|
|
13803
|
+
app.resetForNewSession(resetMessages);
|
|
13804
|
+
app.setTaskPlan(rehydrated.taskPlan);
|
|
13805
|
+
if (rehydrated.status) app.setStatus(rehydrated.status);
|
|
13806
|
+
tui.requestRender();
|
|
13807
|
+
}
|
|
13712
13808
|
async appendStartupRuntimeEvents(session, messages, events) {
|
|
13713
13809
|
for (const event of events) {
|
|
13714
13810
|
messages.push(...renderRuntimeEvent(event));
|
|
@@ -14284,6 +14380,18 @@ function createTopchesterProgram() {
|
|
|
14284
14380
|
process.exitCode = 1;
|
|
14285
14381
|
}
|
|
14286
14382
|
});
|
|
14383
|
+
program.command("fork").description("fork a saved project session").argument("[session]", "session id to fork").option("--last", "fork the latest project session").action(async (sessionId, options) => {
|
|
14384
|
+
const context = createContextFromOptions(program);
|
|
14385
|
+
try {
|
|
14386
|
+
if (options.last && sessionId) throw new Error("Usage: topchester fork [--last] [session-id]");
|
|
14387
|
+
const source = options.last ? "latest" : sessionId;
|
|
14388
|
+
if (!source) throw new Error("topchester fork requires --last or a session id until a saved-session picker exists.");
|
|
14389
|
+
await openForkedSession(context, source);
|
|
14390
|
+
} catch (error) {
|
|
14391
|
+
console.error(formatStartupError(error));
|
|
14392
|
+
process.exitCode = 1;
|
|
14393
|
+
}
|
|
14394
|
+
});
|
|
14287
14395
|
program.command("search").description("search compiled L1 knowledge entries").argument("<query...>", "search query").option("--limit <count>", "maximum number of matches", parsePositiveInteger).option("--json", "write full JSON search result to stdout").action(async (queryParts, options) => {
|
|
14288
14396
|
await executeKbSearchCommand(program, queryParts, options);
|
|
14289
14397
|
});
|
|
@@ -14372,6 +14480,17 @@ function printStartupSummary(context) {
|
|
|
14372
14480
|
}
|
|
14373
14481
|
}
|
|
14374
14482
|
}
|
|
14483
|
+
async function openForkedSession(context, sourceSession) {
|
|
14484
|
+
const fork = await forkSession(context.workspaceRoot, sourceSession);
|
|
14485
|
+
const loaded = await loadSession(context.workspaceRoot, fork.sessionId);
|
|
14486
|
+
const session = await loadSessionForAppend(context.workspaceRoot, loaded.sessionId);
|
|
14487
|
+
const rehydrated = rehydrateSession(loaded.events);
|
|
14488
|
+
await new TopchesterTuiShell(context, void 0, {
|
|
14489
|
+
session,
|
|
14490
|
+
initialMessages: rehydrated.messages,
|
|
14491
|
+
initialTaskPlan: rehydrated.taskPlan
|
|
14492
|
+
}).render();
|
|
14493
|
+
}
|
|
14375
14494
|
function createContextFromOptions(program) {
|
|
14376
14495
|
return createAppContext(getContextOptionsFromProgram(program));
|
|
14377
14496
|
}
|
|
@@ -14434,4 +14553,4 @@ function formatDryRunSyncStatus(status) {
|
|
|
14434
14553
|
//#endregion
|
|
14435
14554
|
export { runTopchesterCli as t };
|
|
14436
14555
|
|
|
14437
|
-
//# sourceMappingURL=cli-
|
|
14556
|
+
//# sourceMappingURL=cli-BHctQstt.mjs.map
|