@dotobokuri/fleet-console 1.17.1 → 1.19.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.
Files changed (68) hide show
  1. package/AGENTS.md +10 -9
  2. package/dist/cli.d.ts +13 -14
  3. package/dist/cli.mjs +1181 -2428
  4. package/dist/client/assets/SymbolsNerdFontMono-Regular-CoUzE12l.woff2 +0 -0
  5. package/dist/client/assets/{_baseUniq-B7sqIsqm.js → _baseUniq-C4_iQlZS.js} +1 -1
  6. package/dist/client/assets/{arc-CvtYLN8U.js → arc-BBAWxYi5.js} +1 -1
  7. package/dist/client/assets/{architectureDiagram-Q4EWVU46-rlI7pNrR.js → architectureDiagram-Q4EWVU46-_oQqhexV.js} +1 -1
  8. package/dist/client/assets/{blockDiagram-DXYQGD6D-nwx9BNRz.js → blockDiagram-DXYQGD6D-3xT9VFiD.js} +1 -1
  9. package/dist/client/assets/{c4Diagram-AHTNJAMY-CRL_0Ism.js → c4Diagram-AHTNJAMY-S-IMcMW2.js} +1 -1
  10. package/dist/client/assets/channel-5tV1napa.js +1 -0
  11. package/dist/client/assets/{chunk-4BX2VUAB-DEkncDR3.js → chunk-4BX2VUAB-tS9Ys0P9.js} +1 -1
  12. package/dist/client/assets/{chunk-4TB4RGXK-Bnot0Ti1.js → chunk-4TB4RGXK-YZgAG7SW.js} +1 -1
  13. package/dist/client/assets/{chunk-55IACEB6-EgKmMTlI.js → chunk-55IACEB6-m-CtqHoO.js} +1 -1
  14. package/dist/client/assets/{chunk-EDXVE4YY-BKiKnVQE.js → chunk-EDXVE4YY-yoymxaEV.js} +1 -1
  15. package/dist/client/assets/{chunk-FMBD7UC4-BRC398vL.js → chunk-FMBD7UC4-REUkrQAG.js} +1 -1
  16. package/dist/client/assets/{chunk-OYMX7WX6-D82gQaXN.js → chunk-OYMX7WX6-DzMuL105.js} +1 -1
  17. package/dist/client/assets/{chunk-QZHKN3VN-BYdI5fky.js → chunk-QZHKN3VN-DXFFll8-.js} +1 -1
  18. package/dist/client/assets/{chunk-YZCP3GAM-BhBgOoc0.js → chunk-YZCP3GAM-CmY7W-gg.js} +1 -1
  19. package/dist/client/assets/classDiagram-6PBFFD2Q-WDM904ap.js +1 -0
  20. package/dist/client/assets/classDiagram-v2-HSJHXN6E-WDM904ap.js +1 -0
  21. package/dist/client/assets/clone-BJg9p0cC.js +1 -0
  22. package/dist/client/assets/{cose-bilkent-S5V4N54A-J8dfEdWD.js → cose-bilkent-S5V4N54A-BupE-a40.js} +1 -1
  23. package/dist/client/assets/{dagre-KV5264BT-zGd_6_Kv.js → dagre-KV5264BT-Jn67LxMS.js} +1 -1
  24. package/dist/client/assets/{diagram-5BDNPKRD-BQpQGgZ3.js → diagram-5BDNPKRD-BAeFEORH.js} +1 -1
  25. package/dist/client/assets/{diagram-G4DWMVQ6-BbgPVITm.js → diagram-G4DWMVQ6-CnZrDABY.js} +1 -1
  26. package/dist/client/assets/{diagram-MMDJMWI5-lJsMf0Ic.js → diagram-MMDJMWI5-DefG-kXU.js} +1 -1
  27. package/dist/client/assets/{diagram-TYMM5635-BpEKTzOh.js → diagram-TYMM5635-BPkXKB-Q.js} +1 -1
  28. package/dist/client/assets/{erDiagram-SMLLAGMA-CHHVwJ_a.js → erDiagram-SMLLAGMA-Bi4jlild.js} +1 -1
  29. package/dist/client/assets/{flowDiagram-DWJPFMVM-Cun8a4NF.js → flowDiagram-DWJPFMVM-BoZyLO7h.js} +1 -1
  30. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-CGCwH_yf.js → ganttDiagram-T4ZO3ILL-LgzSNjrh.js} +1 -1
  31. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-BkY4qdoV.js → gitGraphDiagram-UUTBAWPF-B4fH-hPT.js} +1 -1
  32. package/dist/client/assets/{graph-HbRzXQtt.js → graph-D3ZAYMLG.js} +1 -1
  33. package/dist/client/assets/index-DctP_CJP.css +1 -0
  34. package/dist/client/assets/index-vgEn_EkG.js +404 -0
  35. package/dist/client/assets/{infoDiagram-42DDH7IO-B21Xtz8v.js → infoDiagram-42DDH7IO-pENdunXd.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DiABngEH.js → ishikawaDiagram-UXIWVN3A-WakLHKte.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-VCZTEJTY-BKZ-Uivu.js → journeyDiagram-VCZTEJTY-CrqXKtzk.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-6JOO6SKY-CoJzTtsH.js → kanban-definition-6JOO6SKY-DdSC9eJ-.js} +1 -1
  39. package/dist/client/assets/{layout-D5tmQpxt.js → layout-BI1Oko_a.js} +1 -1
  40. package/dist/client/assets/{linear-B_riNOXk.js → linear-KUjZtfMe.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-CyRCP3U3.js → mermaid.core-DNlIf4_3.js} +4 -4
  42. package/dist/client/assets/{min-Cn8LZZXB.js → min-DCAecveI.js} +1 -1
  43. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BN_7mZUt.js → mindmap-definition-QFDTVHPH-BSCyQA-4.js} +1 -1
  44. package/dist/client/assets/{pieDiagram-DEJITSTG-DPP2-sYA.js → pieDiagram-DEJITSTG-C_1I63z5.js} +1 -1
  45. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CQ_oo-f9.js → quadrantDiagram-34T5L4WZ-CtqeYPcA.js} +1 -1
  46. package/dist/client/assets/{requirementDiagram-MS252O5E-BkGq7nxD.js → requirementDiagram-MS252O5E-Cpakdg-s.js} +1 -1
  47. package/dist/client/assets/{sankeyDiagram-XADWPNL6-BVcoktBR.js → sankeyDiagram-XADWPNL6-CK_eMH3e.js} +1 -1
  48. package/dist/client/assets/{sequenceDiagram-FGHM5R23-B5rr_VYJ.js → sequenceDiagram-FGHM5R23-D2YygHqF.js} +1 -1
  49. package/dist/client/assets/{stateDiagram-FHFEXIEX-BV8RqzB9.js → stateDiagram-FHFEXIEX-DEDhnbhU.js} +1 -1
  50. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-ji50PgJQ.js +1 -0
  51. package/dist/client/assets/{timeline-definition-GMOUNBTQ-CJ8TM-3t.js → timeline-definition-GMOUNBTQ-Brgny70W.js} +1 -1
  52. package/dist/client/assets/{vennDiagram-DHZGUBPP-BETSq-Pu.js → vennDiagram-DHZGUBPP-B1I5E8Hy.js} +1 -1
  53. package/dist/client/assets/{wardley-RL74JXVD-CyT2qapq.js → wardley-RL74JXVD-P0VCYfKI.js} +1 -1
  54. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-CWHaiUEG.js → wardleyDiagram-NUSXRM2D-DhYC5Sus.js} +1 -1
  55. package/dist/client/assets/{xychartDiagram-5P7HB3ND-oVPlZ_Yn.js → xychartDiagram-5P7HB3ND-ByJX99rJ.js} +1 -1
  56. package/dist/client/index.html +2 -2
  57. package/dist/fleet-plugins/diff/routes.mjs +260 -32
  58. package/dist/fleet-plugins/file-explorer/routes.mjs +1 -1
  59. package/dist/fleet-plugins/skills/routes.mjs +108 -201
  60. package/dist/fleet-plugins/terminal/routes.mjs +821 -1283
  61. package/package.json +1 -1
  62. package/dist/client/assets/channel-Ct9EqqyG.js +0 -1
  63. package/dist/client/assets/classDiagram-6PBFFD2Q-CPMS8Qpo.js +0 -1
  64. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CPMS8Qpo.js +0 -1
  65. package/dist/client/assets/clone-C5XNYXHD.js +0 -1
  66. package/dist/client/assets/index-CfNSyAUJ.css +0 -1
  67. package/dist/client/assets/index-CzCMC0e-.js +0 -402
  68. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Bx_fpQWQ.js +0 -1
