@paradow/ledge 0.8.0 → 0.9.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.
@@ -1,25 +1,26 @@
1
1
  import {
2
- QUESTION_TOOLS
3
- } from "./chunk-Y5TCZTFL.js";
2
+ QUESTION_TOOLS,
3
+ gitInfo
4
+ } from "./chunk-JVELEEIO.js";
4
5
  import {
5
6
  CODEX_ENV_VAR,
6
7
  CODEX_HOOK_VAR,
7
8
  codexPlan
8
- } from "./chunk-ZPANTZKC.js";
9
+ } from "./chunk-564TJJZ6.js";
9
10
  import {
10
11
  log
11
12
  } from "./chunk-L6IGH4LF.js";
12
13
  import {
13
14
  connect
14
- } from "./chunk-6ZKNEJND.js";
15
- import "./chunk-KJWMK65Y.js";
15
+ } from "./chunk-XPWGZIYL.js";
16
+ import "./chunk-WM4MAV46.js";
16
17
 
17
18
  // src/cli/hook.ts
18
19
  import { readFileSync, openSync, fstatSync, readSync, closeSync } from "fs";
19
20
  import { basename, join } from "path";
20
21
  var str = (v) => typeof v === "string" ? v : void 0;
21
22
  var PROMPT_MAX = 500;
22
- var fromAgent = (t) => t.startsWith('<agent-message from="') || t.startsWith("<cross-session-message ");
23
+ var fromAgent = (t) => t.startsWith('<agent-message from="') || t.startsWith("<cross-session-message ") || t.startsWith("[Cross-session ");
23
24
  var promptText = (p) => {
24
25
  const t = str(p.prompt)?.trim();
25
26
  return t ? { text: t.slice(0, fromAgent(t) ? INPUT_MAX : PROMPT_MAX) } : {};
@@ -300,34 +301,68 @@ function mapCursorEvent(eventName, p, at, launched, transcript) {
300
301
  return null;
301
302
  }
302
303
  }
304
+ function identityContext(s) {
305
+ const where = [`projet ${s.repo ?? s.project}`, ...s.branch ? [`branche ${s.branch}`] : [], ...s.worktree ? [`worktree ${basename(s.cwd) || s.cwd}`] : []].join(", ");
306
+ return [
307
+ `Tu es ${s.name}, ${where} : une session lanc\xE9e par Ledge (${s.tool}). Les autres sessions Ledge te connaissent sous le nom ${s.name}.`,
308
+ "Pour voir les autres sessions (nom, outil, \xE9tat, projet, branche, sujet), lance `ledge who`.",
309
+ ...s.tool === "claude" ? ["Pour \xE9crire \xE0 une autre session Ledge, utilise son nom Ledge comme adresse SendMessage."] : []
310
+ ].join("\n");
311
+ }
312
+ async function identity(agent, cwd, known, deadline) {
313
+ const name = known?.name ?? process.env.LEDGE_NAME;
314
+ const dir = known?.cwd ?? cwd;
315
+ if (!name || !dir) return void 0;
316
+ const git = known?.branch ? null : await Promise.race([gitInfo(dir).catch(() => null), new Promise((r) => setTimeout(() => r(null), Math.max(0, deadline - Date.now())))]);
317
+ return {
318
+ name,
319
+ tool: known?.tool ?? agent,
320
+ cwd: dir,
321
+ project: known?.project ?? (basename(dir) || dir),
322
+ ...known?.repo ?? git?.repo ? { repo: known?.repo ?? git.repo } : {},
323
+ ...known?.branch ?? git?.branch ? { branch: known?.branch ?? git.branch } : {},
324
+ ...known?.worktree ?? git?.worktree ? { worktree: true } : {}
325
+ };
326
+ }
303
327
  async function runHook(eventName, stdin, opts = {}) {
304
- const timeoutMs = opts.timeoutMs ?? 100;
305
- const deadline = Date.now() + timeoutMs;
306
328
  const agent = opts.tool ?? "claude";
307
329
  const label = agent === "claude" ? eventName : `${agent} ${eventName}`;
308
330
  if (process.env.LEDGE_CHAT === "1") {
309
331
  log(`hook ${label} ignored: Ledge chat`);
310
- return;
332
+ return void 0;
311
333
  }
334
+ let mapped = null;
335
+ let identify = false;
336
+ let known;
337
+ let deadline = Date.now() + (opts.timeoutMs ?? 100);
312
338
  try {
313
339
  const payload = JSON.parse(stdin || "{}");
314
- const mapped = agent === "cursor" ? mapCursorHook(eventName, payload, (/* @__PURE__ */ new Date()).toISOString()) : mapHook(agent, eventName, payload, (/* @__PURE__ */ new Date()).toISOString());
340
+ mapped = agent === "cursor" ? mapCursorHook(eventName, payload, (/* @__PURE__ */ new Date()).toISOString()) : mapHook(agent, eventName, payload, (/* @__PURE__ */ new Date()).toISOString());
315
341
  if (!mapped) {
316
342
  log(`hook ${label} ignored: unknown event, malformed payload, non-terminal SessionEnd or subagent event without a session of its own`);
317
- return;
343
+ return void 0;
318
344
  }
345
+ const launchedId = agent === "codex" ? codexLedgeId() : agent === "claude" ? process.env.LEDGE_SESSION : void 0;
346
+ identify = eventName === "SessionStart" && !!launchedId && mapped.id === launchedId;
347
+ const timeoutMs = opts.timeoutMs ?? (identify ? 1e3 : 100);
348
+ deadline = Date.now() + timeoutMs;
319
349
  const client = await connect({ port: opts.port, timeoutMs });
320
350
  client.send({ type: "session.event", id: mapped.id, event: mapped.event, agent: mapped.agent, ...mapped.cwd ? { cwd: mapped.cwd } : {} });
351
+ if (identify) known = (await client.next("snapshot", Math.max(0, deadline - Date.now())).catch(() => void 0))?.sessions.find((s) => s.id === mapped.id);
321
352
  await Promise.race([client.close(), new Promise((r) => setTimeout(r, Math.max(0, deadline - Date.now())))]);
322
353
  } catch (e) {
323
354
  log(`hook ${label} dropped: ${e.message}`);
324
355
  }
356
+ if (!identify || !mapped) return void 0;
357
+ const who = await identity(agent, mapped.cwd, known, deadline);
358
+ return who ? JSON.stringify({ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: identityContext(who) } }) : void 0;
325
359
  }
