@enruana/claude-orka 0.3.2 → 0.4.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/dist/cli.js +154 -65
- package/dist/electron/main/main.js +1792 -153
- package/dist/electron/main/main.js.map +1 -1
- package/dist/electron/preload/preload.js +19 -14
- package/dist/electron/preload/preload.js.map +1 -1
- package/dist/electron/renderer/assets/main-86v1OCiP.js +144 -0
- package/dist/electron/renderer/assets/main-BhBDXIbL.css +1 -0
- package/dist/electron/renderer/index.html +13 -0
- package/dist/src/core/ClaudeOrka.d.ts +2 -1
- package/dist/src/core/ClaudeOrka.d.ts.map +1 -1
- package/dist/src/core/ClaudeOrka.js +4 -2
- package/dist/src/core/ClaudeOrka.js.map +1 -1
- package/dist/src/core/SessionManager.d.ts +1 -1
- package/dist/src/core/SessionManager.d.ts.map +1 -1
- package/dist/src/core/SessionManager.js +100 -23
- package/dist/src/core/SessionManager.js.map +1 -1
- package/dist/src/models/Fork.d.ts +3 -1
- package/dist/src/models/Fork.d.ts.map +1 -1
- package/dist/src/models/Summary.d.ts +1 -1
- package/dist/src/models/Summary.d.ts.map +1 -1
- package/dist/src/utils/logger.d.ts +3 -0
- package/dist/src/utils/logger.d.ts.map +1 -1
- package/dist/src/utils/logger.js +25 -0
- package/dist/src/utils/logger.js.map +1 -1
- package/package.json +4 -3
package/dist/cli.js
CHANGED
|
@@ -13,36 +13,61 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
13
13
|
import { dirname as dirname2, join } from "path";
|
|
14
14
|
|
|
15
15
|
// src/core/StateManager.ts
|
|
16
|
-
import
|
|
17
|
-
import
|
|
16
|
+
import path3 from "path";
|
|
17
|
+
import fs3 from "fs-extra";
|
|
18
18
|
|
|
19
19
|
// src/utils/tmux.ts
|
|
20
20
|
import execa from "execa";
|
|
21
21
|
|
|
22
22
|
// src/utils/logger.ts
|
|
23
|
+
import fs from "fs-extra";
|
|
24
|
+
import path from "path";
|
|
23
25
|
var Logger = class {
|
|
24
26
|
level = 1 /* INFO */;
|
|
27
|
+
logFilePath = null;
|
|
25
28
|
setLevel(level) {
|
|
26
29
|
this.level = level;
|
|
27
30
|
}
|
|
31
|
+
setLogFile(projectPath) {
|
|
32
|
+
const logDir = path.join(projectPath, ".claude-orka");
|
|
33
|
+
fs.ensureDirSync(logDir);
|
|
34
|
+
this.logFilePath = path.join(logDir, "orka.log");
|
|
35
|
+
}
|
|
36
|
+
writeToFile(level, ...args) {
|
|
37
|
+
if (!this.logFilePath) return;
|
|
38
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
39
|
+
const message = args.map(
|
|
40
|
+
(arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
|
|
41
|
+
).join(" ");
|
|
42
|
+
const logLine = `${timestamp} [${level}] ${message}
|
|
43
|
+
`;
|
|
44
|
+
try {
|
|
45
|
+
fs.appendFileSync(this.logFilePath, logLine);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
28
49
|
debug(...args) {
|
|
29
50
|
if (this.level <= 0 /* DEBUG */) {
|
|
30
51
|
console.log("[DEBUG]", ...args);
|
|
52
|
+
this.writeToFile("DEBUG", ...args);
|
|
31
53
|
}
|
|
32
54
|
}
|
|
33
55
|
info(...args) {
|
|
34
56
|
if (this.level <= 1 /* INFO */) {
|
|
35
57
|
console.log("[INFO]", ...args);
|
|
58
|
+
this.writeToFile("INFO", ...args);
|
|
36
59
|
}
|
|
37
60
|
}
|
|
38
61
|
warn(...args) {
|
|
39
62
|
if (this.level <= 2 /* WARN */) {
|
|
40
63
|
console.warn("[WARN]", ...args);
|
|
64
|
+
this.writeToFile("WARN", ...args);
|
|
41
65
|
}
|
|
42
66
|
}
|
|
43
67
|
error(...args) {
|
|
44
68
|
if (this.level <= 3 /* ERROR */) {
|
|
45
69
|
console.error("[ERROR]", ...args);
|
|
70
|
+
this.writeToFile("ERROR", ...args);
|
|
46
71
|
}
|
|
47
72
|
}
|
|
48
73
|
};
|
|
@@ -367,18 +392,18 @@ var TmuxCommands = class {
|
|
|
367
392
|
};
|
|
368
393
|
|
|
369
394
|
// src/utils/claude-history.ts
|
|
370
|
-
import
|
|
395
|
+
import fs2 from "fs-extra";
|
|
371
396
|
import os from "os";
|
|
372
|
-
import
|
|
373
|
-
var CLAUDE_HISTORY_PATH =
|
|
397
|
+
import path2 from "path";
|
|
398
|
+
var CLAUDE_HISTORY_PATH = path2.join(os.homedir(), ".claude", "history.jsonl");
|
|
374
399
|
async function readClaudeHistory() {
|
|
375
400
|
try {
|
|
376
|
-
const exists = await
|
|
401
|
+
const exists = await fs2.pathExists(CLAUDE_HISTORY_PATH);
|
|
377
402
|
if (!exists) {
|
|
378
403
|
logger.warn(`Claude history file not found: ${CLAUDE_HISTORY_PATH}`);
|
|
379
404
|
return [];
|
|
380
405
|
}
|
|
381
|
-
const content = await
|
|
406
|
+
const content = await fs2.readFile(CLAUDE_HISTORY_PATH, "utf-8");
|
|
382
407
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
383
408
|
const entries = [];
|
|
384
409
|
for (const line of lines) {
|
|
@@ -421,9 +446,9 @@ var StateManager = class {
|
|
|
421
446
|
orkaDir;
|
|
422
447
|
statePath;
|
|
423
448
|
constructor(projectPath) {
|
|
424
|
-
this.projectPath =
|
|
425
|
-
this.orkaDir =
|
|
426
|
-
this.statePath =
|
|
449
|
+
this.projectPath = path3.resolve(projectPath);
|
|
450
|
+
this.orkaDir = path3.join(this.projectPath, ".claude-orka");
|
|
451
|
+
this.statePath = path3.join(this.orkaDir, "state.json");
|
|
427
452
|
}
|
|
428
453
|
/**
|
|
429
454
|
* Initialize StateManager
|
|
@@ -432,7 +457,7 @@ var StateManager = class {
|
|
|
432
457
|
async initialize() {
|
|
433
458
|
logger.debug("Initializing StateManager");
|
|
434
459
|
await this.ensureDirectories();
|
|
435
|
-
if (!await
|
|
460
|
+
if (!await fs3.pathExists(this.statePath)) {
|
|
436
461
|
logger.info("Creating initial state.json");
|
|
437
462
|
const initialState = {
|
|
438
463
|
version: "1.0.0",
|
|
@@ -448,7 +473,7 @@ var StateManager = class {
|
|
|
448
473
|
* Create directory structure
|
|
449
474
|
*/
|
|
450
475
|
async ensureDirectories() {
|
|
451
|
-
await
|
|
476
|
+
await fs3.ensureDir(this.orkaDir);
|
|
452
477
|
logger.debug("Directories ensured");
|
|
453
478
|
}
|
|
454
479
|
/**
|
|
@@ -456,7 +481,7 @@ var StateManager = class {
|
|
|
456
481
|
*/
|
|
457
482
|
async read() {
|
|
458
483
|
try {
|
|
459
|
-
const content = await
|
|
484
|
+
const content = await fs3.readFile(this.statePath, "utf-8");
|
|
460
485
|
return JSON.parse(content);
|
|
461
486
|
} catch (error) {
|
|
462
487
|
logger.error("Failed to read state:", error);
|
|
@@ -469,7 +494,7 @@ var StateManager = class {
|
|
|
469
494
|
async save(state) {
|
|
470
495
|
try {
|
|
471
496
|
state.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
472
|
-
await
|
|
497
|
+
await fs3.writeFile(this.statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
473
498
|
logger.debug("State saved");
|
|
474
499
|
} catch (error) {
|
|
475
500
|
logger.error("Failed to save state:", error);
|
|
@@ -685,8 +710,8 @@ var StateManager = class {
|
|
|
685
710
|
*/
|
|
686
711
|
async saveContext(type, id, content) {
|
|
687
712
|
const contextPath = type === "session" ? this.getSessionContextPath(id) : this.getForkContextPath(id);
|
|
688
|
-
const fullPath =
|
|
689
|
-
await
|
|
713
|
+
const fullPath = path3.join(this.projectPath, contextPath);
|
|
714
|
+
await fs3.writeFile(fullPath, content, "utf-8");
|
|
690
715
|
logger.info(`Context saved: ${contextPath}`);
|
|
691
716
|
return contextPath;
|
|
692
717
|
}
|
|
@@ -694,11 +719,11 @@ var StateManager = class {
|
|
|
694
719
|
* Leer un contexto desde archivo
|
|
695
720
|
*/
|
|
696
721
|
async readContext(contextPath) {
|
|
697
|
-
const fullPath =
|
|
698
|
-
if (!await
|
|
722
|
+
const fullPath = path3.join(this.projectPath, contextPath);
|
|
723
|
+
if (!await fs3.pathExists(fullPath)) {
|
|
699
724
|
throw new Error(`Context file not found: ${contextPath}`);
|
|
700
725
|
}
|
|
701
|
-
return await
|
|
726
|
+
return await fs3.readFile(fullPath, "utf-8");
|
|
702
727
|
}
|
|
703
728
|
// --- HELPERS ---
|
|
704
729
|
/**
|
|
@@ -723,10 +748,10 @@ var StateManager = class {
|
|
|
723
748
|
|
|
724
749
|
// src/core/SessionManager.ts
|
|
725
750
|
import { v4 as uuidv4 } from "uuid";
|
|
726
|
-
import
|
|
751
|
+
import path4 from "path";
|
|
727
752
|
import { fileURLToPath } from "url";
|
|
728
753
|
import { dirname } from "path";
|
|
729
|
-
import
|
|
754
|
+
import fs4 from "fs-extra";
|
|
730
755
|
import { spawn } from "child_process";
|
|
731
756
|
import { createRequire } from "module";
|
|
732
757
|
var require2 = createRequire(import.meta.url);
|
|
@@ -807,14 +832,24 @@ var SessionManager = class {
|
|
|
807
832
|
}
|
|
808
833
|
logger.info(`Resuming session: ${session.name}`);
|
|
809
834
|
const tmuxSessionId = `orka-${sessionId}`;
|
|
810
|
-
|
|
811
|
-
|
|
835
|
+
const tmuxExists = await TmuxCommands.sessionExists(tmuxSessionId);
|
|
836
|
+
if (tmuxExists) {
|
|
837
|
+
logger.info(`Tmux session exists, reconnecting...`);
|
|
812
838
|
if (openTerminal) {
|
|
813
839
|
await TmuxCommands.openTerminalWindow(tmuxSessionId);
|
|
814
840
|
await this.launchUI(sessionId);
|
|
815
841
|
}
|
|
842
|
+
if (session.status === "saved") {
|
|
843
|
+
const paneId2 = await TmuxCommands.getMainPaneId(tmuxSessionId);
|
|
844
|
+
session.main.tmuxPaneId = paneId2;
|
|
845
|
+
session.main.status = "active";
|
|
846
|
+
session.status = "active";
|
|
847
|
+
session.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
848
|
+
await this.stateManager.replaceSession(session);
|
|
849
|
+
}
|
|
816
850
|
return session;
|
|
817
851
|
}
|
|
852
|
+
logger.info(`Tmux session not found, recovering from Claude session...`);
|
|
818
853
|
await TmuxCommands.createSession(tmuxSessionId, this.projectPath);
|
|
819
854
|
if (openTerminal) {
|
|
820
855
|
await TmuxCommands.openTerminalWindow(tmuxSessionId);
|
|
@@ -901,14 +936,25 @@ var SessionManager = class {
|
|
|
901
936
|
/**
|
|
902
937
|
* Crear un fork (rama de conversación)
|
|
903
938
|
*/
|
|
904
|
-
async createFork(sessionId, name, vertical = false) {
|
|
939
|
+
async createFork(sessionId, name, parentId = "main", vertical = false) {
|
|
905
940
|
const session = await this.getSession(sessionId);
|
|
906
941
|
if (!session) {
|
|
907
942
|
throw new Error(`Session ${sessionId} not found`);
|
|
908
943
|
}
|
|
909
944
|
const forkId = uuidv4();
|
|
910
945
|
const forkName = name || `Fork-${session.forks.length + 1}`;
|
|
911
|
-
logger.info(`Creating fork: ${forkName} in session ${session.name}`);
|
|
946
|
+
logger.info(`Creating fork: ${forkName} from parent ${parentId} in session ${session.name}`);
|
|
947
|
+
let parentClaudeSessionId;
|
|
948
|
+
if (parentId === "main") {
|
|
949
|
+
parentClaudeSessionId = session.main.claudeSessionId;
|
|
950
|
+
} else {
|
|
951
|
+
const parentFork = session.forks.find((f) => f.id === parentId);
|
|
952
|
+
if (!parentFork) {
|
|
953
|
+
throw new Error(`Parent fork ${parentId} not found`);
|
|
954
|
+
}
|
|
955
|
+
parentClaudeSessionId = parentFork.claudeSessionId;
|
|
956
|
+
}
|
|
957
|
+
logger.debug(`Parent Claude session ID: ${parentClaudeSessionId}`);
|
|
912
958
|
await TmuxCommands.splitPane(session.tmuxSessionId, vertical);
|
|
913
959
|
await sleep(1e3);
|
|
914
960
|
const allPanes = await TmuxCommands.listPanes(session.tmuxSessionId);
|
|
@@ -918,7 +964,7 @@ var SessionManager = class {
|
|
|
918
964
|
logger.debug(`Existing sessions before fork: ${existingIds.size}`);
|
|
919
965
|
await this.initializeClaude(forkPaneId, {
|
|
920
966
|
type: "fork",
|
|
921
|
-
parentSessionId:
|
|
967
|
+
parentSessionId: parentClaudeSessionId,
|
|
922
968
|
forkName
|
|
923
969
|
});
|
|
924
970
|
logger.info("Detecting fork session ID from history...");
|
|
@@ -932,6 +978,7 @@ var SessionManager = class {
|
|
|
932
978
|
const fork = {
|
|
933
979
|
id: forkId,
|
|
934
980
|
name: forkName,
|
|
981
|
+
parentId,
|
|
935
982
|
claudeSessionId: detectedForkId,
|
|
936
983
|
// ✅ ID real detectado
|
|
937
984
|
tmuxPaneId: forkPaneId,
|
|
@@ -957,6 +1004,17 @@ var SessionManager = class {
|
|
|
957
1004
|
throw new Error(`Fork ${forkId} not found`);
|
|
958
1005
|
}
|
|
959
1006
|
logger.info(`Resuming fork: ${fork.name}`);
|
|
1007
|
+
if (fork.status === "active" && fork.tmuxPaneId) {
|
|
1008
|
+
try {
|
|
1009
|
+
const allPanes2 = await TmuxCommands.listPanes(session.tmuxSessionId);
|
|
1010
|
+
if (allPanes2.includes(fork.tmuxPaneId)) {
|
|
1011
|
+
logger.info(`Fork pane already exists, no need to resume`);
|
|
1012
|
+
return fork;
|
|
1013
|
+
}
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
logger.warn(`Fork was marked active but pane not found, recreating...`);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
960
1018
|
await TmuxCommands.splitPane(session.tmuxSessionId, false);
|
|
961
1019
|
await sleep(1e3);
|
|
962
1020
|
const allPanes = await TmuxCommands.listPanes(session.tmuxSessionId);
|
|
@@ -989,7 +1047,7 @@ var SessionManager = class {
|
|
|
989
1047
|
if (fork.tmuxPaneId) {
|
|
990
1048
|
await TmuxCommands.killPane(fork.tmuxPaneId);
|
|
991
1049
|
}
|
|
992
|
-
fork.status = "
|
|
1050
|
+
fork.status = "closed";
|
|
993
1051
|
fork.tmuxPaneId = void 0;
|
|
994
1052
|
session.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
995
1053
|
await this.stateManager.replaceSession(session);
|
|
@@ -1075,14 +1133,15 @@ var SessionManager = class {
|
|
|
1075
1133
|
throw new Error(`Fork ${forkId} not found`);
|
|
1076
1134
|
}
|
|
1077
1135
|
logger.info(`Generating export for fork: ${fork.name}`);
|
|
1078
|
-
const exportsDir =
|
|
1079
|
-
await
|
|
1136
|
+
const exportsDir = path4.join(this.projectPath, ".claude-orka", "exports");
|
|
1137
|
+
await fs4.ensureDir(exportsDir);
|
|
1080
1138
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1081
1139
|
const exportName = `fork-${fork.name}-${timestamp}.md`;
|
|
1082
1140
|
const relativeExportPath = `.claude-orka/exports/${exportName}`;
|
|
1141
|
+
const absoluteExportPath = path4.join(this.projectPath, relativeExportPath);
|
|
1083
1142
|
const prompt = `
|
|
1084
1143
|
Please generate a complete summary of this fork conversation "${fork.name}" and save it to the file:
|
|
1085
|
-
\`${
|
|
1144
|
+
\`${absoluteExportPath}\`
|
|
1086
1145
|
|
|
1087
1146
|
The summary should include:
|
|
1088
1147
|
|
|
@@ -1113,7 +1172,10 @@ Write the summary in Markdown format and save it to the specified file.
|
|
|
1113
1172
|
await TmuxCommands.sendEnter(fork.tmuxPaneId);
|
|
1114
1173
|
fork.contextPath = relativeExportPath;
|
|
1115
1174
|
await this.stateManager.replaceSession(session);
|
|
1116
|
-
logger.info(`Export generation requested
|
|
1175
|
+
logger.info(`Export generation requested`);
|
|
1176
|
+
logger.info(` Filename: ${exportName}`);
|
|
1177
|
+
logger.info(` Relative path (saved in state): ${relativeExportPath}`);
|
|
1178
|
+
logger.info(` Absolute path (sent to Claude): ${absoluteExportPath}`);
|
|
1117
1179
|
logger.warn("IMPORTANT: Wait for Claude to complete before calling merge()");
|
|
1118
1180
|
return relativeExportPath;
|
|
1119
1181
|
}
|
|
@@ -1135,27 +1197,52 @@ Write the summary in Markdown format and save it to the specified file.
|
|
|
1135
1197
|
"Fork does not have an exported context. Call generateForkExport() first."
|
|
1136
1198
|
);
|
|
1137
1199
|
}
|
|
1138
|
-
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1200
|
+
const parentId = fork.parentId;
|
|
1201
|
+
const parentName = parentId === "main" ? "MAIN" : session.forks.find((f) => f.id === parentId)?.name || parentId;
|
|
1202
|
+
logger.info(`Merging fork ${fork.name} to parent ${parentName}`);
|
|
1203
|
+
let parentTmuxPaneId;
|
|
1204
|
+
if (parentId === "main") {
|
|
1205
|
+
parentTmuxPaneId = session.main.tmuxPaneId;
|
|
1206
|
+
} else {
|
|
1207
|
+
const parentFork = session.forks.find((f) => f.id === parentId);
|
|
1208
|
+
if (!parentFork) {
|
|
1209
|
+
throw new Error(`Parent fork ${parentId} not found`);
|
|
1210
|
+
}
|
|
1211
|
+
parentTmuxPaneId = parentFork.tmuxPaneId;
|
|
1212
|
+
}
|
|
1213
|
+
if (!parentTmuxPaneId) {
|
|
1214
|
+
throw new Error(`Parent ${parentName} is not active. Cannot send merge command.`);
|
|
1215
|
+
}
|
|
1216
|
+
let contextPath = fork.contextPath;
|
|
1217
|
+
let fullPath = path4.join(this.projectPath, contextPath);
|
|
1218
|
+
let exists = await fs4.pathExists(fullPath);
|
|
1219
|
+
if (!exists) {
|
|
1220
|
+
logger.warn(`Export file not found: ${contextPath}. Looking for most recent export...`);
|
|
1221
|
+
const exportsDir = path4.join(this.projectPath, ".claude-orka", "exports");
|
|
1222
|
+
const files = await fs4.readdir(exportsDir);
|
|
1223
|
+
const forkExports = files.filter((f) => f.startsWith(`fork-${fork.name}-`) && f.endsWith(".md")).sort().reverse();
|
|
1224
|
+
if (forkExports.length > 0) {
|
|
1225
|
+
contextPath = `.claude-orka/exports/${forkExports[0]}`;
|
|
1226
|
+
fullPath = path4.join(this.projectPath, contextPath);
|
|
1227
|
+
exists = await fs4.pathExists(fullPath);
|
|
1228
|
+
logger.info(`Using most recent export: ${contextPath}`);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1141
1231
|
if (!exists) {
|
|
1142
1232
|
throw new Error(
|
|
1143
|
-
`
|
|
1233
|
+
`No export file found for fork "${fork.name}". Please run Export first and wait for Claude to complete.`
|
|
1144
1234
|
);
|
|
1145
1235
|
}
|
|
1146
1236
|
const mergePrompt = `
|
|
1147
1237
|
I have completed work on the fork "${fork.name}".
|
|
1148
|
-
Please read the file \`${
|
|
1238
|
+
Please read the file \`${contextPath}\` which contains:
|
|
1149
1239
|
1. An executive summary of the work completed
|
|
1150
1240
|
2. The complete context of the fork conversation
|
|
1151
1241
|
|
|
1152
|
-
Analyze the content and help me integrate the changes and learnings from the fork into this
|
|
1242
|
+
Analyze the content and help me integrate the changes and learnings from the fork into this conversation.
|
|
1153
1243
|
`.trim();
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
}
|
|
1157
|
-
await TmuxCommands.sendKeys(session.main.tmuxPaneId, mergePrompt);
|
|
1158
|
-
await TmuxCommands.sendEnter(session.main.tmuxPaneId);
|
|
1244
|
+
await TmuxCommands.sendKeys(parentTmuxPaneId, mergePrompt);
|
|
1245
|
+
await TmuxCommands.sendEnter(parentTmuxPaneId);
|
|
1159
1246
|
fork.status = "merged";
|
|
1160
1247
|
fork.mergedToMain = true;
|
|
1161
1248
|
fork.mergedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1217,11 +1304,11 @@ Analyze the content and help me integrate the changes and learnings from the for
|
|
|
1217
1304
|
logger.warn("Electron not available, skipping UI launch");
|
|
1218
1305
|
return;
|
|
1219
1306
|
}
|
|
1220
|
-
let mainPath =
|
|
1221
|
-
if (!
|
|
1222
|
-
mainPath =
|
|
1307
|
+
let mainPath = path4.join(__dirname, "../../electron/main/main.js");
|
|
1308
|
+
if (!fs4.existsSync(mainPath)) {
|
|
1309
|
+
mainPath = path4.join(__dirname, "../../dist/electron/main/main.js");
|
|
1223
1310
|
}
|
|
1224
|
-
if (!
|
|
1311
|
+
if (!fs4.existsSync(mainPath)) {
|
|
1225
1312
|
logger.warn(`Electron main.js not found at ${mainPath}, skipping UI launch`);
|
|
1226
1313
|
return;
|
|
1227
1314
|
}
|
|
@@ -1249,6 +1336,7 @@ var ClaudeOrka = class {
|
|
|
1249
1336
|
* @param projectPath Absolute path to the project
|
|
1250
1337
|
*/
|
|
1251
1338
|
constructor(projectPath) {
|
|
1339
|
+
logger.setLogFile(projectPath);
|
|
1252
1340
|
this.sessionManager = new SessionManager(projectPath);
|
|
1253
1341
|
}
|
|
1254
1342
|
/**
|
|
@@ -1361,11 +1449,12 @@ var ClaudeOrka = class {
|
|
|
1361
1449
|
* Create a fork (conversation branch)
|
|
1362
1450
|
* @param sessionId Session ID
|
|
1363
1451
|
* @param name Optional fork name
|
|
1452
|
+
* @param parentId Parent fork/session ID (default: 'main')
|
|
1364
1453
|
* @param vertical Whether to split vertically (default: false = horizontal)
|
|
1365
1454
|
* @returns Created fork
|
|
1366
1455
|
*/
|
|
1367
|
-
async createFork(sessionId, name, vertical) {
|
|
1368
|
-
return await this.sessionManager.createFork(sessionId, name, vertical);
|
|
1456
|
+
async createFork(sessionId, name, parentId = "main", vertical) {
|
|
1457
|
+
return await this.sessionManager.createFork(sessionId, name, parentId, vertical);
|
|
1369
1458
|
}
|
|
1370
1459
|
/**
|
|
1371
1460
|
* Close a fork
|
|
@@ -1727,8 +1816,8 @@ ${statusEmoji} ${chalk.bold(session.name)}`);
|
|
|
1727
1816
|
|
|
1728
1817
|
// src/cli/utils/errors.ts
|
|
1729
1818
|
import chalk2 from "chalk";
|
|
1730
|
-
import
|
|
1731
|
-
import
|
|
1819
|
+
import fs5 from "fs";
|
|
1820
|
+
import path5 from "path";
|
|
1732
1821
|
var CLIError = class extends Error {
|
|
1733
1822
|
constructor(message, exitCode = 1) {
|
|
1734
1823
|
super(message);
|
|
@@ -1760,8 +1849,8 @@ function validateForkId(forkId) {
|
|
|
1760
1849
|
}
|
|
1761
1850
|
}
|
|
1762
1851
|
function validateInitialized(projectPath) {
|
|
1763
|
-
const orkaDir =
|
|
1764
|
-
if (!
|
|
1852
|
+
const orkaDir = path5.join(projectPath, ".claude-orka");
|
|
1853
|
+
if (!fs5.existsSync(orkaDir)) {
|
|
1765
1854
|
throw new CLIError(
|
|
1766
1855
|
'Project not initialized. Run "orka init" first.',
|
|
1767
1856
|
2
|
|
@@ -2154,8 +2243,8 @@ function mergeCommand(program2) {
|
|
|
2154
2243
|
|
|
2155
2244
|
// src/cli/commands/doctor.ts
|
|
2156
2245
|
import execa2 from "execa";
|
|
2157
|
-
import
|
|
2158
|
-
import
|
|
2246
|
+
import fs6 from "fs-extra";
|
|
2247
|
+
import path6 from "path";
|
|
2159
2248
|
import chalk4 from "chalk";
|
|
2160
2249
|
function doctorCommand(program2) {
|
|
2161
2250
|
program2.command("doctor").description("Check system dependencies and configuration").action(async () => {
|
|
@@ -2246,11 +2335,11 @@ async function checkClaude() {
|
|
|
2246
2335
|
}
|
|
2247
2336
|
async function checkProjectInit() {
|
|
2248
2337
|
const projectPath = process.cwd();
|
|
2249
|
-
const orkaDir =
|
|
2250
|
-
const stateFile =
|
|
2338
|
+
const orkaDir = path6.join(projectPath, ".claude-orka");
|
|
2339
|
+
const stateFile = path6.join(orkaDir, "state.json");
|
|
2251
2340
|
try {
|
|
2252
|
-
const dirExists = await
|
|
2253
|
-
const stateExists = await
|
|
2341
|
+
const dirExists = await fs6.pathExists(orkaDir);
|
|
2342
|
+
const stateExists = await fs6.pathExists(stateFile);
|
|
2254
2343
|
if (dirExists && stateExists) {
|
|
2255
2344
|
return {
|
|
2256
2345
|
name: "Project initialization",
|
|
@@ -2287,9 +2376,9 @@ async function checkProjectInit() {
|
|
|
2287
2376
|
async function checkWritePermissions() {
|
|
2288
2377
|
const projectPath = process.cwd();
|
|
2289
2378
|
try {
|
|
2290
|
-
const testFile =
|
|
2291
|
-
await
|
|
2292
|
-
await
|
|
2379
|
+
const testFile = path6.join(projectPath, ".claude-orka-write-test");
|
|
2380
|
+
await fs6.writeFile(testFile, "test");
|
|
2381
|
+
await fs6.remove(testFile);
|
|
2293
2382
|
return {
|
|
2294
2383
|
name: "Write permissions",
|
|
2295
2384
|
status: "pass",
|
|
@@ -2308,11 +2397,11 @@ async function checkWritePermissions() {
|
|
|
2308
2397
|
}
|
|
2309
2398
|
async function checkClaudeDir() {
|
|
2310
2399
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
2311
|
-
const claudeDir =
|
|
2312
|
-
const historyFile =
|
|
2400
|
+
const claudeDir = path6.join(homeDir, ".claude");
|
|
2401
|
+
const historyFile = path6.join(claudeDir, "history.jsonl");
|
|
2313
2402
|
try {
|
|
2314
|
-
const dirExists = await
|
|
2315
|
-
const historyExists = await
|
|
2403
|
+
const dirExists = await fs6.pathExists(claudeDir);
|
|
2404
|
+
const historyExists = await fs6.pathExists(historyFile);
|
|
2316
2405
|
if (dirExists && historyExists) {
|
|
2317
2406
|
return {
|
|
2318
2407
|
name: "Claude directory",
|