@@ -1,19 +1,27 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'fs/promises';
3
- import path from 'path';
4
2
  import { spawn } from 'child_process';
3
+ import 'fs';
4
+ import path3 from 'path';
5
+ import fs from 'fs/promises';
5
6
 
6
7
  // sdk/plugin/node.ts
7
8
  function definePlugin(definition) {
8
9
  return definition;
9
10
  }
10
- function registerRouter(ctx, path3, handler) {
11
- ctx.registerRouter(normalizePath(path3), handler);
11
+ function registerRouter(ctx, path4, handler) {
12
+ ctx.registerRouter(normalizePath(path4), handler);
12
13
  }
13
- function normalizePath(path3) {
14
- if (path3.length === 0 || path3 === "/") return "/";
15
- return path3;
14
+ function normalizePath(path4) {
15
+ if (path4.length === 0 || path4 === "/") return "/";
16
+ return path4;
16
17
  }
18
+
19
+ // ../../packages/core-process/src/hidden.ts
20
+ function withHidden(options) {
21
+ return { ...options ?? {}, windowsHide: true };
22
+ }
23
+
24
+ // ../fleet-plugins/diff/server/git-executor.ts
17
25
  var GitExecutorError = class extends Error {
18
26
  code;
19
27
  exitCode;
@@ -34,7 +42,7 @@ function runGit(args, opts) {
34
42
  return new Promise((resolve, reject) => {
35
43
  let child;
36
44
  try {
37
- child = spawn("git", args, { cwd: opts.cwd, shell: false, windowsHide: true });
45
+ child = spawn("git", args, withHidden({ cwd: opts.cwd, shell: false }));
38
46
  } catch (error) {
39
47
  const code = error.code === "ENOENT" ? "git_unavailable" : "spawn_failed";
40
48
  reject(new GitExecutorError(code, String(error)));
@@ -89,8 +97,6 @@ function runGit(args, opts) {
89
97
  });
90
98
  });
91
99
  }
92
-
93
- // ../fleet-plugins/diff/server/diff.ts
94
100
  function isPlainObject(value) {
95
101
  return typeof value === "object" && value !== null && !Array.isArray(value);
96
102
  }
@@ -141,9 +147,9 @@ function isNoHeadError(error) {
141
147
  }
142
148
  async function resolveGitCwd(theaterPath, rawSubPath) {
143
149
  if (rawSubPath === "") return { gitCwd: theaterPath };
144
- const resolvedSub = path.resolve(theaterPath, rawSubPath);
145
- if (!resolvedSub.startsWith(theaterPath + path.sep) && resolvedSub !== theaterPath) return null;
146
- if (path.normalize(rawSubPath).startsWith("..")) return null;
150
+ const resolvedSub = path3.resolve(theaterPath, rawSubPath);
151
+ if (!resolvedSub.startsWith(theaterPath + path3.sep) && resolvedSub !== theaterPath) return null;
152
+ if (path3.normalize(rawSubPath).startsWith("..")) return null;
147
153
  let realTheater;
148
154
  let realSub;
149
155
  try {
@@ -154,7 +160,7 @@ async function resolveGitCwd(theaterPath, rawSubPath) {
154
160
  } catch {
155
161
  return null;
156
162
  }
157
- const norm = realTheater.endsWith(path.sep) ? realTheater : realTheater + path.sep;
163
+ const norm = realTheater.endsWith(path3.sep) ? realTheater : realTheater + path3.sep;
158
164
  if (realSub !== realTheater && !realSub.startsWith(norm)) return null;
159
165
  return { gitCwd: resolvedSub };
160
166
  }
@@ -263,12 +269,12 @@ async function handleDiffFile(req, res, ctx) {
263
269
  }
264
270
  const { gitCwd } = cwdResult;
265
271
  const rawFilePath = body.filePath;
266
- const resolvedFilePath = path.resolve(gitCwd, rawFilePath);
267
- if (!resolvedFilePath.startsWith(gitCwd + path.sep) && resolvedFilePath !== gitCwd) {
272
+ const resolvedFilePath = path3.resolve(gitCwd, rawFilePath);
273
+ if (!resolvedFilePath.startsWith(gitCwd + path3.sep) && resolvedFilePath !== gitCwd) {
268
274
  ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
269
275
  return;
270
276
  }
271
- const relativePath = path.normalize(rawFilePath);
277
+ const relativePath = path3.normalize(rawFilePath);
272
278
  if (relativePath.startsWith("..")) {
273
279
  ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
274
280
  return;
@@ -286,7 +292,7 @@ async function handleDiffFile(req, res, ctx) {
286
292
  ctx.host.http.writeJson(res, 404, { error: "file_not_found" });
287
293
  return;
288
294
  }
289
- if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path.sep)) {
295
+ if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path3.sep)) {
290
296
  ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
291
297
  return;
292
298
  }
@@ -302,7 +308,7 @@ async function handleDiffFile(req, res, ctx) {
302
308
  fs.realpath(gitCwd),
303
309
  fs.realpath(resolvedFilePath)
304
310
  ]);
305
- if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path.sep)) {
311
+ if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path3.sep)) {
306
312
  ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
307
313
  return;
308
314
  }
