@paradow/ledge 0.17.0 → 0.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 (40) hide show
  1. package/README.md +7 -1
  2. package/assets/ledge.ico +0 -0
  3. package/dist/cli/{attach-EYIX7FZN.js → attach-3BKQSEWC.js} +12 -7
  4. package/dist/cli/{bridge-6UHCD76N.js → bridge-APNFBPNE.js} +2 -2
  5. package/dist/cli/chunk-3EIRKAX7.js +165 -0
  6. package/dist/cli/chunk-3HCID6NW.js +145 -0
  7. package/dist/cli/{chunk-JQZHXVIZ.js → chunk-43D6NMYH.js} +1 -1
  8. package/dist/cli/chunk-4RD7OJ2J.js +6 -0
  9. package/dist/cli/{chunk-ELLXOV4Q.js → chunk-7ZAA75HG.js} +1 -1
  10. package/dist/cli/{chunk-5ZCHFGCV.js → chunk-BVBX222F.js} +9 -7
  11. package/dist/cli/{chunk-OVJL2QTN.js → chunk-FQZ3IX6E.js} +2 -2
  12. package/dist/cli/{chunk-VVK4I6B5.js → chunk-GXFU4XAX.js} +1 -1
  13. package/dist/cli/chunk-HP3G2TYJ.js +1772 -0
  14. package/dist/cli/{chunk-NC5YAEJB.js → chunk-KKNE7BFZ.js} +55 -3
  15. package/dist/cli/{chunk-36KJUV62.js → chunk-LOOOHYZR.js} +38 -12
  16. package/dist/cli/{chunk-VE4LGNXX.js → chunk-TRTS24PF.js} +23 -5
  17. package/dist/cli/{chunk-Q7YNLP66.js → chunk-WCS24ICX.js} +5 -3
  18. package/dist/cli/{chunk-LAUNHDTX.js → chunk-WJ6YMZTT.js} +2 -2
  19. package/dist/cli/{daemon-QEJLAIBT.js → chunk-WP77O5LC.js} +6045 -7228
  20. package/dist/cli/chunk-Y5DJ5INP.js +60 -0
  21. package/dist/cli/{chunk-AQ25HFBP.js → chunk-ZUYOA55W.js} +5 -4
  22. package/dist/cli/daemon-66EUIZ3J.js +2240 -0
  23. package/dist/cli/handover-UXISKYPS.js +16 -0
  24. package/dist/cli/{hook-AE5HC6AS.js → hook-5UAARBWU.js} +86 -25
  25. package/dist/cli/index.js +30 -16
  26. package/dist/cli/launch-YQYCDZ7N.js +34 -0
  27. package/dist/cli/{send-2OZ3YZRV.js → send-NLVR6PL2.js} +5 -5
  28. package/dist/cli/{setup-2VM4GCDJ.js → setup-EEIXRAMU.js} +4 -4
  29. package/dist/cli/{status-3VJAO6UL.js → status-2OCYHOWZ.js} +3 -3
  30. package/dist/cli/{task-PMKNYP6M.js → task-AOOWHPMN.js} +5 -5
  31. package/dist/cli/team-TVNSVYWW.js +101 -0
  32. package/dist/cli/{tui-DWA2EDC2.js → tui-6JY7IS4D.js} +985 -125
  33. package/dist/cli/{uninstall-AKUWE33D.js → uninstall-UUN7LIIT.js} +4 -4
  34. package/dist/cli/{version-WOGCIZY3.js → version-XSEI3V2C.js} +1 -1
  35. package/dist/cli/who-52MENZTT.js +11 -0
  36. package/package.json +2 -2
  37. package/dist/cli/chunk-QD27W6W5.js +0 -1474
  38. package/dist/cli/chunk-V2H7UD2N.js +0 -6
  39. package/dist/cli/launch-B4G5OFBV.js +0 -27
  40. package/dist/cli/who-37TNDYVA.js +0 -11