326
360
  export {
327
361
  INPUT_MAX,
328
362
  REPLY_MAX,
329
363
  codexLedgeId,
330
364
  cursorReply,
365
+ identityContext,
331
366
  mapClaudeHook,
332
367
  mapCursorHook,
333
368
  mapHook,
package/dist/cli/index.js CHANGED
@@ -8,6 +8,7 @@ ledge hook [codex|cursor] <Event> receive a hook (rea
8
8
  ledge setup install hooks and ~/.ledge
9
9
  ledge uninstall [--purge] remove hooks, service and link (--purge: ~/.ledge too)
10
10
  ledge status list sessions
11
+ ledge who list the live sessions (name, tool, state, project, branch, topic)
11
12
  ledge bridge exchange Ledge messages over SSH stdio
12
13
  ledge --version print the installed version`;
13
14
  async function hookMain(args) {
@@ -16,8 +17,10 @@ async function hookMain(args) {
16
17
  try {
17
18
  const chunks = [];
18
19
  for await (const c of process.stdin) chunks.push(c);
19
- const { runHook } = await import("./hook-BBVUY4RK.js");
20
- await runHook(event, Buffer.concat(chunks).toString("utf8"), { tool });
20
+ const { runHook } = await import("./hook-WQYL4YFH.js");
21
+ const out = await runHook(event, Buffer.concat(chunks).toString("utf8"), { tool });
22
+ if (out) process.stdout.write(`${out}
23
+ `);
21
24
  } catch {
22
25
  }
23
26
  if (tool === "cursor") process.stdout.write("{}\n");
@@ -26,7 +29,7 @@ async function main(argv) {
26
29
  const [cmd, ...rest] = argv;
27
30
  switch (cmd) {
28
31
  case "daemon": {
29
- const { startDaemon } = await import("./daemon-SRNTDIZW.js");
32
+ const { startDaemon } = await import("./daemon-SGTQRTHV.js");
30
33
  await startDaemon();
31
34
  await new Promise(() => {
32
35
  });
@@ -34,7 +37,7 @@ async function main(argv) {
34
37
  }
35
38
  case "--version":
36
39
  case "version": {
37
- const { LEDGE_VERSION } = await import("./version-M6IZZGUV.js");
40
+ const { LEDGE_VERSION } = await import("./version-XFBINYKQ.js");
38
41
  console.log(LEDGE_VERSION);
39
42
  return 0;
40
43
  }
@@ -50,23 +53,28 @@ async function main(argv) {
50
53
  return r.ok ? 0 : 1;
51
54
  }
52
55
  case "status": {
53
- const { printStatus } = await import("./status-L2GROM4J.js");
56
+ const { printStatus } = await import("./status-GGXF4DSQ.js");
54
57
  console.log(await printStatus());
55
58
  return 0;
56
59
  }
60
+ case "who": {
61
+ const { printWho } = await import("./who-EJWN7HM6.js");
62
+ console.log(await printWho());
63
+ return 0;
64
+ }
57
65
  case "bridge": {
58
- const { bridge } = await import("./bridge-QKQW7UD6.js");
66
+ const { bridge } = await import("./bridge-2CVD7BMI.js");
59
67
  await bridge();
60
68
  return 0;
61
69
  }
62
70
  case "claude":
63
71
  case "codex":
64
72
  case "cursor": {
65
- const { launch } = await import("./launch-RWT4EWWI.js");
73
+ const { launch } = await import("./launch-I4T5MFWF.js");
66
74
  return launch(cmd, rest);
67
75
  }
68
76
  case "attach": {
69
- const { attach } = await import("./attach-KTZOG7E5.js");
77
+ const { attach } = await import("./attach-HPGWEUKX.js");
70
78
  return attach(rest[0] ?? "");
71
79
  }
72
80
  default:
@@ -5,15 +5,15 @@ import {
5
5
  ensureDaemon,
6
6
  launch,
7
7
  parseLaunchArgs
8
- } from "./chunk-7AKUPFMS.js";
8
+ } from "./chunk-N3MPPCON.js";
9
9
  import {
10
10
  agentEnv,
11
11
  exitFile
12
- } from "./chunk-ZPANTZKC.js";
12
+ } from "./chunk-564TJJZ6.js";
13
13
  import "./chunk-3TNPEAMR.js";
14
14
  import "./chunk-L6IGH4LF.js";
15
- import "./chunk-6ZKNEJND.js";
16
- import "./chunk-KJWMK65Y.js";
15
+ import "./chunk-XPWGZIYL.js";
16
+ import "./chunk-WM4MAV46.js";
17
17
  export {
18
18
  DaemonError,
19
19
  agentEnv,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  connect
3
- } from "./chunk-6ZKNEJND.js";
4
- import "./chunk-KJWMK65Y.js";
3
+ } from "./chunk-XPWGZIYL.js";
4
+ import "./chunk-WM4MAV46.js";
5
5
 
6
6
  // src/cli/status.ts
7
7
  async function printStatus(opts = {}) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  LEDGE_VERSION
3
- } from "./chunk-3KKTSYHP.js";
3
+ } from "./chunk-LWPKX7SB.js";
4
4
  export {
5
5
  LEDGE_VERSION
6
6
  };
@@ -0,0 +1,42 @@
1
+ import {
2
+ connect
3
+ } from "./chunk-XPWGZIYL.js";
4
+ import {
5
+ isArchived
6
+ } from "./chunk-WM4MAV46.js";
7
+
8
+ // src/cli/who.ts
9
+ import { basename } from "path";
10
+ function formatWho(sessions, self) {
11
+ const live = sessions.filter((s) => s.state !== "exited" && !isArchived(s));
12
+ if (live.length === 0) return "aucune autre session";
13
+ const rows = live.map((s) => [
14
+ s.id === self ? "*" : " ",
15
+ s.name,
16
+ s.tool,
17
+ s.state,
18
+ `${s.repo ?? s.project}${s.worktree ? ` (${basename(s.cwd)})` : ""}`,
19
+ s.branch ?? "-",
20
+ `${s.topic ?? "-"}${s.source === "hook_only" ? " \xB7 lecture seule" : ""}`
21
+ ]);
22
+ const head = [" ", "NOM", "OUTIL", "\xC9TAT", "PROJET", "BRANCHE", "SUJET"];
23
+ const widths = head.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
24
+ return [head, ...rows].map((r) => r.map((c, i) => i === r.length - 1 ? c : c.padEnd(widths[i])).join(" ")).join("\n");
25
+ }
26
+ async function printWho(opts = {}) {
27
+ let client;
28
+ try {
29
+ client = await connect({ port: opts.port, timeoutMs: 800 });
30
+ } catch {
31
+ return "daemon not running (start a session with `ledge claude`, or run `ledge daemon`)";
32
+ }
33
+ try {
34
+ return formatWho((await client.next("snapshot")).sessions, opts.self ?? process.env.LEDGE_SESSION);
35
+ } finally {
36
+ await client.close();
37
+ }
38
+ }
39
+ export {
40
+ formatWho,
41
+ printWho
42
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paradow/ledge",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Follow and answer your WSL agent sessions (Claude Code, Codex, Cursor Agent) from the Ledge island on Windows.",
5
5
  "type": "module",
6
6
  "bin": { "ledge": "dist/cli/index.js" },
@@ -1,6 +0,0 @@
1
- // src/version.ts
2
- var LEDGE_VERSION = true ? "0.8.0" : "0.0.0-dev";
3
-
4
- export {
5
- LEDGE_VERSION
6
- };