@@ -328,10 +334,169 @@ async function handleDiffFile(req, res, ctx) {
328
334
  throw error;
329
335
  }
330
336
  }
337
+
338
+ // ../fleet-plugins/diff/server/commit.ts
339
+ var REF_RE = /^[0-9a-f]{7,40}$/i;
340
+ function isPlainObject2(value) {
341
+ return typeof value === "object" && value !== null && !Array.isArray(value);
342
+ }
343
+ async function handleDiffCommit(req, res, ctx) {
344
+ if (req.method !== "POST") {
345
+ ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
346
+ return;
347
+ }
348
+ if (!ctx.host.security.isTerminalAuthorized(req)) {
349
+ ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
350
+ return;
351
+ }
352
+ const body = await ctx.host.http.readJsonBody(req);
353
+ if (!isPlainObject2(body)) {
354
+ ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
355
+ return;
356
+ }
357
+ const theaterId = body.theaterId;
358
+ if (typeof theaterId !== "string") {
359
+ ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
360
+ return;
361
+ }
362
+ const ref = body.ref;
363
+ if (typeof ref !== "string" || !REF_RE.test(ref)) {
364
+ ctx.host.http.writeJson(res, 400, { error: "invalid_ref" });
365
+ return;
366
+ }
367
+ const theaterPath = ctx.host.paths.resolveTheaterPath(theaterId);
368
+ if (!theaterPath) {
369
+ ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
370
+ return;
371
+ }
372
+ const rawSubPath = typeof body.subPath === "string" ? body.subPath : "";
373
+ const cwdResult = await resolveGitCwd(theaterPath, rawSubPath);
374
+ if (!cwdResult) {
375
+ ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
376
+ return;
377
+ }
378
+ const { gitCwd } = cwdResult;
379
+ try {
380
+ const result = await runGit(["show", ref, "--unified=3", "--format="], { cwd: gitCwd });
381
+ ctx.host.http.writeJson(res, 200, { content: result.stdout, ...result.truncated ? { truncated: true } : {} });
382
+ } catch (error) {
383
+ if (error instanceof GitExecutorError) {
384
+ if (error.code === "no_git_repo" || error.code === "git_unavailable") {
385
+ ctx.host.http.writeJson(res, 422, { error: error.code });
386
+ return;
387
+ }
388
+ ctx.host.http.writeJson(res, 500, { error: "git_failed" });
389
+ return;
390
+ }
391
+ throw error;
392
+ }
393
+ }
394
+
395
+ // ../fleet-plugins/diff/server/log.ts
396
+ function isPlainObject3(value) {
397
+ return typeof value === "object" && value !== null && !Array.isArray(value);
398
+ }
399
+ function parseLogOutput(stdout) {
400
+ if (!stdout.trim()) return [];
401
+ const commits = [];
402
+ for (const chunk of stdout.split("")) {
403
+ const trimmed = chunk.trimStart();
404
+ if (!trimmed) continue;
405
+ const lines = trimmed.split("\n");
406
+ const firstLine = lines[0] ?? "";
407
+ const fields = firstLine.split("\0");
408
+ const fullHash = fields[0] ?? "";
409
+ const shortHash = fields[1] ?? "";
410
+ const subject = fields[2] ?? "";
411
+ const authorName = fields[3] ?? "";
412
+ const relTime = fields[4] ?? "";
413
+ const refsRaw = fields[5] ?? "";
414
+ const parentsRaw = fields[6] ?? "";
415
+ if (!fullHash) continue;
416
+ const refs = refsRaw.split(",").map((r) => r.trim()).filter(Boolean);
417
+ const parents = parentsRaw.split(" ").map((p) => p.trim()).filter(Boolean);
418
+ let additions = 0;
419
+ let deletions = 0;
420
+ for (const line of lines.slice(1)) {
421
+ if (!line.trim()) continue;
422
+ const parts = line.split(" ");
423
+ if (parts.length < 2) continue;
424
+ const a = parseInt(parts[0] ?? "0", 10);
425
+ const d = parseInt(parts[1] ?? "0", 10);
426
+ if (!isNaN(a)) additions += a;
427
+ if (!isNaN(d)) deletions += d;
428
+ }
429
+ commits.push({ shortHash, fullHash, subject, authorName, relTime, refs, parents, additions, deletions });
430
+ }
431
+ return commits;
432
+ }
433
+ function isNoHeadError2(error) {
434
+ if (!(error instanceof GitExecutorError)) return false;
435
+ if (error.code !== "non_zero_exit") return false;
436
+ return error.stderr.includes("unknown revision") || error.stderr.includes("bad revision");
437
+ }
438
+ async function handleDiffLog(req, res, ctx) {
439
+ if (req.method !== "POST") {
440
+ ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
441
+ return;
442
+ }
443
+ if (!ctx.host.security.isTerminalAuthorized(req)) {
444
+ ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
445
+ return;
446
+ }
447
+ const body = await ctx.host.http.readJsonBody(req);
448
+ if (!isPlainObject3(body)) {
449
+ ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
450
+ return;
451
+ }
452
+ const theaterId = body.theaterId;
453
+ if (typeof theaterId !== "string") {
454
+ ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
455
+ return;
456
+ }
457
+ const theaterPath = ctx.host.paths.resolveTheaterPath(theaterId);
458
+ if (!theaterPath) {
459
+ ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
460
+ return;
461
+ }
462
+ const rawSubPath = typeof body.subPath === "string" ? body.subPath : "";
463
+ const cwdResult = await resolveGitCwd(theaterPath, rawSubPath);
464
+ if (!cwdResult) {
465
+ ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
466
+ return;
467
+ }
468
+ const { gitCwd } = cwdResult;
469
+ try {
470
+ const result = await runGit(
471
+ ["log", "-n", "50", "--numstat", "--pretty=format:%x1e%H%x00%h%x00%s%x00%an%x00%ar%x00%D%x00%P"],
472
+ { cwd: gitCwd }
473
+ );
474
+ const commits = parseLogOutput(result.stdout);
475
+ ctx.host.http.writeJson(res, 200, { commits, ...result.truncated ? { truncated: true } : {} });
476
+ } catch (error) {
477
+ if (error instanceof GitExecutorError) {
478
+ if (error.code === "no_git_repo") {
479
+ ctx.host.http.writeJson(res, 200, { commits: [] });
480
+ return;
481
+ }
482
+ if (error.code === "git_unavailable") {
483
+ ctx.host.http.writeJson(res, 422, { error: error.code });
484
+ return;
485
+ }
486
+ if (isNoHeadError2(error)) {
487
+ ctx.host.http.writeJson(res, 200, { commits: [] });
488
+ return;
489
+ }
490
+ ctx.host.http.writeJson(res, 500, { error: "git_failed" });
491
+ return;
492
+ }
493
+ throw error;
494
+ }
495
+ }
331
496
  var REPOS_CAP = 200;
