@enruana/claude-orka 0.4.4 → 0.4.6
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/.tmux.orka.conf +127 -0
- package/README.md +35 -0
- package/dist/cli.js +162 -62
- package/dist/electron/main/main.js +100 -48
- package/dist/src/cli/commands/doctor.d.ts.map +1 -1
- package/dist/src/cli/commands/doctor.js +24 -0
- package/dist/src/cli/commands/doctor.js.map +1 -1
- package/dist/src/cli/commands/prepare.d.ts.map +1 -1
- package/dist/src/cli/commands/prepare.js +29 -0
- package/dist/src/cli/commands/prepare.js.map +1 -1
- package/dist/src/core/SessionManager.d.ts.map +1 -1
- package/dist/src/core/SessionManager.js +22 -7
- package/dist/src/core/SessionManager.js.map +1 -1
- package/dist/src/utils/tmux.d.ts +4 -0
- package/dist/src/utils/tmux.d.ts.map +1 -1
- package/dist/src/utils/tmux.js +43 -0
- package/dist/src/utils/tmux.js.map +1 -1
- package/package.json +2 -1
package/.tmux.orka.conf
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Claude-Orka Custom tmux Theme
|
|
2
|
+
# This configuration is automatically applied to all Claude-Orka sessions
|
|
3
|
+
|
|
4
|
+
# ============================================
|
|
5
|
+
# GENERAL SETTINGS
|
|
6
|
+
# ============================================
|
|
7
|
+
|
|
8
|
+
# Enable mouse support for easier pane navigation
|
|
9
|
+
set -g mouse on
|
|
10
|
+
|
|
11
|
+
# Start windows and panes at 1 (not 0)
|
|
12
|
+
set -g base-index 1
|
|
13
|
+
setw -g pane-base-index 1
|
|
14
|
+
|
|
15
|
+
# Increase scrollback buffer
|
|
16
|
+
set -g history-limit 10000
|
|
17
|
+
|
|
18
|
+
# Reduce escape time (better for vim users)
|
|
19
|
+
set -sg escape-time 0
|
|
20
|
+
|
|
21
|
+
# Enable focus events
|
|
22
|
+
set -g focus-events on
|
|
23
|
+
|
|
24
|
+
# Enable 256 colors
|
|
25
|
+
set -g default-terminal "screen-256color"
|
|
26
|
+
|
|
27
|
+
# ============================================
|
|
28
|
+
# STATUS BAR THEME
|
|
29
|
+
# ============================================
|
|
30
|
+
|
|
31
|
+
# Status bar position
|
|
32
|
+
set -g status-position top
|
|
33
|
+
|
|
34
|
+
# Status bar update interval
|
|
35
|
+
set -g status-interval 1
|
|
36
|
+
|
|
37
|
+
# Status bar colors
|
|
38
|
+
set -g status-style bg=colour234,fg=colour137
|
|
39
|
+
|
|
40
|
+
# Status bar length
|
|
41
|
+
set -g status-left-length 50
|
|
42
|
+
set -g status-right-length 100
|
|
43
|
+
|
|
44
|
+
# Left side: Session name with Orka branding
|
|
45
|
+
set -g status-left '#[fg=colour232,bg=colour208,bold] 🎭 ORKA #[fg=colour208,bg=colour236,nobold] #[fg=colour208,bg=colour236] #S #[fg=colour236,bg=colour234,nobold] '
|
|
46
|
+
|
|
47
|
+
# Right side: Project info and time
|
|
48
|
+
set -g status-right '#[fg=colour236,bg=colour234]#[fg=colour137,bg=colour236] #{pane_current_path} #[fg=colour240,bg=colour236]#[fg=colour250,bg=colour240] %Y-%m-%d #[fg=colour245,bg=colour240]#[fg=colour232,bg=colour245,bold] %H:%M:%S '
|
|
49
|
+
|
|
50
|
+
# Window status format
|
|
51
|
+
setw -g window-status-format '#[fg=colour234,bg=colour236]#[fg=colour137,bg=colour236] #I:#W #[fg=colour236,bg=colour234]'
|
|
52
|
+
|
|
53
|
+
# Current window status format (highlighted)
|
|
54
|
+
setw -g window-status-current-format '#[fg=colour234,bg=colour208]#[fg=colour232,bg=colour208,bold] #I:#W #[fg=colour208,bg=colour234,nobold]'
|
|
55
|
+
|
|
56
|
+
# Window with activity status
|
|
57
|
+
setw -g window-status-activity-style fg=colour166,bg=colour236
|
|
58
|
+
|
|
59
|
+
# ============================================
|
|
60
|
+
# PANE BORDERS
|
|
61
|
+
# ============================================
|
|
62
|
+
|
|
63
|
+
# Pane border colors
|
|
64
|
+
set -g pane-border-style fg=colour236
|
|
65
|
+
set -g pane-active-border-style fg=colour208
|
|
66
|
+
|
|
67
|
+
# Pane border format (show pane title)
|
|
68
|
+
set -g pane-border-format '#[fg=colour208,bg=colour234] #{pane_index} #{pane_current_command} '
|
|
69
|
+
set -g pane-border-status top
|
|
70
|
+
|
|
71
|
+
# ============================================
|
|
72
|
+
# MESSAGES & COMMAND PROMPT
|
|
73
|
+
# ============================================
|
|
74
|
+
|
|
75
|
+
# Command prompt style
|
|
76
|
+
set -g message-style fg=colour232,bg=colour208,bold
|
|
77
|
+
|
|
78
|
+
# Command prompt message style
|
|
79
|
+
set -g message-command-style fg=colour232,bg=colour166
|
|
80
|
+
|
|
81
|
+
# ============================================
|
|
82
|
+
# CLOCK MODE
|
|
83
|
+
# ============================================
|
|
84
|
+
|
|
85
|
+
# Clock color (prefix + t)
|
|
86
|
+
setw -g clock-mode-colour colour208
|
|
87
|
+
|
|
88
|
+
# ============================================
|
|
89
|
+
# WINDOW MODES
|
|
90
|
+
# ============================================
|
|
91
|
+
|
|
92
|
+
# Copy mode highlighting
|
|
93
|
+
setw -g mode-style fg=colour232,bg=colour208
|
|
94
|
+
|
|
95
|
+
# ============================================
|
|
96
|
+
# KEY BINDINGS (Optional enhancements)
|
|
97
|
+
# ============================================
|
|
98
|
+
|
|
99
|
+
# Reload config easily (prefix + r)
|
|
100
|
+
bind r source-file ~/.tmux.conf \; display-message "Config reloaded!"
|
|
101
|
+
|
|
102
|
+
# Better pane splitting (keep current directory)
|
|
103
|
+
bind | split-window -h -c "#{pane_current_path}"
|
|
104
|
+
bind - split-window -v -c "#{pane_current_path}"
|
|
105
|
+
|
|
106
|
+
# Pane navigation with vim-like keys
|
|
107
|
+
bind h select-pane -L
|
|
108
|
+
bind j select-pane -D
|
|
109
|
+
bind k select-pane -U
|
|
110
|
+
bind l select-pane -R
|
|
111
|
+
|
|
112
|
+
# Resize panes with vim-like keys
|
|
113
|
+
bind -r H resize-pane -L 5
|
|
114
|
+
bind -r J resize-pane -D 5
|
|
115
|
+
bind -r K resize-pane -U 5
|
|
116
|
+
bind -r L resize-pane -R 5
|
|
117
|
+
|
|
118
|
+
# ============================================
|
|
119
|
+
# VISUAL INDICATORS
|
|
120
|
+
# ============================================
|
|
121
|
+
|
|
122
|
+
# Bell/Activity
|
|
123
|
+
set -g visual-activity off
|
|
124
|
+
set -g visual-bell off
|
|
125
|
+
set -g visual-silence off
|
|
126
|
+
setw -g monitor-activity on
|
|
127
|
+
set -g bell-action none
|
package/README.md
CHANGED
|
@@ -136,6 +136,41 @@ orka status
|
|
|
136
136
|
- **Fork management** - Create, export, merge, and close forks visually
|
|
137
137
|
- **Status indicators** - Visual distinction for active, saved, merged, and closed forks
|
|
138
138
|
|
|
139
|
+
### 🎨 Custom tmux Theme
|
|
140
|
+
|
|
141
|
+
- **Automatic branding** - Claude-Orka sessions get a custom orange theme
|
|
142
|
+
- **Enhanced status bar** - Shows session name, project path, and current time
|
|
143
|
+
- **Visual hierarchy** - Distinct colors for active/inactive panes and windows
|
|
144
|
+
- **Mouse support** - Click to select panes and windows
|
|
145
|
+
- **Vim-like navigation** - Optional h/j/k/l keys for pane movement
|
|
146
|
+
- **Persistent configuration** - Theme automatically applied to all new sessions
|
|
147
|
+
|
|
148
|
+
The custom theme makes it easy to distinguish Claude-Orka managed sessions from regular tmux sessions. The orange branding 🎭 appears in the status bar, with the session name and project information clearly visible.
|
|
149
|
+
|
|
150
|
+
**Customization:**
|
|
151
|
+
|
|
152
|
+
To modify the theme, edit `.tmux.orka.conf` in your Claude-Orka installation:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Find your global installation
|
|
156
|
+
npm root -g
|
|
157
|
+
# Edit the config
|
|
158
|
+
vim $(npm root -g)/@enruana/claude-orka/.tmux.orka.conf
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Or create your own theme file and source it manually:
|
|
162
|
+
```bash
|
|
163
|
+
# After creating a session
|
|
164
|
+
tmux source-file ~/.my-custom-theme.conf -t <session-name>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Key Features of the Theme:**
|
|
168
|
+
- **Orange highlights** (#208) for active windows and Orka branding
|
|
169
|
+
- **Top status bar** with session info
|
|
170
|
+
- **Pane borders** with titles showing current command
|
|
171
|
+
- **Enhanced readability** with high contrast colors
|
|
172
|
+
- **Powerline-style separators** for a modern look
|
|
173
|
+
|
|
139
174
|
---
|
|
140
175
|
|
|
141
176
|
## CLI Reference
|
package/dist/cli.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { readFileSync } from "fs";
|
|
6
|
-
import { fileURLToPath as
|
|
7
|
-
import { dirname as
|
|
6
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7
|
+
import { dirname as dirname3, join as join2 } from "path";
|
|
8
8
|
|
|
9
9
|
// src/core/StateManager.ts
|
|
10
|
-
import
|
|
11
|
-
import
|
|
10
|
+
import path4 from "path";
|
|
11
|
+
import fs4 from "fs-extra";
|
|
12
12
|
|
|
13
13
|
// src/utils/tmux.ts
|
|
14
14
|
import execa from "execa";
|
|
@@ -68,6 +68,11 @@ var Logger = class {
|
|
|
68
68
|
var logger = new Logger();
|
|
69
69
|
|
|
70
70
|
// src/utils/tmux.ts
|
|
71
|
+
import * as path2 from "path";
|
|
72
|
+
import * as fs2 from "fs";
|
|
73
|
+
import { fileURLToPath } from "url";
|
|
74
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
75
|
+
var __dirname = path2.dirname(__filename);
|
|
71
76
|
var TmuxError = class extends Error {
|
|
72
77
|
constructor(message, command, originalError) {
|
|
73
78
|
super(message);
|
|
@@ -96,6 +101,7 @@ var TmuxCommands = class {
|
|
|
96
101
|
logger.debug(`Creating tmux session: ${name} at ${projectPath}`);
|
|
97
102
|
await execa("tmux", ["new-session", "-d", "-s", name, "-c", projectPath]);
|
|
98
103
|
logger.info(`Tmux session created: ${name}`);
|
|
104
|
+
await this.applyOrkaTheme(name);
|
|
99
105
|
} catch (error) {
|
|
100
106
|
throw new TmuxError(
|
|
101
107
|
`Failed to create tmux session: ${name}`,
|
|
@@ -104,6 +110,37 @@ var TmuxCommands = class {
|
|
|
104
110
|
);
|
|
105
111
|
}
|
|
106
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Apply Claude-Orka custom tmux theme to a session
|
|
115
|
+
*/
|
|
116
|
+
static async applyOrkaTheme(sessionName) {
|
|
117
|
+
try {
|
|
118
|
+
const possiblePaths = [
|
|
119
|
+
// When installed globally via npm
|
|
120
|
+
path2.join(__dirname, "../../.tmux.orka.conf"),
|
|
121
|
+
// When running from source
|
|
122
|
+
path2.join(process.cwd(), ".tmux.orka.conf"),
|
|
123
|
+
// Fallback: check in the module directory
|
|
124
|
+
path2.join(__dirname, "../../../.tmux.orka.conf")
|
|
125
|
+
];
|
|
126
|
+
let configPath = null;
|
|
127
|
+
for (const p of possiblePaths) {
|
|
128
|
+
if (fs2.existsSync(p)) {
|
|
129
|
+
configPath = p;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!configPath) {
|
|
134
|
+
logger.warn("Claude-Orka tmux config not found, skipping theme application");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
logger.debug(`Applying Orka theme from: ${configPath}`);
|
|
138
|
+
await execa("tmux", ["source-file", "-t", sessionName, configPath]);
|
|
139
|
+
logger.info("Claude-Orka theme applied successfully");
|
|
140
|
+
} catch (error) {
|
|
141
|
+
logger.warn("Failed to apply Orka theme, continuing with default tmux theme", error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
107
144
|
/**
|
|
108
145
|
* Abrir una terminal que se adjunte a una sesión tmux existente
|
|
109
146
|
* (Solo macOS por ahora)
|
|
@@ -386,18 +423,18 @@ var TmuxCommands = class {
|
|
|
386
423
|
};
|
|
387
424
|
|
|
388
425
|
// src/utils/claude-history.ts
|
|
389
|
-
import
|
|
426
|
+
import fs3 from "fs-extra";
|
|
390
427
|
import os from "os";
|
|
391
|
-
import
|
|
392
|
-
var CLAUDE_HISTORY_PATH =
|
|
428
|
+
import path3 from "path";
|
|
429
|
+
var CLAUDE_HISTORY_PATH = path3.join(os.homedir(), ".claude", "history.jsonl");
|
|
393
430
|
async function readClaudeHistory() {
|
|
394
431
|
try {
|
|
395
|
-
const exists = await
|
|
432
|
+
const exists = await fs3.pathExists(CLAUDE_HISTORY_PATH);
|
|
396
433
|
if (!exists) {
|
|
397
434
|
logger.warn(`Claude history file not found: ${CLAUDE_HISTORY_PATH}`);
|
|
398
435
|
return [];
|
|
399
436
|
}
|
|
400
|
-
const content = await
|
|
437
|
+
const content = await fs3.readFile(CLAUDE_HISTORY_PATH, "utf-8");
|
|
401
438
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
402
439
|
const entries = [];
|
|
403
440
|
for (const line of lines) {
|
|
@@ -440,9 +477,9 @@ var StateManager = class {
|
|
|
440
477
|
orkaDir;
|
|
441
478
|
statePath;
|
|
442
479
|
constructor(projectPath) {
|
|
443
|
-
this.projectPath =
|
|
444
|
-
this.orkaDir =
|
|
445
|
-
this.statePath =
|
|
480
|
+
this.projectPath = path4.resolve(projectPath);
|
|
481
|
+
this.orkaDir = path4.join(this.projectPath, ".claude-orka");
|
|
482
|
+
this.statePath = path4.join(this.orkaDir, "state.json");
|
|
446
483
|
}
|
|
447
484
|
/**
|
|
448
485
|
* Initialize StateManager
|
|
@@ -451,7 +488,7 @@ var StateManager = class {
|
|
|
451
488
|
async initialize() {
|
|
452
489
|
logger.debug("Initializing StateManager");
|
|
453
490
|
await this.ensureDirectories();
|
|
454
|
-
if (!await
|
|
491
|
+
if (!await fs4.pathExists(this.statePath)) {
|
|
455
492
|
logger.info("Creating initial state.json");
|
|
456
493
|
const initialState = {
|
|
457
494
|
version: "1.0.0",
|
|
@@ -467,7 +504,7 @@ var StateManager = class {
|
|
|
467
504
|
* Create directory structure
|
|
468
505
|
*/
|
|
469
506
|
async ensureDirectories() {
|
|
470
|
-
await
|
|
507
|
+
await fs4.ensureDir(this.orkaDir);
|
|
471
508
|
logger.debug("Directories ensured");
|
|
472
509
|
}
|
|
473
510
|
/**
|
|
@@ -475,7 +512,7 @@ var StateManager = class {
|
|
|
475
512
|
*/
|
|
476
513
|
async read() {
|
|
477
514
|
try {
|
|
478
|
-
const content = await
|
|
515
|
+
const content = await fs4.readFile(this.statePath, "utf-8");
|
|
479
516
|
return JSON.parse(content);
|
|
480
517
|
} catch (error) {
|
|
481
518
|
logger.error("Failed to read state:", error);
|
|
@@ -488,7 +525,7 @@ var StateManager = class {
|
|
|
488
525
|
async save(state) {
|
|
489
526
|
try {
|
|
490
527
|
state.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
491
|
-
await
|
|
528
|
+
await fs4.writeFile(this.statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
492
529
|
logger.debug("State saved");
|
|
493
530
|
} catch (error) {
|
|
494
531
|
logger.error("Failed to save state:", error);
|
|
@@ -704,8 +741,8 @@ var StateManager = class {
|
|
|
704
741
|
*/
|
|
705
742
|
async saveContext(type, id, content) {
|
|
706
743
|
const contextPath = type === "session" ? this.getSessionContextPath(id) : this.getForkContextPath(id);
|
|
707
|
-
const fullPath =
|
|
708
|
-
await
|
|
744
|
+
const fullPath = path4.join(this.projectPath, contextPath);
|
|
745
|
+
await fs4.writeFile(fullPath, content, "utf-8");
|
|
709
746
|
logger.info(`Context saved: ${contextPath}`);
|
|
710
747
|
return contextPath;
|
|
711
748
|
}
|
|
@@ -713,11 +750,11 @@ var StateManager = class {
|
|
|
713
750
|
* Leer un contexto desde archivo
|
|
714
751
|
*/
|
|
715
752
|
async readContext(contextPath) {
|
|
716
|
-
const fullPath =
|
|
717
|
-
if (!await
|
|
753
|
+
const fullPath = path4.join(this.projectPath, contextPath);
|
|
754
|
+
if (!await fs4.pathExists(fullPath)) {
|
|
718
755
|
throw new Error(`Context file not found: ${contextPath}`);
|
|
719
756
|
}
|
|
720
|
-
return await
|
|
757
|
+
return await fs4.readFile(fullPath, "utf-8");
|
|
721
758
|
}
|
|
722
759
|
// --- HELPERS ---
|
|
723
760
|
/**
|
|
@@ -742,15 +779,15 @@ var StateManager = class {
|
|
|
742
779
|
|
|
743
780
|
// src/core/SessionManager.ts
|
|
744
781
|
import { v4 as uuidv4 } from "uuid";
|
|
745
|
-
import
|
|
746
|
-
import { fileURLToPath } from "url";
|
|
747
|
-
import { dirname } from "path";
|
|
748
|
-
import
|
|
782
|
+
import path5 from "path";
|
|
783
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
784
|
+
import { dirname as dirname2 } from "path";
|
|
785
|
+
import fs5 from "fs-extra";
|
|
749
786
|
import { spawn } from "child_process";
|
|
750
787
|
import { createRequire } from "module";
|
|
751
788
|
var require2 = createRequire(import.meta.url);
|
|
752
|
-
var
|
|
753
|
-
var
|
|
789
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
790
|
+
var __dirname2 = dirname2(__filename2);
|
|
754
791
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
755
792
|
var SessionManager = class {
|
|
756
793
|
stateManager;
|
|
@@ -1127,12 +1164,12 @@ var SessionManager = class {
|
|
|
1127
1164
|
throw new Error(`Fork ${forkId} not found`);
|
|
1128
1165
|
}
|
|
1129
1166
|
logger.info(`Generating export for fork: ${fork.name}`);
|
|
1130
|
-
const exportsDir =
|
|
1131
|
-
await
|
|
1167
|
+
const exportsDir = path5.join(this.projectPath, ".claude-orka", "exports");
|
|
1168
|
+
await fs5.ensureDir(exportsDir);
|
|
1132
1169
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1133
1170
|
const exportName = `fork-${fork.name}-${timestamp}.md`;
|
|
1134
1171
|
const relativeExportPath = `.claude-orka/exports/${exportName}`;
|
|
1135
|
-
const absoluteExportPath =
|
|
1172
|
+
const absoluteExportPath = path5.join(this.projectPath, relativeExportPath);
|
|
1136
1173
|
const prompt = `
|
|
1137
1174
|
Please generate a complete summary of this fork conversation "${fork.name}" and save it to the file:
|
|
1138
1175
|
\`${absoluteExportPath}\`
|
|
@@ -1208,17 +1245,17 @@ Write the summary in Markdown format and save it to the specified file.
|
|
|
1208
1245
|
throw new Error(`Parent ${parentName} is not active. Cannot send merge command.`);
|
|
1209
1246
|
}
|
|
1210
1247
|
let contextPath = fork.contextPath;
|
|
1211
|
-
let fullPath =
|
|
1212
|
-
let exists = await
|
|
1248
|
+
let fullPath = path5.join(this.projectPath, contextPath);
|
|
1249
|
+
let exists = await fs5.pathExists(fullPath);
|
|
1213
1250
|
if (!exists) {
|
|
1214
1251
|
logger.warn(`Export file not found: ${contextPath}. Looking for most recent export...`);
|
|
1215
|
-
const exportsDir =
|
|
1216
|
-
const files = await
|
|
1252
|
+
const exportsDir = path5.join(this.projectPath, ".claude-orka", "exports");
|
|
1253
|
+
const files = await fs5.readdir(exportsDir);
|
|
1217
1254
|
const forkExports = files.filter((f) => f.startsWith(`fork-${fork.name}-`) && f.endsWith(".md")).sort().reverse();
|
|
1218
1255
|
if (forkExports.length > 0) {
|
|
1219
1256
|
contextPath = `.claude-orka/exports/${forkExports[0]}`;
|
|
1220
|
-
fullPath =
|
|
1221
|
-
exists = await
|
|
1257
|
+
fullPath = path5.join(this.projectPath, contextPath);
|
|
1258
|
+
exists = await fs5.pathExists(fullPath);
|
|
1222
1259
|
logger.info(`Using most recent export: ${contextPath}`);
|
|
1223
1260
|
}
|
|
1224
1261
|
}
|
|
@@ -1298,12 +1335,27 @@ Analyze the content and help me integrate the changes and learnings from the for
|
|
|
1298
1335
|
logger.warn("Electron not available, skipping UI launch");
|
|
1299
1336
|
return;
|
|
1300
1337
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1338
|
+
const possiblePaths = [
|
|
1339
|
+
// Production (installed globally): dist/core -> ../../dist/electron/main/main.js
|
|
1340
|
+
path5.join(__dirname2, "../../dist/electron/main/main.js"),
|
|
1341
|
+
// Development (running from dist): dist/core -> ../electron/main/main.js
|
|
1342
|
+
path5.join(__dirname2, "../electron/main/main.js"),
|
|
1343
|
+
// Development (running from src): src/core -> ../../dist/electron/main/main.js
|
|
1344
|
+
path5.join(__dirname2, "../../dist/electron/main/main.js"),
|
|
1345
|
+
// Fallback: check relative to package root
|
|
1346
|
+
path5.join(__dirname2, "../../electron/main/main.js")
|
|
1347
|
+
];
|
|
1348
|
+
let mainPath = null;
|
|
1349
|
+
for (const p of possiblePaths) {
|
|
1350
|
+
if (fs5.existsSync(p)) {
|
|
1351
|
+
mainPath = p;
|
|
1352
|
+
logger.debug(`Found Electron main.js at: ${p}`);
|
|
1353
|
+
break;
|
|
1354
|
+
}
|
|
1304
1355
|
}
|
|
1305
|
-
if (!
|
|
1306
|
-
logger.warn(`Electron main.js not found
|
|
1356
|
+
if (!mainPath) {
|
|
1357
|
+
logger.warn(`Electron main.js not found. Tried paths: ${possiblePaths.join(", ")}`);
|
|
1358
|
+
logger.warn("Skipping UI launch");
|
|
1307
1359
|
return;
|
|
1308
1360
|
}
|
|
1309
1361
|
const electronProcess = spawn(
|
|
@@ -1810,8 +1862,8 @@ ${statusEmoji} ${chalk.bold(session.name)}`);
|
|
|
1810
1862
|
|
|
1811
1863
|
// src/cli/utils/errors.ts
|
|
1812
1864
|
import chalk2 from "chalk";
|
|
1813
|
-
import
|
|
1814
|
-
import
|
|
1865
|
+
import fs6 from "fs";
|
|
1866
|
+
import path6 from "path";
|
|
1815
1867
|
var CLIError = class extends Error {
|
|
1816
1868
|
constructor(message, exitCode = 1) {
|
|
1817
1869
|
super(message);
|
|
@@ -1843,8 +1895,8 @@ function validateForkId(forkId) {
|
|
|
1843
1895
|
}
|
|
1844
1896
|
}
|
|
1845
1897
|
function validateInitialized(projectPath) {
|
|
1846
|
-
const orkaDir =
|
|
1847
|
-
if (!
|
|
1898
|
+
const orkaDir = path6.join(projectPath, ".claude-orka");
|
|
1899
|
+
if (!fs6.existsSync(orkaDir)) {
|
|
1848
1900
|
throw new CLIError(
|
|
1849
1901
|
'Project not initialized. Run "orka init" first.',
|
|
1850
1902
|
2
|
|
@@ -2237,8 +2289,8 @@ function mergeCommand(program2) {
|
|
|
2237
2289
|
|
|
2238
2290
|
// src/cli/commands/doctor.ts
|
|
2239
2291
|
import execa2 from "execa";
|
|
2240
|
-
import
|
|
2241
|
-
import
|
|
2292
|
+
import fs7 from "fs-extra";
|
|
2293
|
+
import path7 from "path";
|
|
2242
2294
|
import chalk4 from "chalk";
|
|
2243
2295
|
function doctorCommand(program2) {
|
|
2244
2296
|
program2.command("doctor").description("Check system dependencies and configuration").action(async () => {
|
|
@@ -2249,6 +2301,7 @@ function doctorCommand(program2) {
|
|
|
2249
2301
|
results.push(await checkNodeVersion());
|
|
2250
2302
|
results.push(await checkTmux());
|
|
2251
2303
|
results.push(await checkClaude());
|
|
2304
|
+
results.push(await checkElectron());
|
|
2252
2305
|
results.push(await checkProjectInit());
|
|
2253
2306
|
results.push(await checkWritePermissions());
|
|
2254
2307
|
results.push(await checkClaudeDir());
|
|
@@ -2327,13 +2380,33 @@ async function checkClaude() {
|
|
|
2327
2380
|
};
|
|
2328
2381
|
}
|
|
2329
2382
|
}
|
|
2383
|
+
async function checkElectron() {
|
|
2384
|
+
try {
|
|
2385
|
+
const { stdout } = await execa2("electron", ["--version"]);
|
|
2386
|
+
const version2 = stdout.trim();
|
|
2387
|
+
return {
|
|
2388
|
+
name: "Electron",
|
|
2389
|
+
status: "pass",
|
|
2390
|
+
message: version2.startsWith("v") ? version2 : `v${version2}`,
|
|
2391
|
+
details: "Required for visual UI"
|
|
2392
|
+
};
|
|
2393
|
+
} catch (error) {
|
|
2394
|
+
return {
|
|
2395
|
+
name: "Electron",
|
|
2396
|
+
status: "warn",
|
|
2397
|
+
message: "Not found",
|
|
2398
|
+
details: "Electron is required for the visual UI (optional)",
|
|
2399
|
+
fix: "Install Electron: npm install -g electron"
|
|
2400
|
+
};
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2330
2403
|
async function checkProjectInit() {
|
|
2331
2404
|
const projectPath = process.cwd();
|
|
2332
|
-
const orkaDir =
|
|
2333
|
-
const stateFile =
|
|
2405
|
+
const orkaDir = path7.join(projectPath, ".claude-orka");
|
|
2406
|
+
const stateFile = path7.join(orkaDir, "state.json");
|
|
2334
2407
|
try {
|
|
2335
|
-
const dirExists = await
|
|
2336
|
-
const stateExists = await
|
|
2408
|
+
const dirExists = await fs7.pathExists(orkaDir);
|
|
2409
|
+
const stateExists = await fs7.pathExists(stateFile);
|
|
2337
2410
|
if (dirExists && stateExists) {
|
|
2338
2411
|
return {
|
|
2339
2412
|
name: "Project initialization",
|
|
@@ -2370,9 +2443,9 @@ async function checkProjectInit() {
|
|
|
2370
2443
|
async function checkWritePermissions() {
|
|
2371
2444
|
const projectPath = process.cwd();
|
|
2372
2445
|
try {
|
|
2373
|
-
const testFile =
|
|
2374
|
-
await
|
|
2375
|
-
await
|
|
2446
|
+
const testFile = path7.join(projectPath, ".claude-orka-write-test");
|
|
2447
|
+
await fs7.writeFile(testFile, "test");
|
|
2448
|
+
await fs7.remove(testFile);
|
|
2376
2449
|
return {
|
|
2377
2450
|
name: "Write permissions",
|
|
2378
2451
|
status: "pass",
|
|
@@ -2391,11 +2464,11 @@ async function checkWritePermissions() {
|
|
|
2391
2464
|
}
|
|
2392
2465
|
async function checkClaudeDir() {
|
|
2393
2466
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
2394
|
-
const claudeDir =
|
|
2395
|
-
const historyFile =
|
|
2467
|
+
const claudeDir = path7.join(homeDir, ".claude");
|
|
2468
|
+
const historyFile = path7.join(claudeDir, "history.jsonl");
|
|
2396
2469
|
try {
|
|
2397
|
-
const dirExists = await
|
|
2398
|
-
const historyExists = await
|
|
2470
|
+
const dirExists = await fs7.pathExists(claudeDir);
|
|
2471
|
+
const historyExists = await fs7.pathExists(historyFile);
|
|
2399
2472
|
if (dirExists && historyExists) {
|
|
2400
2473
|
return {
|
|
2401
2474
|
name: "Claude directory",
|
|
@@ -2487,6 +2560,7 @@ function prepareCommand(program2) {
|
|
|
2487
2560
|
console.log(chalk5.bold.cyan("\n\u{1F527} Claude-Orka Preparation\n"));
|
|
2488
2561
|
console.log("This will help you install required dependencies:\n");
|
|
2489
2562
|
console.log(" \u2022 tmux (terminal multiplexer)");
|
|
2563
|
+
console.log(" \u2022 Electron (for visual UI)");
|
|
2490
2564
|
console.log(" \u2022 Claude CLI (if needed)\n");
|
|
2491
2565
|
if (!options.yes) {
|
|
2492
2566
|
const rl = readline2.createInterface({
|
|
@@ -2506,6 +2580,7 @@ function prepareCommand(program2) {
|
|
|
2506
2580
|
console.log(chalk5.gray(`
|
|
2507
2581
|
Detected: ${system.platform}`));
|
|
2508
2582
|
await installTmux(system);
|
|
2583
|
+
await installElectron();
|
|
2509
2584
|
await checkClaudeCLI();
|
|
2510
2585
|
console.log(chalk5.bold.green("\n\u2713 Preparation complete!\n"));
|
|
2511
2586
|
console.log("Run " + chalk5.cyan("orka doctor") + " to verify everything is working.");
|
|
@@ -2594,6 +2669,31 @@ Error: ${error.message}`));
|
|
|
2594
2669
|
console.log(chalk5.cyan(" CentOS: sudo yum install tmux"));
|
|
2595
2670
|
}
|
|
2596
2671
|
}
|
|
2672
|
+
async function installElectron() {
|
|
2673
|
+
console.log(chalk5.bold("\n\u26A1 Installing Electron...\n"));
|
|
2674
|
+
try {
|
|
2675
|
+
await execa3("electron", ["--version"]);
|
|
2676
|
+
Output.success("Electron is already installed");
|
|
2677
|
+
return;
|
|
2678
|
+
} catch {
|
|
2679
|
+
}
|
|
2680
|
+
const spinner = ora4("Installing Electron globally...").start();
|
|
2681
|
+
try {
|
|
2682
|
+
await execa3("npm", ["install", "-g", "electron"], {
|
|
2683
|
+
stdio: "ignore"
|
|
2684
|
+
});
|
|
2685
|
+
spinner.succeed("Electron installed globally");
|
|
2686
|
+
} catch (error) {
|
|
2687
|
+
spinner.fail("Failed to install Electron");
|
|
2688
|
+
console.log(chalk5.red(`
|
|
2689
|
+
Error: ${error.message}`));
|
|
2690
|
+
console.log(chalk5.yellow("\nPlease install Electron manually:"));
|
|
2691
|
+
console.log(chalk5.cyan(" npm install -g electron"));
|
|
2692
|
+
console.log(
|
|
2693
|
+
chalk5.gray("\nNote: Electron is required for the visual UI interface.")
|
|
2694
|
+
);
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2597
2697
|
async function checkClaudeCLI() {
|
|
2598
2698
|
console.log(chalk5.bold("\n\u{1F916} Checking Claude CLI...\n"));
|
|
2599
2699
|
try {
|
|
@@ -2612,13 +2712,13 @@ async function checkClaudeCLI() {
|
|
|
2612
2712
|
}
|
|
2613
2713
|
|
|
2614
2714
|
// src/cli/index.ts
|
|
2615
|
-
var
|
|
2616
|
-
var
|
|
2617
|
-
var packageJsonPath =
|
|
2715
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
2716
|
+
var __dirname3 = dirname3(__filename3);
|
|
2717
|
+
var packageJsonPath = join2(__dirname3, "../package.json");
|
|
2618
2718
|
try {
|
|
2619
2719
|
readFileSync(packageJsonPath, "utf-8");
|
|
2620
2720
|
} catch {
|
|
2621
|
-
packageJsonPath =
|
|
2721
|
+
packageJsonPath = join2(__dirname3, "../../package.json");
|
|
2622
2722
|
}
|
|
2623
2723
|
var packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
2624
2724
|
var version = packageJson.version;
|