@paradow/ledge 0.17.0 → 0.18.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/cli/{attach-EYIX7FZN.js → attach-I6TTCGNV.js} +12 -7
- package/dist/cli/{bridge-6UHCD76N.js → bridge-APNFBPNE.js} +2 -2
- package/dist/cli/chunk-3EIRKAX7.js +165 -0
- package/dist/cli/chunk-3HCID6NW.js +145 -0
- package/dist/cli/{chunk-JQZHXVIZ.js → chunk-43D6NMYH.js} +1 -1
- package/dist/cli/{daemon-QEJLAIBT.js → chunk-7IQ6FV2F.js} +6044 -7227
- package/dist/cli/{chunk-ELLXOV4Q.js → chunk-7ZAA75HG.js} +1 -1
- package/dist/cli/{chunk-5ZCHFGCV.js → chunk-BVBX222F.js} +9 -7
- package/dist/cli/{chunk-OVJL2QTN.js → chunk-FQZ3IX6E.js} +2 -2
- package/dist/cli/chunk-G6K2NSQS.js +6 -0
- package/dist/cli/{chunk-VVK4I6B5.js → chunk-GXFU4XAX.js} +1 -1
- package/dist/cli/chunk-HP3G2TYJ.js +1772 -0
- package/dist/cli/{chunk-NC5YAEJB.js → chunk-KKNE7BFZ.js} +55 -3
- package/dist/cli/{chunk-36KJUV62.js → chunk-QQZALYNI.js} +38 -12
- package/dist/cli/{chunk-VE4LGNXX.js → chunk-TRTS24PF.js} +23 -5
- package/dist/cli/{chunk-Q7YNLP66.js → chunk-WCS24ICX.js} +5 -3
- package/dist/cli/{chunk-LAUNHDTX.js → chunk-WJ6YMZTT.js} +2 -2
- package/dist/cli/chunk-Y5DJ5INP.js +60 -0
- package/dist/cli/{chunk-AQ25HFBP.js → chunk-ZUYOA55W.js} +5 -4
- package/dist/cli/daemon-SKC7VKYQ.js +2240 -0
- package/dist/cli/handover-UXISKYPS.js +16 -0
- package/dist/cli/{hook-AE5HC6AS.js → hook-5UAARBWU.js} +86 -25
- package/dist/cli/index.js +29 -15
- package/dist/cli/launch-T5FJ42XS.js +34 -0
- package/dist/cli/{send-2OZ3YZRV.js → send-NLVR6PL2.js} +5 -5
- package/dist/cli/{setup-2VM4GCDJ.js → setup-EEIXRAMU.js} +4 -4
- package/dist/cli/{status-3VJAO6UL.js → status-2OCYHOWZ.js} +3 -3
- package/dist/cli/{task-PMKNYP6M.js → task-AOOWHPMN.js} +5 -5
- package/dist/cli/team-TVNSVYWW.js +101 -0
- package/dist/cli/{tui-DWA2EDC2.js → tui-MQLMUUJB.js} +866 -113
- package/dist/cli/{uninstall-AKUWE33D.js → uninstall-UUN7LIIT.js} +4 -4
- package/dist/cli/{version-WOGCIZY3.js → version-AEN2CRT2.js} +1 -1
- package/dist/cli/who-52MENZTT.js +11 -0
- package/package.json +1 -1
- package/dist/cli/chunk-QD27W6W5.js +0 -1474
- package/dist/cli/chunk-V2H7UD2N.js +0 -6
- package/dist/cli/launch-B4G5OFBV.js +0 -27
- package/dist/cli/who-37TNDYVA.js +0 -11
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ledgeTitleRest
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TRTS24PF.js";
|
|
4
4
|
import {
|
|
5
5
|
cleanTopic,
|
|
6
|
+
isArchived,
|
|
6
7
|
sessionTitle
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-HP3G2TYJ.js";
|
|
9
|
+
|
|
10
|
+
// src/daemon/seat.ts
|
|
11
|
+
function teammates(team, sessions) {
|
|
12
|
+
const byId = new Map(sessions.map((s) => [s.id, s]));
|
|
13
|
+
return team.seats.flatMap((seat) => {
|
|
14
|
+
const s = seat.sessionId ? byId.get(seat.sessionId) : void 0;
|
|
15
|
+
return s && s.state !== "exited" && !isArchived(s) ? [{ name: s.name, role: seat.role }] : [];
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function seatContext(o) {
|
|
19
|
+
const id = o.role?.id ?? o.roleId;
|
|
20
|
+
const label = o.role?.label ?? o.roleLabel ?? id;
|
|
21
|
+
const lines = [];
|
|
22
|
+
if (id) lines.push(`Ton r\xF4le : ${label} (${id})${o.team ? `, dans l'\xE9quipe ${o.team.name}` : ""}.`);
|
|
23
|
+
if (o.role?.prompt.trim()) lines.push(o.role.prompt.trim());
|
|
24
|
+
if (o.team) {
|
|
25
|
+
const members = o.team.members.map((m) => `${m.name} (${m.role})`).join(", ") || "personne d'autre pour l'instant";
|
|
26
|
+
lines.push(
|
|
27
|
+
`Ton \xE9quipe : ${members}. \xC9cris-leur par leur r\xF4le : ledge send @build "\u2026", ledge task give @qa "\u2026".`,
|
|
28
|
+
`L'op\xE9rateur est Ledge : il t'a confi\xE9 ou te confiera ton travail par une <ledge-task>. Travaille en autonomie avec ton \xE9quipe ; ne t'arr\xEAte pour l'utilisateur que pour une d\xE9cision qui lui revient. Termine toujours une t\xE2che re\xE7ue (ledge task done <id> "<r\xE9sultat>", ou refuse) : c'est ainsi que Ledge et l'utilisateur savent o\xF9 en est l'\xE9quipe.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return lines.join("\n");
|
|
32
|
+
}
|
|
33
|
+
function resolveRoleAddress(to, from, sessions, teams) {
|
|
34
|
+
if (!to.startsWith("@")) return { to };
|
|
35
|
+
const role = to.slice(1);
|
|
36
|
+
const team = teams.find((t) => t.state !== "dissolved" && t.seats.some((s) => s.sessionId === from));
|
|
37
|
+
if (!team) return { reason: `${to} n'existe que dans une \xE9quipe` };
|
|
38
|
+
const byId = new Map(sessions.map((s) => [s.id, s]));
|
|
39
|
+
const held = team.seats.filter((s) => s.role === role && s.sessionId).map((s) => byId.get(s.sessionId)).filter((s) => !!s && s.state !== "exited" && !isArchived(s));
|
|
40
|
+
if (held.length === 1) return { to: held[0].name };
|
|
41
|
+
if (held.length > 1) return { reason: `plusieurs sessions tiennent ${to} dans ${team.name} : ${held.map((s) => s.name).join(", ")}` };
|
|
42
|
+
const roles = teammates(team, sessions).map((m) => `@${m.role}`);
|
|
43
|
+
return { reason: `personne ne tient ${to} dans l'\xE9quipe ${team.name}${roles.length ? ` (r\xF4les tenus : ${roles.join(", ")})` : ""}` };
|
|
44
|
+
}
|
|
8
45
|
|
|
9
46
|
// src/daemon/crossSession.ts
|
|
10
47
|
import { createHash } from "crypto";
|
|
@@ -71,6 +108,16 @@ function formatLedgeNotice(n) {
|
|
|
71
108
|
const quote = ledgeBody(flat.length > NOTICE_QUOTE ? `${flat.slice(0, NOTICE_QUOTE)}\u2026` : flat);
|
|
72
109
|
return `<ledge-notice to="${to}">Ledge n'a pas pu remettre ton message \xE0 ${to} (\xAB ${quote}${NOTICE_REASON}${ledgeBody(n.reason)}</ledge-notice>`;
|
|
73
110
|
}
|
|
111
|
+
function formatTeamNotice(n) {
|
|
112
|
+
return `<ledge-notice team="${attr(n.team)}">${ledgeBody(n.text)}</ledge-notice>`;
|
|
113
|
+
}
|
|
114
|
+
var TEAM_NOTICE = /^<ledge-notice\b([^>]*\bteam="[^"]*"[^>]*)>([\s\S]*?)(?:<\/ledge-notice>|$)/;
|
|
115
|
+
function ledgeTeamNotice(text) {
|
|
116
|
+
const m = TEAM_NOTICE.exec(text.trimStart());
|
|
117
|
+
if (!m) return void 0;
|
|
118
|
+
const team = new Map([...m[1].matchAll(ATTR)].map((a) => [a[1], a[2]])).get("team");
|
|
119
|
+
return team ? { team, text: m[2].trim() } : void 0;
|
|
120
|
+
}
|
|
74
121
|
var TASK_BLOCK = /^<ledge-task\b([^>]*)>([\s\S]*?)(?:<\/ledge-task>|$)/;
|
|
75
122
|
function formatLedgeTask(t) {
|
|
76
123
|
return `<ledge-task id="${attr(t.id)}" from="${attr(t.from)}">${ledgeBody(t.text)}</ledge-task>`;
|
|
@@ -436,7 +483,7 @@ function reduceEvent(s, ev) {
|
|
|
436
483
|
case "PreToolUse":
|
|
437
484
|
case "PermissionRequest":
|
|
438
485
|
case "Notification": {
|
|
439
|
-
const notice2 = ev.name === "UserPromptSubmit" && !!ev.text && (!!crossSessionNotices(ev.text) || !!ledgeNotice(ev.text) || !!ledgeTask(ev.text));
|
|
486
|
+
const notice2 = ev.name === "UserPromptSubmit" && !!ev.text && (!!crossSessionNotices(ev.text) || !!ledgeNotice(ev.text) || !!ledgeTask(ev.text) || !!ledgeTeamNotice(ev.text));
|
|
440
487
|
if (ev.name === "UserPromptSubmit" && ev.text && !s.topic && !notice2 && !crossSession(ev.text)) s = withTopic(s, ev.text, "prompt");
|
|
441
488
|
if (s.pending?.source === "rpc" && ev.name === "PreToolUse") return { ...s, lastHookAt: at, lastActivityAt: at };
|
|
442
489
|
const incoming = pendingFor(ev);
|
|
@@ -563,6 +610,9 @@ function resolveTarget(to, sessions, peers) {
|
|
|
563
610
|
}
|
|
564
611
|
|
|
565
612
|
export {
|
|
613
|
+
teammates,
|
|
614
|
+
seatContext,
|
|
615
|
+
resolveRoleAddress,
|
|
566
616
|
crossSession,
|
|
567
617
|
messageNote,
|
|
568
618
|
stripNote,
|
|
@@ -572,6 +622,8 @@ export {
|
|
|
572
622
|
formatLedgeMessage,
|
|
573
623
|
ledgeMessages,
|
|
574
624
|
formatLedgeNotice,
|
|
625
|
+
formatTeamNotice,
|
|
626
|
+
ledgeTeamNotice,
|
|
575
627
|
formatLedgeTask,
|
|
576
628
|
ledgeTask,
|
|
577
629
|
ledgeNotice,
|
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
2
|
+
EFFORT_ARGS,
|
|
3
|
+
MODEL_ARGS
|
|
4
|
+
} from "./chunk-7IQ6FV2F.js";
|
|
5
|
+
import {
|
|
6
|
+
autonomyArgs,
|
|
7
|
+
findRole
|
|
8
|
+
} from "./chunk-3HCID6NW.js";
|
|
5
9
|
import {
|
|
6
10
|
exitFile,
|
|
7
11
|
spawnSession
|
|
8
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-TRTS24PF.js";
|
|
13
|
+
import {
|
|
14
|
+
DaemonRefusedError,
|
|
15
|
+
connect
|
|
16
|
+
} from "./chunk-GXFU4XAX.js";
|
|
9
17
|
import {
|
|
10
18
|
PROTOCOL_PORT
|
|
11
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-HP3G2TYJ.js";
|
|
12
20
|
import {
|
|
13
21
|
realTmux
|
|
14
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-WJ6YMZTT.js";
|
|
15
23
|
import {
|
|
16
24
|
log
|
|
17
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-43D6NMYH.js";
|
|
18
26
|
import {
|
|
19
27
|
ensureLedgeDir
|
|
20
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-7ZAA75HG.js";
|
|
21
29
|
|
|
22
30
|
// src/cli/launch.ts
|
|
23
31
|
import { spawn } from "child_process";
|
|
@@ -28,19 +36,30 @@ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
28
36
|
function parseLaunchArgs(argv) {
|
|
29
37
|
const args = [];
|
|
30
38
|
let name;
|
|
39
|
+
let role;
|
|
31
40
|
for (let i = 0; i < argv.length; i++) {
|
|
32
41
|
const a = argv[i];
|
|
33
42
|
if (a === "--name") {
|
|
34
43
|
name = argv[++i];
|
|
35
44
|
continue;
|
|
36
45
|
}
|
|
46
|
+
if (a === "--role") {
|
|
47
|
+
role = argv[++i];
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
37
50
|
if (a === "--") {
|
|
38
51
|
args.push(...argv.slice(i + 1));
|
|
39
52
|
break;
|
|
40
53
|
}
|
|
41
54
|
args.push(a);
|
|
42
55
|
}
|
|
43
|
-
return { name, args };
|
|
56
|
+
return { name, ...role ? { role } : {}, args };
|
|
57
|
+
}
|
|
58
|
+
function roleArgs(tool, role, args) {
|
|
59
|
+
const own = role.tool === void 0 || role.tool === tool;
|
|
60
|
+
const hasModel = args.some((a) => a === "--model" || a === "-m" || a.startsWith("--model="));
|
|
61
|
+
const hasEffort = args.some((a) => a === "--effort" || a.startsWith("--effort=") || a.includes("model_reasoning_effort"));
|
|
62
|
+
return [...own && role.model && !hasModel ? MODEL_ARGS[tool](role.model) : [], ...own && role.effort && !hasEffort ? EFFORT_ARGS[tool]?.(role.effort) ?? [] : [], ...autonomyArgs(tool, role.autonomy)];
|
|
44
63
|
}
|
|
45
64
|
function portOpen(port) {
|
|
46
65
|
return new Promise((res) => {
|
|
@@ -118,7 +137,8 @@ async function createSession(opts) {
|
|
|
118
137
|
ids.push(s.id);
|
|
119
138
|
}
|
|
120
139
|
}
|
|
121
|
-
const
|
|
140
|
+
const { role, ...rest } = opts;
|
|
141
|
+
const session = await spawnSession({ ...rest, ...role ? { args: [...roleArgs(opts.tool, role, opts.args), ...opts.args], role: { id: role.id, label: role.label }, ...role.autonomy ? { autonomy: role.autonomy } : {} } : {}, taken: { names, ids } });
|
|
122
142
|
try {
|
|
123
143
|
opts.client?.send({ type: "session.register", session });
|
|
124
144
|
} catch (e) {
|
|
@@ -156,15 +176,20 @@ var UNTRACKED = {
|
|
|
156
176
|
};
|
|
157
177
|
async function launch(tool, argv, opts = {}) {
|
|
158
178
|
await ensureLedgeDir();
|
|
159
|
-
const { name, args } = parseLaunchArgs(argv);
|
|
179
|
+
const { name, role: roleId, args } = parseLaunchArgs(argv);
|
|
160
180
|
const port = opts.port ?? PROTOCOL_PORT;
|
|
161
181
|
const socket = opts.tmuxSocket ?? TMUX_SOCKET;
|
|
162
182
|
const tmux = realTmux(socket);
|
|
163
183
|
const cwd = opts.cwd ?? process.cwd();
|
|
184
|
+
const role = roleId ? findRole(roleId, cwd) : void 0;
|
|
185
|
+
if (roleId && !role) {
|
|
186
|
+
console.error(`ledge: r\xF4le inconnu : ${roleId} (aucun fichier .ledge/roles/${roleId}.md ni ~/.ledge/roles/${roleId}.md)`);
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
164
189
|
const daemon = await ensureDaemon({ port, spawnDaemon: opts.spawnDaemon, retryDelayMs: opts.retryDelayMs });
|
|
165
190
|
let client = daemon.client;
|
|
166
191
|
if (!daemon.client) console.error(UNTRACKED[daemon.reason](port));
|
|
167
|
-
const create = (c) => createSession({ tool, args, command: opts.command, recordExit: true, name, cwd, tmux, client: c });
|
|
192
|
+
const create = (c) => createSession({ tool, args, command: opts.command, recordExit: true, name, ...role ? { role } : {}, cwd, tmux, client: c });
|
|
168
193
|
let session;
|
|
169
194
|
try {
|
|
170
195
|
session = await create(client);
|
|
@@ -198,6 +223,7 @@ async function launch(tool, argv, opts = {}) {
|
|
|
198
223
|
|
|
199
224
|
export {
|
|
200
225
|
parseLaunchArgs,
|
|
226
|
+
roleArgs,
|
|
201
227
|
ensureDaemon,
|
|
202
228
|
DaemonError,
|
|
203
229
|
createSession,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NAMES,
|
|
3
3
|
pickName
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HP3G2TYJ.js";
|
|
5
5
|
import {
|
|
6
6
|
paths
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-7ZAA75HG.js";
|
|
8
8
|
|
|
9
9
|
// src/daemon/transcript.ts
|
|
10
10
|
import { appendFileSync, closeSync, fstatSync, openSync, readFileSync, readSync, statSync } from "fs";
|
|
@@ -206,7 +206,7 @@ var PARENT_AGENT_VARS = [
|
|
|
206
206
|
var PARENT_AGENT = new Set(PARENT_AGENT_VARS);
|
|
207
207
|
function agentEnv(base, extra) {
|
|
208
208
|
const env = {};
|
|
209
|
-
for (const [k, v] of Object.entries(base)) if (v !== void 0 && k !== "TMUX" && !k.startsWith("TMUX_") && !PARENT_AGENT.has(k)) env[k] = v;
|
|
209
|
+
for (const [k, v] of Object.entries(base)) if (v !== void 0 && k !== "TMUX" && !k.startsWith("TMUX_") && !PARENT_AGENT.has(k) && k !== "LEDGE_ROLE" && k !== "LEDGE_TEAM") env[k] = v;
|
|
210
210
|
return { ...env, ...extra };
|
|
211
211
|
}
|
|
212
212
|
async function spawnSession(opts) {
|
|
@@ -224,9 +224,27 @@ async function spawnSession(opts) {
|
|
|
224
224
|
const env = opts.env ?? process.env;
|
|
225
225
|
const transcriptPath = agentSessionId ? claudeTranscriptPath(opts.cwd, agentSessionId, env) : void 0;
|
|
226
226
|
const command = opts.recordExit ? ["sh", "-c", '"$@"; echo $? > "$0"', exitFile(id), ...agent] : agent;
|
|
227
|
-
await opts.tmux.newSession({ name: tmuxSession, cwd: opts.cwd, env: agentEnv(env, { LEDGE_SESSION: id, LEDGE_NAME: name, ...opts.tool === "codex" ? { [CODEX_ENV_VAR]: id } : {} }), unset: PARENT_AGENT_VARS, command });
|
|
227
|
+
await opts.tmux.newSession({ name: tmuxSession, cwd: opts.cwd, env: agentEnv(env, { LEDGE_SESSION: id, LEDGE_NAME: name, ...opts.role ? { LEDGE_ROLE: opts.role.id } : {}, ...opts.team ? { LEDGE_TEAM: opts.team } : {}, ...opts.tool === "codex" ? { [CODEX_ENV_VAR]: id } : {} }), unset: PARENT_AGENT_VARS, command });
|
|
228
228
|
const at = now();
|
|
229
|
-
return {
|
|
229
|
+
return {
|
|
230
|
+
id,
|
|
231
|
+
name,
|
|
232
|
+
tool: opts.tool,
|
|
233
|
+
cwd: opts.cwd,
|
|
234
|
+
project,
|
|
235
|
+
title: `${name} \xB7 ${project}`,
|
|
236
|
+
tmuxSession,
|
|
237
|
+
state: "starting",
|
|
238
|
+
helpers: [],
|
|
239
|
+
startedAt: at,
|
|
240
|
+
lastActivityAt: at,
|
|
241
|
+
source: "launcher",
|
|
242
|
+
...agentSessionId ? { agentSessionId } : {},
|
|
243
|
+
...transcriptPath ? { transcriptPath } : {},
|
|
244
|
+
...opts.role ? { role: opts.role.id, roleLabel: opts.role.label } : {},
|
|
245
|
+
...opts.team ? { team: opts.team } : {},
|
|
246
|
+
...opts.autonomy && opts.autonomy !== "ask" ? { autonomy: opts.autonomy } : {}
|
|
247
|
+
};
|
|
230
248
|
}
|
|
231
249
|
|
|
232
250
|
export {
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
2
|
connect
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GXFU4XAX.js";
|
|
4
4
|
import {
|
|
5
5
|
isArchived
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-HP3G2TYJ.js";
|
|
7
7
|
|
|
8
8
|
// src/cli/who.ts
|
|
9
9
|
import { basename } from "path";
|
|
10
10
|
function formatWho(sessions, self) {
|
|
11
11
|
const live = sessions.filter((s) => s.state !== "exited" && !isArchived(s));
|
|
12
12
|
if (live.length === 0) return "aucune autre session";
|
|
13
|
+
const roles = live.some((s) => s.role);
|
|
13
14
|
const rows = live.map((s) => [
|
|
14
15
|
s.id === self ? "*" : " ",
|
|
15
16
|
s.name,
|
|
17
|
+
...roles ? [s.role ? `@${s.role}` : "-"] : [],
|
|
16
18
|
s.tool,
|
|
17
19
|
s.state,
|
|
18
20
|
`${s.repo ?? s.project}${s.worktree ? ` (${basename(s.cwd)})` : ""}`,
|
|
19
21
|
s.branch ?? "-",
|
|
20
22
|
`${s.topic ?? "-"}${s.source === "hook_only" ? " \xB7 lecture seule" : ""}`
|
|
21
23
|
]);
|
|
22
|
-
const head = [" ", "NOM", "OUTIL", "\xC9TAT", "PROJET", "BRANCHE", "SUJET"];
|
|
24
|
+
const head = [" ", "NOM", ...roles ? ["R\xD4LE"] : [], "OUTIL", "\xC9TAT", "PROJET", "BRANCHE", "SUJET"];
|
|
23
25
|
const widths = head.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
24
26
|
return [head, ...rows].map((r) => r.map((c, i) => i === r.length - 1 ? c : c.padEnd(widths[i])).join(" ")).join("\n");
|
|
25
27
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CODEX_HOOK_VAR
|
|
3
|
+
} from "./chunk-TRTS24PF.js";
|
|
4
|
+
import {
|
|
5
|
+
connect
|
|
6
|
+
} from "./chunk-GXFU4XAX.js";
|
|
7
|
+
import {
|
|
8
|
+
BRIEF_MAX
|
|
9
|
+
} from "./chunk-HP3G2TYJ.js";
|
|
10
|
+
|
|
11
|
+
// src/cli/handover.ts
|
|
12
|
+
import { randomUUID } from "crypto";
|
|
13
|
+
var HANDOVER_USAGE = `ledge handover "<bilan>" ton bilan de rel\xE8ve, quand Ledge te le demande (fait, en cours, suite, pi\xE8ges, fichiers cl\xE9s)
|
|
14
|
+
ledge handover show relire le bilan de la session dont tu as pris la rel\xE8ve`;
|
|
15
|
+
var HANDOVER_MS = 4e3;
|
|
16
|
+
function handoverArgs(args) {
|
|
17
|
+
if (args.length === 1 && args[0] === "show") return { op: "show" };
|
|
18
|
+
const text = args.join(" ").trim();
|
|
19
|
+
if (!args.length || !text || text.length > BRIEF_MAX) return void 0;
|
|
20
|
+
return { op: "brief", text };
|
|
21
|
+
}
|
|
22
|
+
async function handoverOnClient(client, args, by, ms) {
|
|
23
|
+
const op = handoverArgs(args);
|
|
24
|
+
if (!op) return { line: HANDOVER_USAGE, code: 1 };
|
|
25
|
+
try {
|
|
26
|
+
const rid = randomUUID();
|
|
27
|
+
client.send(op.op === "show" ? { type: "handover.show", rid, by } : { type: "handover.brief", rid, by, text: op.text });
|
|
28
|
+
const r = await client.next("handover.result", ms);
|
|
29
|
+
if (r.reason !== void 0) return { line: `refus\xE9 : ${r.reason}`, code: 1 };
|
|
30
|
+
return op.op === "show" ? { line: r.text ?? "", code: 0 } : { line: "bilan re\xE7u : Ledge lance la session qui prend ta rel\xE8ve et lui passe tes t\xE2ches. Arr\xEAte-toi l\xE0.", code: 0 };
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return { line: `pas de r\xE9ponse de Ledge : ${e.message}`, code: 1 };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function runHandover(args, opts = {}) {
|
|
36
|
+
if (!handoverArgs(args)) return { line: HANDOVER_USAGE, code: args.length ? 1 : 0 };
|
|
37
|
+
const env = opts.env ?? process.env;
|
|
38
|
+
const by = env[CODEX_HOOK_VAR] || env.LEDGE_SESSION;
|
|
39
|
+
if (!by) return { line: "ledge handover ne marche que dans une session lanc\xE9e par Ledge", code: 1 };
|
|
40
|
+
const deadline = Date.now() + (opts.timeoutMs ?? HANDOVER_MS);
|
|
41
|
+
const left = () => Math.max(0, deadline - Date.now());
|
|
42
|
+
let client;
|
|
43
|
+
try {
|
|
44
|
+
client = await connect({ port: opts.port, timeoutMs: left() });
|
|
45
|
+
} catch {
|
|
46
|
+
return { line: "daemon not running (start a session with `ledge claude`, or run `ledge daemon`)", code: 1 };
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return await handoverOnClient(client, args, by, left());
|
|
50
|
+
} finally {
|
|
51
|
+
await client.close();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
HANDOVER_USAGE,
|
|
57
|
+
handoverArgs,
|
|
58
|
+
handoverOnClient,
|
|
59
|
+
runHandover
|
|
60
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CODEX_HOOK_VAR
|
|
3
|
+
} from "./chunk-TRTS24PF.js";
|
|
1
4
|
import {
|
|
2
5
|
connect,
|
|
3
6
|
sendMessageAndAwaitAck
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
CODEX_HOOK_VAR
|
|
7
|
-
} from "./chunk-VE4LGNXX.js";
|
|
7
|
+
} from "./chunk-GXFU4XAX.js";
|
|
8
8
|
|
|
9
9
|
// src/cli/send.ts
|
|
10
10
|
import { randomUUID } from "crypto";
|
|
@@ -131,6 +131,7 @@ function parseLedgeCommand(command) {
|
|
|
131
131
|
if (words[1] === "send") return { cmd: "send", args: words.slice(2) };
|
|
132
132
|
if (words[1] === "read") return { cmd: "read", args: words.slice(2) };
|
|
133
133
|
if (words[1] === "task") return { cmd: "task", args: words.slice(2) };
|
|
134
|
+
if (words[1] === "handover") return { cmd: "handover", args: words.slice(2) };
|
|
134
135
|
return void 0;
|
|
135
136
|
}
|
|
136
137
|
|