332
497
  var HARD_CAP_DEPTH = 8;
333
498
  var DEFAULT_DEPTH = 3;
334
- function isPlainObject2(value) {
499
+ function isPlainObject4(value) {
335
500
  return typeof value === "object" && value !== null && !Array.isArray(value);
336
501
  }
337
502
  async function resolveRepoBranch(repoDir) {
@@ -347,15 +512,49 @@ async function resolveRepoBranch(repoDir) {
347
512
  return "unknown";
348
513
  }
349
514
  }
515
+ async function detectWorktreeParent(dir, gitFileContent) {
516
+ const match = gitFileContent.trim().match(/^gitdir:\s*(.+)$/m);
517
+ if (!match) return null;
518
+ const rawPointer = match[1].trim();
519
+ const resolvedGitdir = path3.normalize(
520
+ path3.isAbsolute(rawPointer) ? rawPointer : path3.resolve(dir, rawPointer)
521
+ );
522
+ if (resolvedGitdir.includes(`${path3.sep}modules${path3.sep}`)) {
523
+ return null;
524
+ }
525
+ const worktreesSegment = `${path3.sep}.git${path3.sep}worktrees${path3.sep}`;
526
+ const wtIdx = resolvedGitdir.indexOf(worktreesSegment);
527
+ if (wtIdx === -1) {
528
+ return null;
529
+ }
530
+ const parentAbsRaw = resolvedGitdir.slice(0, wtIdx);
531
+ let parentAbs;
532
+ try {
533
+ parentAbs = await fs.realpath(parentAbsRaw);
534
+ } catch {
535
+ parentAbs = path3.normalize(parentAbsRaw);
536
+ }
537
+ return { isWorktree: true, parentAbs };
538
+ }
350
539
  async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDepth, repos, cap = REPOS_CAP) {
351
540
  if (repos.length >= cap) return true;
352
- const gitPath = path.join(dir, ".git");
541
+ const gitPath = path3.join(dir, ".git");
353
542
  try {
354
- await fs.stat(gitPath);
355
- const relPath = path.relative(theaterPath, dir);
356
- const name = relPath === "" ? path.basename(theaterPath) : path.basename(dir);
543
+ const gitStat = await fs.stat(gitPath);
544
+ const relPath = path3.relative(theaterPath, dir);
545
+ const name = relPath === "" ? path3.basename(theaterPath) : path3.basename(dir);
357
546
  const branch = await resolveRepoBranch(dir);
358
- repos.push({ relPath, name, branch });
547
+ if (gitStat.isFile()) {
548
+ const content = await fs.readFile(gitPath, "utf8");
549
+ const worktreeResult = await detectWorktreeParent(dir, content);
550
+ if (worktreeResult) {
551
+ repos.push({ relPath, name, branch, isWorktree: true, _wtParentAbs: worktreeResult.parentAbs });
552
+ } else {
553
+ repos.push({ relPath, name, branch });
554
+ }
555
+ } else {
556
+ repos.push({ relPath, name, branch });
557
+ }
359
558
  if (repos.length >= cap) return true;
360
559
  } catch {
361
560
  }
@@ -373,7 +572,7 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
373
572
  dirent = await dirHandle.read();
374
573
  continue;
375
574
  }