package/README.md CHANGED
@@ -42,11 +42,17 @@ ledge claude # start Claude Code in a tracked tmux session (tab renam
42
42
  ledge claude --name max # pick the session's Ledge name
43
43
  ledge status # list bots and what they wait for
44
44
  ledge attach <id> # put this terminal on a running session (what the overlay opens a tab with)
45
- ledge tui # follow and answer every session from this terminal (--notify bell|osc9|off)
45
+ ledge tui # follow and answer every session (--notify bell|osc9|off); see below for Windows Terminal
46
46
  ledge daemon # run the daemon in the foreground (normally run by the systemd service)
47
47
  ledge uninstall # undo ledge setup: hooks, service and ~/.local/bin/ledge (--purge: ~/.ledge too)
48
48
  ```
49
49
 
50
+ From a WSL tab of Windows Terminal, `ledge tui` opens in a tab of its own with the Ledge mascot as its icon. A tab's
51
+ icon comes only from its profile, so Ledge installs a `Ledge` profile as a fragment under
52
+ `%LOCALAPPDATA%\Microsoft\Windows Terminal\Fragments\Ledge` (no admin right needed). That profile runs
53
+ `~/.ledge/tui.sh`, which each `ledge tui` rewrites with its node and CLI. `ledge tui --here`, tmux, SSH and any other
54
+ terminal keep the TUI where it was started.
55
+
50
56
  When a Claude it launched exits, Ledge appends a `custom-title` « name - topic » to its transcript, so `claude --resume` lists the conversation that way, unless the user named it (`claude -n`, `/rename`).
51
57
 
52
58
  `ledge setup` installs `ledge-daemon.service` as a systemd user unit, starts it and enables linger, so the daemon runs from WSL boot and restarts after a crash. Rerun it after a node upgrade or a repository move: the unit is rewritten and the service restarted. Useful commands:
Binary file
@@ -1,19 +1,24 @@
1
1
  import {
2
2
  attachEnv
3
- } from "./chunk-36KJUV62.js";
3
+ } from "./chunk-LOOOHYZR.js";
4
+ import "./chunk-WP77O5LC.js";
5
+ import "./chunk-3EIRKAX7.js";
6
+ import "./chunk-OLLQBK6N.js";
7
+ import "./chunk-KKNE7BFZ.js";
8
+ import "./chunk-3HCID6NW.js";
9
+ import "./chunk-TRTS24PF.js";
4
10
  import {
5
11
  connect
6
- } from "./chunk-VVK4I6B5.js";
7
- import "./chunk-VE4LGNXX.js";
12
+ } from "./chunk-GXFU4XAX.js";
8
13
  import {
9
14
  PROTOCOL_PORT,
10
15
  backendOf
11
- } from "./chunk-QD27W6W5.js";
16
+ } from "./chunk-HP3G2TYJ.js";
12
17
  import {
13
18
  realTmux
14
- } from "./chunk-LAUNHDTX.js";
15
- import "./chunk-JQZHXVIZ.js";
16
- import "./chunk-ELLXOV4Q.js";
19
+ } from "./chunk-WJ6YMZTT.js";
20
+ import "./chunk-43D6NMYH.js";
21
+ import "./chunk-7ZAA75HG.js";
17
22
 
18
23
  // src/cli/attach.ts
19
24
  import { spawn } from "child_process";
@@ -7,8 +7,8 @@ import {
7
7
  parseClientMessage,
8
8
  parseServerMessage,
9
9
  readDaemonToken
10
- } from "./chunk-QD27W6W5.js";
11
- import "./chunk-ELLXOV4Q.js";
10
+ } from "./chunk-HP3G2TYJ.js";
11
+ import "./chunk-7ZAA75HG.js";
12
12
 
13
13
  // src/cli/bridge.ts
14
14
  import WebSocket from "ws";
@@ -0,0 +1,165 @@
1
+ import {
2
+ definitionDirs,
3
+ definitionFiles,
4
+ listRoles,
5
+ readSmall,
6
+ unquote
7
+ } from "./chunk-3HCID6NW.js";
8
+ import {
9
+ SeatsSchema,
10
+ TeamNameSchema
11
+ } from "./chunk-HP3G2TYJ.js";
12
+ import {
13
+ log
14
+ } from "./chunk-43D6NMYH.js";
15
+ import {
16
+ ledgeDir
17
+ } from "./chunk-7ZAA75HG.js";
18
+
19
+ // src/daemon/teams.ts
20
+ import { writeFileSync, mkdirSync, renameSync } from "fs";
21
+ import { join } from "path";
22
+ var scalar = (raw) => unquote(raw.replace(/\s+#.*$/, "").trim());
23
+ function flowMap(raw) {
24
+ const inner = raw.trim().replace(/^\{/, "").replace(/\}$/, "");
25
+ const out = {};
26
+ for (const part of inner.split(",")) {
27
+ if (!part.trim()) continue;
28
+ const i = part.indexOf(":");
29
+ if (i <= 0) throw new Error(`not a key: value pair: ${part.trim().slice(0, 40)}`);
30
+ out[part.slice(0, i).trim()] = scalar(part.slice(i + 1));
31
+ }
32
+ return out;
33
+ }
34
+ function parseTemplateText(text) {
35
+ const src = text.replace(/^/, "").replace(/\r\n/g, "\n");
36
+ if (src.trimStart().startsWith("{")) return JSON.parse(src);
37
+ const out = {};
38
+ let list = null;
39
+ let item = null;
40
+ let itemIndent = -1;
41
+ for (const raw of src.split("\n")) {
42
+ if (!raw.trim() || raw.trimStart().startsWith("#")) continue;
43
+ if (raw.includes(" ")) throw new Error("tabs are not allowed in YAML");
44
+ const indent = raw.length - raw.trimStart().length;
45
+ const line = raw.trim();
46
+ if (indent === 0 && !line.startsWith("- ")) {
47
+ item = null;
48
+ list = null;
49
+ const i2 = line.indexOf(":");
50
+ if (i2 <= 0) throw new Error(`line without a key: ${line.slice(0, 60)}`);
51
+ const key = line.slice(0, i2).trim();
52
+ const rest = line.slice(i2 + 1).trim();
53
+ if (rest === "") {
54
+ list = [];
55
+ out[key] = list;
56
+ } else if (rest.startsWith("[")) out[key] = rest.replace(/^\[|\]$/g, "").split(",").map(scalar).filter(Boolean);
57
+ else out[key] = scalar(rest);
58
+ continue;
59
+ }
60
+ if (!list) throw new Error(`indented line outside a list: ${line.slice(0, 60)}`);
61
+ if (line.startsWith("- ") || line === "-") {
62
+ const rest = line.slice(1).trim();
63
+ if (rest.startsWith("{")) {
64
+ list.push(flowMap(rest));
65
+ item = null;
66
+ continue;
67
+ }
68
+ item = {};
69
+ itemIndent = indent;
70
+ list.push(item);
71
+ if (rest) {
72
+ const i2 = rest.indexOf(":");
73
+ if (i2 <= 0) {
74
+ list[list.length - 1] = scalar(rest);
75
+ item = null;
76
+ continue;
77
+ }
78
+ item[rest.slice(0, i2).trim()] = scalar(rest.slice(i2 + 1));
79
+ }
80
+ continue;
81
+ }
82
+ if (!item || indent <= itemIndent) throw new Error(`line outside a seat: ${line.slice(0, 60)}`);
83
+ const i = line.indexOf(":");
84
+ if (i <= 0) throw new Error(`line without a key: ${line.slice(0, 60)}`);
85
+ item[line.slice(0, i).trim()] = scalar(line.slice(i + 1));
86
+ }
87
+ return out;
88
+ }
89
+ function parseTemplate(id, text, source, path) {
90
+ let raw;
91
+ try {
92
+ raw = parseTemplateText(text);
93
+ } catch (e) {
94
+ return e.message;
95
+ }
96
+ const name = TeamNameSchema.safeParse(typeof raw.name === "string" ? raw.name : id);
97
+ if (!name.success) return "name: a name of 1 to 60 characters";
98
+ const seats = SeatsSchema.safeParse(raw.seats);
99
+ if (!seats.success) {
100
+ const issue = seats.error.issues[0];
101
+ return `seats${issue.path.length ? `.${issue.path.join(".")}` : ""}: ${issue.message}`;
102
+ }
103
+ return { id, name: name.data, seats: seats.data, source, path };
104
+ }
105
+ function listTemplates(cwd) {
106
+ const templates = /* @__PURE__ */ new Map();
107
+ const problems = [];
108
+ for (const { dir, source } of definitionDirs("teams", cwd)) {
109
+ for (const path of definitionFiles(dir, [".yaml", ".yml"])) {
110
+ const id = path.slice(dir.length + 1).replace(/\.ya?ml$/, "");
111
+ if (templates.has(id)) continue;
112
+ let t;
113
+ try {
114
+ t = parseTemplate(id, readSmall(path), source, path);
115
+ } catch (e) {
116
+ t = e.message;
117
+ }
118
+ if (typeof t === "string") {
119
+ log(`team ${path} skipped: ${t}`);
120
+ problems.push({ path, reason: t });
121
+ continue;
122
+ }
123
+ templates.set(id, t);
124
+ }
125
+ }
126
+ return { templates: [...templates.values()].sort((a, b) => a.id.localeCompare(b.id)), problems };
127
+ }
128
+ function resolveSeats(seats, roles, defaults = { tool: "claude" }) {
129
+ const byId = new Map(roles.map((r) => [r.id, r]));
130
+ const unknown = seats.filter((s) => !byId.has(s.role)).map((s) => s.role);
131
+ if (unknown.length) return { unknown };
132
+ return seats.map((s) => {
133
+ const role = byId.get(s.role);
134
+ const tool = s.tool ?? role.tool ?? defaults.tool;
135
+ const own = s.tool === void 0 || s.tool === role.tool;
136
+ const model = s.model ?? (own ? role.model : void 0) ?? (tool === defaults.tool ? defaults.model : void 0);
137
+ const effort = s.effort ?? (own ? role.effort : void 0) ?? (tool === defaults.tool ? defaults.effort : void 0);
138
+ return { role, tool, ...model ? { model } : {}, ...effort ? { effort } : {}, ...s.name ? { name: s.name } : {} };
139
+ });
140
+ }
141
+ function listDefinitions(cwd) {
142
+ const r = listRoles(cwd);
143
+ const t = listTemplates(cwd);
144
+ return { roles: r.roles, templates: t.templates, problems: [...r.problems, ...t.problems] };
145
+ }
146
+ function templateId(name) {
147
+ const id = name.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
148
+ return id || "team";
149
+ }
150
+ var yamlScalar = (v) => /^[\w./@:+-]+$/.test(v) && !/^(true|false|null|yes|no|~)$/i.test(v) ? v : JSON.stringify(v);
151
+ function saveTemplate(name, seats, dir = join(ledgeDir(), "teams")) {
152
+ const path = join(dir, `${templateId(name)}.yaml`);
153
+ const lines = [`name: ${yamlScalar(name)}`, "seats:", ...seats.map((s) => ` - { ${Object.entries(s).filter(([, v]) => v !== void 0).map(([k, v]) => `${k}: ${yamlScalar(String(v))}`).join(", ")} }`), ""];
154
+ mkdirSync(dir, { recursive: true });
155
+ const tmp = `${path}.tmp`;
156
+ writeFileSync(tmp, lines.join("\n"));
157
+ renameSync(tmp, path);
158
+ return path;
159
+ }
160
+
161
+ export {
162
+ resolveSeats,
163
+ listDefinitions,
164
+ saveTemplate
165
+ };
@@ -0,0 +1,145 @@
1
+ import {
2
+ ROLE_LABEL_MAX,
3
+ ROLE_PROMPT_MAX,
4
+ RoleSchema
5
+ } from "./chunk-HP3G2TYJ.js";
6
+ import {
7
+ log
8
+ } from "./chunk-43D6NMYH.js";
9
+ import {
10
+ ledgeDir
11
+ } from "./chunk-7ZAA75HG.js";
12
+
13
+ // src/daemon/roles.ts
14
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
15
+ import { dirname, join, resolve } from "path";
16
+ var FILE_MAX = 64 * 1024;
17
+ function repoRoot(cwd) {
18
+ const start = resolve(cwd);
19
+ for (let dir = start; ; dir = dirname(dir)) {
20
+ if (existsSync(join(dir, ".git"))) return dir;
21
+ if (dirname(dir) === dir) return start;
22
+ }
23
+ }
24
+ function definitionDirs(kind, cwd) {
25
+ const home = join(ledgeDir(), kind);
26
+ const repo = cwd ? join(repoRoot(cwd), ".ledge", kind) : void 0;
27
+ return [...repo && resolve(repo) !== resolve(home) ? [{ dir: repo, source: "repo" }] : [], { dir: home, source: "home" }];
28
+ }
29
+ function definitionFiles(dir, ext) {
30
+ let names;
31
+ try {
32
+ names = readdirSync(dir);
33
+ } catch {
34
+ return [];
35
+ }
36
+ return names.filter((n) => ext.some((e) => n.endsWith(e)) && !n.startsWith(".")).sort().map((n) => join(dir, n)).filter((p) => {
37
+ try {
38
+ return statSync(p).isFile();
39
+ } catch {
40
+ return false;
41
+ }
42
+ });
43
+ }
44
+ function readSmall(path) {
45
+ if (statSync(path).size > FILE_MAX) throw new Error(`file over ${FILE_MAX / 1024} KB`);
46
+ return readFileSync(path, "utf8");
47
+ }
48
+ function frontMatter(text) {
49
+ const src = text.replace(/^/, "").replace(/\r\n/g, "\n");
50
+ const m = /^---\n([\s\S]*?)\n---[ \t]*(?:\n|$)/.exec(src);
51
+ if (!m) return { fields: {}, body: src.trim() };
52
+ const fields = {};
53
+ for (const raw of m[1].split("\n")) {
54
+ const line = raw.replace(/\s+#.*$/, "").trim();
55
+ if (!line || line.startsWith("#")) continue;
56
+ const i = line.indexOf(":");
57
+ if (i <= 0) throw new Error(`front matter line without a key: ${line.slice(0, 60)}`);
58
+ fields[line.slice(0, i).trim()] = unquote(line.slice(i + 1).trim());
59
+ }
60
+ return { fields, body: src.slice(m[0].length).trim() };
61
+ }
62
+ var unquote = (v) => v.length >= 2 && (v[0] === '"' || v[0] === "'") && v.at(-1) === v[0] ? v.slice(1, -1) : v;
63
+ var ROLE_KEYS = /* @__PURE__ */ new Set(["label", "tool", "model", "effort", "autonomy"]);
64
+ function parseRole(id, text, source, path) {
65
+ let parsed;
66
+ try {
67
+ parsed = frontMatter(text);
68
+ } catch (e) {
69
+ return e.message;
70
+ }
71
+ const { fields, body } = parsed;
72
+ const unknown = Object.keys(fields).find((k) => !ROLE_KEYS.has(k));
73
+ if (unknown) return `unknown key ${unknown}`;
74
+ if (body.length > ROLE_PROMPT_MAX) return `prompt over ${ROLE_PROMPT_MAX} characters`;
75
+ const label = (fields.label ?? id).slice(0, ROLE_LABEL_MAX);
76
+ const r = RoleSchema.safeParse({
77
+ id,
78
+ label,
79
+ prompt: body,
80
+ source,
81
+ path,
82
+ ...fields.tool ? { tool: fields.tool } : {},
83
+ ...fields.model ? { model: fields.model } : {},
84
+ ...fields.effort ? { effort: fields.effort } : {},
85
+ ...fields.autonomy ? { autonomy: fields.autonomy } : {}
86
+ });
87
+ if (!r.success) {
88
+ const issue = r.error.issues[0];
89
+ return `${issue.path.join(".") || "role"}: ${issue.message}`;
90
+ }
91
+ return r.data;
92
+ }
93
+ function listRoles(cwd) {
94
+ const roles = /* @__PURE__ */ new Map();
95
+ const problems = [];
96
+ for (const { dir, source } of definitionDirs("roles", cwd)) {
97
+ for (const path of definitionFiles(dir, [".md"])) {
98
+ const id = path.slice(dir.length + 1, -".md".length);
99
+ if (roles.has(id)) continue;
100
+ let r;
101
+ try {
102
+ r = parseRole(id, readSmall(path), source, path);
103
+ } catch (e) {
104
+ r = e.message;
105
+ }
106
+ if (typeof r === "string") {
107
+ log(`role ${path} skipped: ${r}`);
108
+ problems.push({ path, reason: r });
109
+ continue;
110
+ }
111
+ roles.set(id, r);
112
+ }
113
+ }
114
+ return { roles: [...roles.values()].sort((a, b) => a.id.localeCompare(b.id)), problems };
115
+ }
116
+ function findRole(id, cwd) {
117
+ return listRoles(cwd).roles.find((r) => r.id === id);
118
+ }
119
+ function autonomyArgs(tool, autonomy) {
120
+ if (!autonomy || autonomy === "ask") return [];
121
+ switch (tool) {
122
+ case "claude":
123
+ return ["--permission-mode", autonomy === "edits" ? "acceptEdits" : "auto"];
124
+ case "codex":
125
+ return autonomy === "edits" ? ["--ask-for-approval", "on-request"] : ["--ask-for-approval", "never", "--sandbox", "workspace-write"];
126
+ case "cursor":
127
+ return autonomy === "auto" ? ["--force"] : [];
128
+ case "omp":
129
+ return [];
130
+ }
131
+ }
132
+ function ompApprovalFor(autonomy) {
133
+ return autonomy === "auto" ? "yolo" : autonomy ? "always-ask" : void 0;
134
+ }
135
+
136
+ export {
137
+ definitionDirs,
138
+ definitionFiles,
139
+ readSmall,
140
+ unquote,
141
+ listRoles,
142
+ findRole,
143
+ autonomyArgs,
144
+ ompApprovalFor
145
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  paths
3
- } from "./chunk-ELLXOV4Q.js";
3
+ } from "./chunk-7ZAA75HG.js";
4
4
 
5
5
  // src/log.ts
6
6
  import { appendFileSync } from "fs";
@@ -0,0 +1,6 @@
1
+ // src/version.ts
2
+ var LEDGE_VERSION = true ? "0.19.0" : "0.0.0-dev";
3
+
4
+ export {
5
+ LEDGE_VERSION
6
+ };
@@ -17,7 +17,7 @@ function ledgeDir() {
17
17
  }
18
18
  function paths() {
19
19
  const dir = ledgeDir();
20
- return { dir, tmuxConf: join(dir, "tmux.conf"), journal: join(dir, "sessions.json"), log: join(dir, "daemon.log"), pid: join(dir, "daemon.pid"), history: join(dir, "history"), mail: join(dir, "mail.json"), tasks: join(dir, "tasks.json"), machines: join(dir, "machines.json"), projects: join(dir, "projects.json"), island: join(dir, "island.json"), token: join(dir, "token"), mobile: join(dir, "mobile.json"), mobileSession: join(dir, "mobile-session.json"), mobileKey: join(dir, "mobile-key"), devices: join(dir, "devices.json"), revocations: join(dir, "revocations.json") };
20
+ return { dir, tmuxConf: join(dir, "tmux.conf"), journal: join(dir, "sessions.json"), log: join(dir, "daemon.log"), pid: join(dir, "daemon.pid"), history: join(dir, "history"), mail: join(dir, "mail.json"), tasks: join(dir, "tasks.json"), teams: join(dir, "teams-live.json"), machines: join(dir, "machines.json"), projects: join(dir, "projects.json"), island: join(dir, "island.json"), token: join(dir, "token"), mobile: join(dir, "mobile.json"), mobileSession: join(dir, "mobile-session.json"), mobileKey: join(dir, "mobile-key"), devices: join(dir, "devices.json"), revocations: join(dir, "revocations.json") };
21
21
  }
22
22
  async function ensureLedgeDir() {
23
23
  const p = paths();
@@ -1,23 +1,24 @@
1
- import {
2
- connect
3
- } from "./chunk-VVK4I6B5.js";
4
1
  import {
5
2
  CODEX_HOOK_VAR
6
- } from "./chunk-VE4LGNXX.js";
3
+ } from "./chunk-TRTS24PF.js";
4
+ import {
5
+ connect
6
+ } from "./chunk-GXFU4XAX.js";
7
7
  import {
8
+ LEDGE_GIVER,
8
9
  TASK_TEXT_MAX,
9
10
  TASK_TITLE_MAX
10
- } from "./chunk-QD27W6W5.js";
11
+ } from "./chunk-HP3G2TYJ.js";
11
12
 
12
13
  // src/cli/task.ts
13
14
  import { randomUUID } from "crypto";
14
- var TASK_USAGE = `ledge task give <nom> "<titre>" ["<d\xE9tail>"] confier un travail \xE0 une session
15
+ var TASK_USAGE = `ledge task give <nom|@r\xF4le> "<titre>" ["<d\xE9tail>"] confier un travail \xE0 une session (@r\xF4le : dans ton \xE9quipe)
15
16
  ledge task list [--all] tes t\xE2ches (tenues ou confi\xE9es), ouvertes ; --all : closes aussi
16
17
  ledge task show <id> une t\xE2che, son d\xE9tail et son historique
17
18
  ledge task take <id> la prendre
18
19
  ledge task done <id> ["<r\xE9sultat>"] la terminer (la session qui l'a confi\xE9e est pr\xE9venue)
19
20
  ledge task refuse <id> "<raison>" la refuser, ou l'annuler si tu l'as confi\xE9e
20
- ledge task pass <id> <nom> ["<note>"] la passer \xE0 une autre session`;
21
+ ledge task pass <id> <nom|@r\xF4le> ["<note>"] la passer \xE0 une autre session`;
21
22
  var TASK_MS = 4e3;
22
23
  var ID = /^t[0-9a-f]{6}$/;
23
24
  function taskArgs(args) {
@@ -58,6 +59,7 @@ function formatTask(t) {
58
59
  async function taskOnClient(client, args, by, ms) {
59
60
  const op = taskArgs(args);
60
61
  if (!op) return { line: TASK_USAGE, code: 1 };
62
+ if (by === LEDGE_GIVER) return { line: "refus\xE9 : seul Ledge confie une t\xE2che en son nom", code: 1 };
61
63
  try {
62
64
  client.send({ type: "task.cmd", rid: randomUUID(), by, ...op });
63
65
  const r = await client.next("task.result", ms);
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  run
3
- } from "./chunk-LAUNHDTX.js";
3
+ } from "./chunk-WJ6YMZTT.js";
4
4
  import {
5
5
  ensureLedgeDir,
6
6
  paths
7
- } from "./chunk-ELLXOV4Q.js";
7
+ } from "./chunk-7ZAA75HG.js";
8
8
 
9
9
  // src/cli/setup.ts
10
10
  import { readFile as readFile2, writeFile, mkdir, rename, realpath, rm, stat, lstat, readlink } from "fs/promises";
@@ -4,7 +4,7 @@ import {
4
4
  SUMMARY_MAX,
5
5
  parseServerMessage,
6
6
  readDaemonToken
7
- } from "./chunk-QD27W6W5.js";
7
+ } from "./chunk-HP3G2TYJ.js";
8
8
 
9
9
  // src/client.ts
10
10
  import WebSocket from "ws";