@shmulikdav/solix 1.0.4 → 1.1.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/index.js +116 -20
- package/dist/web/assets/{index-Cwodzfxn.js → index-6_jn0Tib.js} +42 -42
- package/dist/web/assets/index-DoON6Diy.css +1 -0
- package/dist/web/icons/solix-192.png +0 -0
- package/dist/web/icons/solix-512.png +0 -0
- package/dist/web/index.html +10 -4
- package/dist/web/manifest.webmanifest +1 -0
- package/dist/web/registerSW.js +1 -0
- package/dist/web/sw.js +1 -0
- package/dist/web/workbox-9c191d2f.js +1 -0
- package/package.json +1 -1
- package/dist/web/assets/index-7MEHHg3R.css +0 -1
package/dist/index.js
CHANGED
|
@@ -926,6 +926,7 @@ function getDb() {
|
|
|
926
926
|
db.exec(SCHEMA);
|
|
927
927
|
ensureColumn(db, "sessions", "kind", "kind TEXT NOT NULL DEFAULT 'user'");
|
|
928
928
|
ensureColumn(db, "sessions", "advisor_role", "advisor_role TEXT");
|
|
929
|
+
ensureColumn(db, "sessions", "worktree_path", "worktree_path TEXT");
|
|
929
930
|
ensureColumn(db, "advisors", "texture_pack", "texture_pack TEXT");
|
|
930
931
|
ensureColumn(db, "missions", "error_summary", "error_summary TEXT");
|
|
931
932
|
_db = db;
|
|
@@ -1011,7 +1012,8 @@ function rowToSession(row) {
|
|
|
1011
1012
|
currentMissionId: row.current_mission_id ?? void 0,
|
|
1012
1013
|
lastCompletedMissionId: row.last_completed_mission_id ?? void 0,
|
|
1013
1014
|
orbitSlot: row.orbit_slot,
|
|
1014
|
-
name: row.name ?? void 0
|
|
1015
|
+
name: row.name ?? void 0,
|
|
1016
|
+
worktreePath: row.worktree_path ?? void 0
|
|
1015
1017
|
};
|
|
1016
1018
|
}
|
|
1017
1019
|
function nextOrbitSlot(db, projectId) {
|
|
@@ -1046,9 +1048,9 @@ function upsertSession(db, input) {
|
|
|
1046
1048
|
`INSERT INTO sessions (
|
|
1047
1049
|
id, pid, project_id, parent_session_id, origin, model, status,
|
|
1048
1050
|
context_usage_pct, orbit_slot, cwd, name, kind, advisor_role,
|
|
1049
|
-
created_at, updated_at
|
|
1051
|
+
worktree_path, created_at, updated_at
|
|
1050
1052
|
)
|
|
1051
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, NULL, ?, ?, ?, ?)`
|
|
1053
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, NULL, ?, ?, ?, ?, ?)`
|
|
1052
1054
|
).run(
|
|
1053
1055
|
input.id,
|
|
1054
1056
|
input.pid,
|
|
@@ -1061,6 +1063,7 @@ function upsertSession(db, input) {
|
|
|
1061
1063
|
input.cwd,
|
|
1062
1064
|
kind,
|
|
1063
1065
|
input.advisorRole ?? null,
|
|
1066
|
+
input.worktreePath ?? null,
|
|
1064
1067
|
ts2,
|
|
1065
1068
|
ts2
|
|
1066
1069
|
);
|
|
@@ -1078,7 +1081,8 @@ function upsertSession(db, input) {
|
|
|
1078
1081
|
advisorRole: input.advisorRole,
|
|
1079
1082
|
parentSessionId: input.parentSessionId,
|
|
1080
1083
|
contextUsagePct: 0,
|
|
1081
|
-
orbitSlot
|
|
1084
|
+
orbitSlot,
|
|
1085
|
+
worktreePath: input.worktreePath
|
|
1082
1086
|
};
|
|
1083
1087
|
}
|
|
1084
1088
|
function setSessionStatus(db, sessionId, status) {
|
|
@@ -2409,9 +2413,51 @@ function mimeFor(filePath) {
|
|
|
2409
2413
|
}
|
|
2410
2414
|
|
|
2411
2415
|
// ../server/src/launcher.ts
|
|
2412
|
-
import { spawn } from "child_process";
|
|
2413
|
-
import { existsSync as existsSync7 } from "fs";
|
|
2416
|
+
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
2417
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
2418
|
+
import { homedir as homedir5 } from "os";
|
|
2419
|
+
import { basename as basename2, join as join9 } from "path";
|
|
2414
2420
|
import { nanoid as nanoid4 } from "nanoid";
|
|
2421
|
+
function ensureWorktree(opts) {
|
|
2422
|
+
const repoRoot = (() => {
|
|
2423
|
+
const r = spawnSync2("git", ["rev-parse", "--show-toplevel"], {
|
|
2424
|
+
cwd: opts.repoCwd,
|
|
2425
|
+
encoding: "utf8"
|
|
2426
|
+
});
|
|
2427
|
+
if (r.status !== 0) {
|
|
2428
|
+
throw new Error(`not a git repository: ${opts.repoCwd}`);
|
|
2429
|
+
}
|
|
2430
|
+
return (r.stdout ?? "").trim();
|
|
2431
|
+
})();
|
|
2432
|
+
const repoName = basename2(repoRoot);
|
|
2433
|
+
const safeBranch = opts.branch.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
2434
|
+
const worktreesDir = join9(homedir5(), ".solix", "worktrees");
|
|
2435
|
+
const path = join9(worktreesDir, `${repoName}-${safeBranch}`);
|
|
2436
|
+
const list = spawnSync2("git", ["worktree", "list", "--porcelain"], {
|
|
2437
|
+
cwd: repoRoot,
|
|
2438
|
+
encoding: "utf8"
|
|
2439
|
+
});
|
|
2440
|
+
if (list.status === 0 && (list.stdout ?? "").includes(`worktree ${path}`)) {
|
|
2441
|
+
return { path, created: false };
|
|
2442
|
+
}
|
|
2443
|
+
mkdirSync3(worktreesDir, { recursive: true });
|
|
2444
|
+
const branchProbe = spawnSync2(
|
|
2445
|
+
"git",
|
|
2446
|
+
["rev-parse", "--verify", "--quiet", `refs/heads/${opts.branch}`],
|
|
2447
|
+
{ cwd: repoRoot, encoding: "utf8" }
|
|
2448
|
+
);
|
|
2449
|
+
const args = branchProbe.status === 0 ? ["worktree", "add", path, opts.branch] : ["worktree", "add", path, "-b", opts.branch, opts.baseRef ?? "HEAD"];
|
|
2450
|
+
const add = spawnSync2("git", args, {
|
|
2451
|
+
cwd: repoRoot,
|
|
2452
|
+
encoding: "utf8"
|
|
2453
|
+
});
|
|
2454
|
+
if (add.status !== 0) {
|
|
2455
|
+
throw new Error(
|
|
2456
|
+
`git worktree add failed: ${(add.stderr ?? "").slice(0, 280)}`
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
return { path, created: true };
|
|
2460
|
+
}
|
|
2415
2461
|
var FAKE_CLAUDE = process.env.SOLIX_FAKE_CLAUDE === "1";
|
|
2416
2462
|
var Launcher = class {
|
|
2417
2463
|
constructor(db, broadcaster) {
|
|
@@ -2565,14 +2611,44 @@ var Launcher = class {
|
|
|
2565
2611
|
*/
|
|
2566
2612
|
launch(opts) {
|
|
2567
2613
|
if (!opts.initialPrompt.trim()) return { ok: false };
|
|
2614
|
+
let spawnCwd = opts.cwd;
|
|
2615
|
+
let worktreePath;
|
|
2616
|
+
if (opts.worktreeBranch?.trim()) {
|
|
2617
|
+
try {
|
|
2618
|
+
const wt = ensureWorktree({
|
|
2619
|
+
repoCwd: opts.cwd,
|
|
2620
|
+
branch: opts.worktreeBranch.trim(),
|
|
2621
|
+
baseRef: opts.worktreeBaseRef?.trim() || void 0
|
|
2622
|
+
});
|
|
2623
|
+
spawnCwd = wt.path;
|
|
2624
|
+
worktreePath = wt.path;
|
|
2625
|
+
this.broadcaster.broadcast({
|
|
2626
|
+
type: "toast",
|
|
2627
|
+
level: "info",
|
|
2628
|
+
message: wt.created ? `Worktree created at ${wt.path}` : `Reusing worktree ${wt.path}`
|
|
2629
|
+
});
|
|
2630
|
+
} catch (err) {
|
|
2631
|
+
this.broadcaster.broadcast({
|
|
2632
|
+
type: "toast",
|
|
2633
|
+
level: "error",
|
|
2634
|
+
message: `Worktree setup failed: ${err.message}`
|
|
2635
|
+
});
|
|
2636
|
+
return { ok: false };
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2568
2639
|
if (FAKE_CLAUDE) {
|
|
2569
|
-
return this.launchSynthetic(
|
|
2640
|
+
return this.launchSynthetic({
|
|
2641
|
+
cwd: spawnCwd,
|
|
2642
|
+
model: opts.model,
|
|
2643
|
+
initialPrompt: opts.initialPrompt,
|
|
2644
|
+
worktreePath
|
|
2645
|
+
});
|
|
2570
2646
|
}
|
|
2571
|
-
if (!existsSync7(
|
|
2647
|
+
if (!existsSync7(spawnCwd)) {
|
|
2572
2648
|
this.broadcaster.broadcast({
|
|
2573
2649
|
type: "toast",
|
|
2574
2650
|
level: "error",
|
|
2575
|
-
message: `Launch failed: cwd does not exist (${
|
|
2651
|
+
message: `Launch failed: cwd does not exist (${spawnCwd})`
|
|
2576
2652
|
});
|
|
2577
2653
|
return { ok: false };
|
|
2578
2654
|
}
|
|
@@ -2582,9 +2658,10 @@ var Launcher = class {
|
|
|
2582
2658
|
const sessionId = `task-${nanoid4(8)}`;
|
|
2583
2659
|
return this.spawnPrint({
|
|
2584
2660
|
sessionId,
|
|
2585
|
-
cwd:
|
|
2661
|
+
cwd: spawnCwd,
|
|
2586
2662
|
args,
|
|
2587
|
-
isFollowUp: false
|
|
2663
|
+
isFollowUp: false,
|
|
2664
|
+
worktreePath
|
|
2588
2665
|
});
|
|
2589
2666
|
}
|
|
2590
2667
|
sendPromptToInternal(sessionId, text) {
|
|
@@ -2630,6 +2707,15 @@ var Launcher = class {
|
|
|
2630
2707
|
isFollowUp: true
|
|
2631
2708
|
}).ok;
|
|
2632
2709
|
}
|
|
2710
|
+
/** Returns the worktree path the launcher resolved for an internal task,
|
|
2711
|
+
* if any. Used by router.onSessionStart to persist worktree_path on the
|
|
2712
|
+
* session row when claude reports its session_start hook. */
|
|
2713
|
+
worktreePathForInternalCwd(cwd) {
|
|
2714
|
+
for (const rec of this.internalTasks.values()) {
|
|
2715
|
+
if (rec.cwd === cwd && rec.worktreePath) return rec.worktreePath;
|
|
2716
|
+
}
|
|
2717
|
+
return void 0;
|
|
2718
|
+
}
|
|
2633
2719
|
spawnPrint(opts) {
|
|
2634
2720
|
let child;
|
|
2635
2721
|
try {
|
|
@@ -2649,7 +2735,10 @@ var Launcher = class {
|
|
|
2649
2735
|
}
|
|
2650
2736
|
const pid = child.pid ?? 0;
|
|
2651
2737
|
if (!opts.isFollowUp) {
|
|
2652
|
-
this.internalTasks.set(opts.sessionId, {
|
|
2738
|
+
this.internalTasks.set(opts.sessionId, {
|
|
2739
|
+
cwd: opts.cwd,
|
|
2740
|
+
worktreePath: opts.worktreePath
|
|
2741
|
+
});
|
|
2653
2742
|
}
|
|
2654
2743
|
let stdout = "";
|
|
2655
2744
|
let stderr = "";
|
|
@@ -2702,7 +2791,8 @@ var Launcher = class {
|
|
|
2702
2791
|
projectId: project.id,
|
|
2703
2792
|
cwd: opts.cwd,
|
|
2704
2793
|
origin: "internal",
|
|
2705
|
-
model: opts.model ?? "sonnet"
|
|
2794
|
+
model: opts.model ?? "sonnet",
|
|
2795
|
+
worktreePath: opts.worktreePath
|
|
2706
2796
|
});
|
|
2707
2797
|
const active = setSessionStatus(this.db, sessionId, "active");
|
|
2708
2798
|
if (active)
|
|
@@ -2845,6 +2935,7 @@ var EventRouter = class {
|
|
|
2845
2935
|
const project = ensureProject(this.db, event.cwd);
|
|
2846
2936
|
const sessionId = this.extractSessionId(event);
|
|
2847
2937
|
const advisorRole = this.launcher?.advisorRoleForPid(event.pid);
|
|
2938
|
+
const worktreePath = this.launcher?.worktreePathForInternalCwd(event.cwd);
|
|
2848
2939
|
const session = upsertSession(this.db, {
|
|
2849
2940
|
id: sessionId,
|
|
2850
2941
|
pid: event.pid,
|
|
@@ -2854,7 +2945,8 @@ var EventRouter = class {
|
|
|
2854
2945
|
model: this.extractModel(event),
|
|
2855
2946
|
parentSessionId: this.extractParentSessionId(event),
|
|
2856
2947
|
kind: advisorRole ? "advisor" : "user",
|
|
2857
|
-
advisorRole
|
|
2948
|
+
advisorRole,
|
|
2949
|
+
worktreePath
|
|
2858
2950
|
});
|
|
2859
2951
|
this.broadcaster.broadcast({ type: "session_upsert", session });
|
|
2860
2952
|
if (!session.parentSessionId) {
|
|
@@ -3132,7 +3224,9 @@ var EventRouter = class {
|
|
|
3132
3224
|
return this.launcher.launch({
|
|
3133
3225
|
cwd: opts.cwd,
|
|
3134
3226
|
model: opts.model,
|
|
3135
|
-
initialPrompt: opts.initialPrompt
|
|
3227
|
+
initialPrompt: opts.initialPrompt,
|
|
3228
|
+
worktreeBranch: opts.worktreeBranch,
|
|
3229
|
+
worktreeBaseRef: opts.worktreeBaseRef
|
|
3136
3230
|
});
|
|
3137
3231
|
}
|
|
3138
3232
|
sendPromptToSession(sessionId, text) {
|
|
@@ -3244,7 +3338,9 @@ function handleClientMessage(ctx, _ws, msg) {
|
|
|
3244
3338
|
ctx.router.launchInternalSession({
|
|
3245
3339
|
cwd: msg.cwd,
|
|
3246
3340
|
model: msg.model,
|
|
3247
|
-
initialPrompt: msg.initialPrompt
|
|
3341
|
+
initialPrompt: msg.initialPrompt,
|
|
3342
|
+
worktreeBranch: msg.worktreeBranch,
|
|
3343
|
+
worktreeBaseRef: msg.worktreeBaseRef
|
|
3248
3344
|
});
|
|
3249
3345
|
break;
|
|
3250
3346
|
case "invoke_advisor":
|
|
@@ -3274,9 +3370,9 @@ import {
|
|
|
3274
3370
|
statSync as statSync5,
|
|
3275
3371
|
watch
|
|
3276
3372
|
} from "fs";
|
|
3277
|
-
import { homedir as
|
|
3278
|
-
import { join as
|
|
3279
|
-
var TRANSCRIPT_BASE =
|
|
3373
|
+
import { homedir as homedir6 } from "os";
|
|
3374
|
+
import { join as join10 } from "path";
|
|
3375
|
+
var TRANSCRIPT_BASE = join10(homedir6(), ".claude", "projects");
|
|
3280
3376
|
var CONTEXT_BUDGETS_BY_MODEL = {
|
|
3281
3377
|
"claude-opus-4-7": 2e5,
|
|
3282
3378
|
"claude-opus-4-6": 2e5,
|
|
@@ -3289,7 +3385,7 @@ function encodeProjectPath(cwd) {
|
|
|
3289
3385
|
return cwd.replace(/[/\\]/g, "-");
|
|
3290
3386
|
}
|
|
3291
3387
|
function transcriptPathFor(cwd, sessionId) {
|
|
3292
|
-
return
|
|
3388
|
+
return join10(TRANSCRIPT_BASE, encodeProjectPath(cwd), `${sessionId}.jsonl`);
|
|
3293
3389
|
}
|
|
3294
3390
|
var TranscriptWatcherManager = class {
|
|
3295
3391
|
constructor(db, broadcaster) {
|