376
- const childPath = path.join(dir, childName);
575
+ const childPath = path3.join(dir, childName);
377
576
  let realChildPath;
378
577
  try {
379
578
  realChildPath = await fs.realpath(childPath);
@@ -381,7 +580,7 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
381
580
  dirent = await dirHandle.read();
382
581
  continue;
383
582
  }
384
- const normalizedReal = realTheaterPath.endsWith(path.sep) ? realTheaterPath : realTheaterPath + path.sep;
583
+ const normalizedReal = realTheaterPath.endsWith(path3.sep) ? realTheaterPath : realTheaterPath + path3.sep;
385
584
  if (realChildPath !== realTheaterPath && !realChildPath.startsWith(normalizedReal)) {
386
585
  dirent = await dirHandle.read();
387
586
  continue;
@@ -405,6 +604,26 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
405
604
  return false;
406
605
  }
407
606
  }
607
+ function resolveWorktreeParents(raw, realTheaterPath) {
608
+ const absToRelPath = /* @__PURE__ */ new Map();
609
+ for (const entry of raw) {
610
+ const absDir = path3.normalize(
611
+ entry.relPath === "" ? realTheaterPath : path3.join(realTheaterPath, entry.relPath)
612
+ );
613
+ absToRelPath.set(absDir, entry.relPath);
614
+ }
615
+ return raw.map((entry) => {
616
+ const { _wtParentAbs, ...rest } = entry;
617
+ if (_wtParentAbs && entry.isWorktree) {
618
+ const normalizedParent = path3.normalize(_wtParentAbs);
619
+ const parentRelPath = absToRelPath.get(normalizedParent);
620
+ if (parentRelPath !== void 0) {
621
+ return { ...rest, worktreeOf: parentRelPath };
622
+ }
623
+ }
624
+ return rest;
625
+ });
626
+ }
408
627
  async function handleDiffRepos(req, res, ctx) {
409
628
  if (req.method !== "POST") {
410
629
  ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
@@ -415,7 +634,7 @@ async function handleDiffRepos(req, res, ctx) {
415
634
  return;
416
635
  }
417
636
  const body = await ctx.host.http.readJsonBody(req);
418
- if (!isPlainObject2(body)) {
637
+ if (!isPlainObject4(body)) {
419
638
  ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
420
639
  return;
421
640
  }
@@ -438,14 +657,15 @@ async function handleDiffRepos(req, res, ctx) {
438
657
  ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
439
658
  return;
440
659
  }
441
- const repos = [];
442
- const truncated = await scanRepos(theaterPath, realTheaterPath, theaterPath, 0, maxDepth, repos);
443
- repos.sort((a, b) => {
660
+ const raw = [];
661
+ const truncated = await scanRepos(theaterPath, realTheaterPath, theaterPath, 0, maxDepth, raw);
662
+ const resolved = resolveWorktreeParents(raw, realTheaterPath);
663
+ resolved.sort((a, b) => {
444
664
  if (a.relPath === "") return -1;
445
665
  if (b.relPath === "") return 1;
446
666
  return a.relPath.localeCompare(b.relPath);
447
667
  });
448
- const result = { repos, ...truncated ? { truncated: true } : {} };
668
+ const result = { repos: resolved, ...truncated ? { truncated: true } : {} };
449
669
  ctx.host.http.writeJson(res, 200, result);
450
670
  }
451
671
 
@@ -465,6 +685,14 @@ var routes_default = definePlugin({
465
685
  await handleDiffFile(req, res, ctx);
466
686
  return true;
467
687
  });
688
+ registerRouter(ctx, "log", async ({ req, res }) => {
689
+ await handleDiffLog(req, res, ctx);
690
+ return true;
691
+ });
692
+ registerRouter(ctx, "commit", async ({ req, res }) => {
693
+ await handleDiffCommit(req, res, ctx);
694
+ return true;
695
+ });
468
696
  }
469
697
  });
470
698
 
@@ -243,7 +243,7 @@ var ImageServeError = class extends Error {
243
243
  this.code = code;
244
244
  }
245
245
  };
246
- var IMAGE_SIZE_CAP = 4 * 1024 * 1024;
246
+ var IMAGE_SIZE_CAP = 16 * 1024 * 1024;
247
247
  var MIME_ALLOWLIST = {
248
248
  ".png": "image/png",
249
249
  ".jpg": "image/jpeg",