wherdr 1.3.2 → 1.3.4

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 (188) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{AgentView.C4YOwvZG.css → AgentView.DzFWBd2A.css} +1 -1
  3. package/.output/public/_nuxt/{Cd8j9O5w.js → BEJHMUrj.js} +1 -1
  4. package/.output/public/_nuxt/BOtcEDnA.js +1 -0
  5. package/.output/public/_nuxt/BUaYhQ2r.js +6 -0
  6. package/.output/public/_nuxt/{yzABg5xl.js → Bpoxvzh6.js} +1 -1
  7. package/.output/public/_nuxt/C25ROUa9.js +3 -0
  8. package/.output/public/_nuxt/CDWjMk9C.js +2 -0
  9. package/.output/public/_nuxt/CJKsMzIp.js +1 -0
  10. package/.output/public/_nuxt/CJNhn9sA.js +36 -0
  11. package/.output/public/_nuxt/COCvtdHN.js +89 -0
  12. package/.output/public/_nuxt/{Ci8N4mWR.js → CXJnLmWv.js} +1 -1
  13. package/.output/public/_nuxt/Cl45J6_Q.js +1 -0
  14. package/.output/public/_nuxt/CndPDbMI.js +2 -0
  15. package/.output/public/_nuxt/Ctbx58Um.js +23 -0
  16. package/.output/public/_nuxt/{8ksjcuDu.js → DMLmW6JM.js} +49 -56
  17. package/.output/public/_nuxt/Dl1PHLOB.js +1 -0
  18. package/.output/public/_nuxt/Nhex3_iH.js +1 -0
  19. package/.output/public/_nuxt/OpWX45i7.js +89 -0
  20. package/.output/public/_nuxt/builds/latest.json +1 -1
  21. package/.output/public/_nuxt/builds/meta/4994f3a4-4126-4f57-a9e5-c7ebb25850bc.json +1 -0
  22. package/.output/public/_nuxt/dTj21DMs.js +1 -0
  23. package/.output/public/_nuxt/entry.BohTWE-n.css +2 -0
  24. package/.output/public/_nuxt/kwSoIiM7.js +1 -0
  25. package/.output/public/_nuxt/mkEwYka4.js +1 -0
  26. package/.output/public/_nuxt/pdf.min.t82Z80JU.mjs +28 -0
  27. package/.output/public/_nuxt/pdf.worker.min.wgc6bjNh.mjs +28 -0
  28. package/.output/public/_nuxt/w7Rm7unj.js +1 -0
  29. package/.output/public/sw.js +1 -1
  30. package/.output/server/chunks/_/branding.mjs +1 -1
  31. package/.output/server/chunks/_/filePaths.mjs +25 -0
  32. package/.output/server/chunks/_/files.mjs +217 -0
  33. package/.output/server/chunks/_/machineOrder.mjs +1 -1
  34. package/.output/server/chunks/_/onboarding.mjs +1 -1
  35. package/.output/server/chunks/_/package.mjs +5 -2
  36. package/.output/server/chunks/_/pluginPolicy.mjs +93 -0
  37. package/.output/server/chunks/_/plugins.mjs +9 -96
  38. package/.output/server/chunks/_/projectBoard.mjs +124 -36
  39. package/.output/server/chunks/_/projectDocs.mjs +106 -0
  40. package/.output/server/chunks/_/projectInbox.mjs +135 -0
  41. package/.output/server/chunks/_/projectThreads.mjs +46 -0
  42. package/.output/server/chunks/_/projectThreads2.mjs +61 -0
  43. package/.output/server/chunks/_/projectsActions.mjs +1 -1
  44. package/.output/server/chunks/_/projectsPlugin.mjs +24 -50
  45. package/.output/server/chunks/_/projectsPlugin2.mjs +57 -0
  46. package/.output/server/chunks/_/restart.mjs +2 -312
  47. package/.output/server/chunks/_/reveal.mjs +2 -24
  48. package/.output/server/chunks/_/selfupdate.mjs +3 -2
  49. package/.output/server/chunks/_/settingstabs.mjs +36 -0
  50. package/.output/server/chunks/_/updates.mjs +51 -10
  51. package/.output/server/chunks/_/worktrees.mjs +1 -1
  52. package/.output/server/chunks/_/ws.mjs +1 -1
  53. package/.output/server/chunks/nitro/nitro.mjs +1718 -417
  54. package/.output/server/chunks/raw/bump-homebrew.mjs +1 -1
  55. package/.output/server/chunks/raw/herdr-plugin.mjs +1 -1
  56. package/.output/server/chunks/raw/release-ready.mjs +5 -0
  57. package/.output/server/chunks/routes/api/_..._.mjs +1 -1
  58. package/.output/server/chunks/routes/api/agents.post.mjs +1 -1
  59. package/.output/server/chunks/routes/api/ask-tab.post.mjs +1 -1
  60. package/.output/server/chunks/routes/api/attach.post.mjs +1 -1
  61. package/.output/server/chunks/routes/api/auth/disable.post.mjs +1 -1
  62. package/.output/server/chunks/routes/api/auth/lock-all.post.mjs +1 -1
  63. package/.output/server/chunks/routes/api/auth/lock.post.mjs +1 -1
  64. package/.output/server/chunks/routes/api/auth/login/options.post.mjs +1 -1
  65. package/.output/server/chunks/routes/api/auth/login/verify.post.mjs +1 -1
  66. package/.output/server/chunks/routes/api/auth/max-session.post.mjs +1 -1
  67. package/.output/server/chunks/routes/api/auth/register/options.post.mjs +1 -1
  68. package/.output/server/chunks/routes/api/auth/register/verify.post.mjs +1 -1
  69. package/.output/server/chunks/routes/api/auth/status.get.mjs +1 -1
  70. package/.output/server/chunks/routes/api/board.get.mjs +148 -0
  71. package/.output/server/chunks/routes/api/changes.get.mjs +2 -2
  72. package/.output/server/chunks/routes/api/chat/image.get.mjs +2 -2
  73. package/.output/server/chunks/routes/api/chat.get.mjs +26 -6
  74. package/.output/server/chunks/routes/api/choose.post.mjs +40 -14
  75. package/.output/server/chunks/routes/api/claude-statusline.post.mjs +2 -2
  76. package/.output/server/chunks/routes/api/close/status.get.mjs +2 -2
  77. package/.output/server/chunks/routes/api/close.post.mjs +1 -1
  78. package/.output/server/chunks/routes/api/codex-update.post.mjs +2 -2
  79. package/.output/server/chunks/routes/api/commands.get.mjs +4 -3
  80. package/.output/server/chunks/routes/api/config.get.mjs +3 -2
  81. package/.output/server/chunks/routes/api/dirs.get.mjs +2 -2
  82. package/.output/server/chunks/routes/api/dismiss-stopped.post.mjs +43 -0
  83. package/.output/server/chunks/routes/api/dismiss.post.mjs +2 -2
  84. package/.output/server/chunks/routes/api/effort.post.mjs +2 -2
  85. package/.output/server/chunks/routes/api/efforts.get.mjs +2 -2
  86. package/.output/server/chunks/routes/api/file.post.mjs +2 -2
  87. package/.output/server/chunks/routes/api/files/download.get.mjs +60 -0
  88. package/.output/server/chunks/routes/api/files/read.get.mjs +45 -0
  89. package/.output/server/chunks/routes/api/files.get.mjs +44 -0
  90. package/.output/server/chunks/routes/api/gitroot.get.mjs +2 -2
  91. package/.output/server/chunks/routes/api/health.get.mjs +1 -1
  92. package/.output/server/chunks/routes/api/index.get.mjs +2 -1
  93. package/.output/server/chunks/routes/api/input.post.mjs +32 -5
  94. package/.output/server/chunks/routes/api/interrupt.post.mjs +2 -2
  95. package/.output/server/chunks/routes/api/isgit.get.mjs +2 -2
  96. package/.output/server/chunks/routes/api/machine/awake.get.mjs +2 -2
  97. package/.output/server/chunks/routes/api/machine/awake.post.mjs +2 -2
  98. package/.output/server/chunks/routes/api/machine/order.get.mjs +1 -1
  99. package/.output/server/chunks/routes/api/machine/order.post.mjs +1 -1
  100. package/.output/server/chunks/routes/api/machine/rename.post.mjs +2 -2
  101. package/.output/server/chunks/routes/api/menu.post.mjs +5 -2
  102. package/.output/server/chunks/routes/api/model.post.mjs +2 -2
  103. package/.output/server/chunks/routes/api/models.get.mjs +2 -2
  104. package/.output/server/chunks/routes/api/nav.post.mjs +36 -22
  105. package/.output/server/chunks/routes/api/onboarding.get.mjs +1 -1
  106. package/.output/server/chunks/routes/api/onboarding.post.mjs +1 -1
  107. package/.output/server/chunks/routes/api/phone.get.mjs +2 -2
  108. package/.output/server/chunks/routes/api/phone.post.mjs +2 -2
  109. package/.output/server/chunks/routes/api/plugins/actions.get.mjs +4 -1
  110. package/.output/server/chunks/routes/api/plugins/invoke.post.mjs +4 -1
  111. package/.output/server/chunks/routes/api/plugins/profiles.get.mjs +4 -1
  112. package/.output/server/chunks/routes/api/plugins/projects.get.mjs +4 -3
  113. package/.output/server/chunks/routes/api/plugins/projects.post.mjs +4 -3
  114. package/.output/server/chunks/routes/api/plugins/thread-limits.get.mjs +2 -2
  115. package/.output/server/chunks/routes/api/plugins/thread-limits.post.mjs +2 -2
  116. package/.output/server/chunks/routes/api/project/inbox.get.mjs +44 -0
  117. package/.output/server/chunks/routes/api/project/inbox.post.mjs +44 -0
  118. package/.output/server/chunks/routes/api/project/memory.get.mjs +43 -0
  119. package/.output/server/chunks/routes/api/project/report.get.mjs +3 -2
  120. package/.output/server/chunks/routes/api/project/routines.get.mjs +44 -0
  121. package/.output/server/chunks/routes/api/project/settings.get.mjs +42 -0
  122. package/.output/server/chunks/routes/api/project/thread.get.mjs +45 -0
  123. package/.output/server/chunks/routes/api/project/thread.post.mjs +47 -0
  124. package/.output/server/chunks/routes/api/prompt.post.mjs +21 -3
  125. package/.output/server/chunks/routes/api/push/quiet.post.mjs +2 -2
  126. package/.output/server/chunks/routes/api/push/subscribe.post.mjs +2 -2
  127. package/.output/server/chunks/routes/api/push/test.post.mjs +2 -2
  128. package/.output/server/chunks/routes/api/push/unsubscribe.post.mjs +2 -2
  129. package/.output/server/chunks/routes/api/quotas.get.mjs +2 -2
  130. package/.output/server/chunks/routes/api/rename.post.mjs +1 -1
  131. package/.output/server/chunks/routes/api/requeue.post.mjs +2 -2
  132. package/.output/server/chunks/routes/api/restart.get.mjs +1 -1
  133. package/.output/server/chunks/routes/api/restart.post.mjs +1 -1
  134. package/.output/server/chunks/routes/api/reveal.post.mjs +2 -1
  135. package/.output/server/chunks/routes/api/screen-key.post.mjs +14 -11
  136. package/.output/server/chunks/routes/api/screen.get.mjs +6 -37
  137. package/.output/server/chunks/routes/api/search.get.mjs +2 -2
  138. package/.output/server/chunks/routes/api/seen.post.mjs +2 -2
  139. package/.output/server/chunks/routes/api/sessions.get.mjs +2 -2
  140. package/.output/server/chunks/routes/api/sessions.post.mjs +2 -2
  141. package/.output/server/chunks/routes/api/settings-tab.post.mjs +89 -0
  142. package/.output/server/chunks/routes/api/space.post.mjs +14 -14
  143. package/.output/server/chunks/routes/api/state.get.mjs +2 -2
  144. package/.output/server/chunks/routes/api/unqueue.post.mjs +2 -2
  145. package/.output/server/chunks/routes/api/update/status.get.mjs +1 -1
  146. package/.output/server/chunks/routes/api/update.get.mjs +2 -2
  147. package/.output/server/chunks/routes/api/update.post.mjs +2 -2
  148. package/.output/server/chunks/routes/api/upload.post.mjs +2 -2
  149. package/.output/server/chunks/routes/api/worktrees/remove.post.mjs +1 -1
  150. package/.output/server/chunks/routes/api/worktrees.get.mjs +1 -1
  151. package/.output/server/chunks/routes/favicon.ico.get.mjs +1 -1
  152. package/.output/server/chunks/routes/icons/_name_.get.mjs +2 -2
  153. package/.output/server/chunks/routes/renderer.mjs +3 -3
  154. package/.output/server/chunks/routes/uploads/_...name_.get.mjs +2 -2
  155. package/.output/server/chunks/routes/ws/_..._.mjs +2 -2
  156. package/.output/server/chunks/routes/ws/events.mjs +2 -2
  157. package/.output/server/chunks/routes/ws/term.mjs +2 -2
  158. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  159. package/.output/server/index.mjs +2 -2
  160. package/.output/server/package.json +1 -1
  161. package/README.md +368 -40
  162. package/bin/install-docker-skill.sh +20 -0
  163. package/bin/lib/commands.mjs +5 -1
  164. package/bin/lib/core.mjs +1 -0
  165. package/bin/lib/install-skill.sh +100 -0
  166. package/bin/lib/skill.mjs +16 -0
  167. package/bin/lib/updater.mjs +65 -12
  168. package/bin/lib/verify-release.mjs +20 -0
  169. package/package.json +5 -2
  170. package/skills/wherdr/SKILL.md +186 -0
  171. package/.output/public/_nuxt/B7QP1TFs.js +0 -23
  172. package/.output/public/_nuxt/BDNMzG2s.js +0 -1
  173. package/.output/public/_nuxt/BOpBIziZ.js +0 -6
  174. package/.output/public/_nuxt/BWXdFfeg.js +0 -17
  175. package/.output/public/_nuxt/COFCNFpG.js +0 -83
  176. package/.output/public/_nuxt/CWwyK2XK.js +0 -2
  177. package/.output/public/_nuxt/CXG-cOdh.js +0 -1
  178. package/.output/public/_nuxt/CkZ8t6uB.js +0 -1
  179. package/.output/public/_nuxt/D4Yz4rat.js +0 -65
  180. package/.output/public/_nuxt/D7sxNXx1.js +0 -1
  181. package/.output/public/_nuxt/DViwOclv.js +0 -1
  182. package/.output/public/_nuxt/DoD7Qb5P.js +0 -30
  183. package/.output/public/_nuxt/DyBZDSCV.js +0 -2
  184. package/.output/public/_nuxt/SiL1FJ5s.js +0 -3
  185. package/.output/public/_nuxt/bLP7Jj-b.js +0 -1
  186. package/.output/public/_nuxt/builds/meta/7afbf062-5369-4002-b20d-7a582ff6e0f1.json +0 -1
  187. package/.output/public/_nuxt/entry.Crt1r4l3.css +0 -2
  188. package/.output/server/chunks/routes/ws/mirror.mjs +0 -161
@@ -1,6 +1,74 @@
1
1
  import { execFile } from 'node:child_process';
2
2
  import path from 'node:path';
3
- import { aE as currentMachineSlots, aF as paneBaseMachine, H as HerdrError, m as machineFor, aC as HERDR_CHILD_ENV, aD as HERDR_BIN, aA as herdrOn, aG as projectOf, aH as isProjectThread } from '../nitro/nitro.mjs';
3
+ import { u as currentMachineSlots, q as paneBaseMachine, H as HerdrError, v as machineFor, x as herdrOn, y as HERDR_CHILD_ENV, z as HERDR_BIN, n as projectOf, B as isProjectThread } from '../nitro/nitro.mjs';
4
+ import { m as manifestBinary } from './projectsPlugin.mjs';
5
+
6
+ const ESCAPES = { b: "\b", t: " ", n: "\n", f: "\f", r: "\r", e: "\x1B", '"': '"', "\\": "\\" };
7
+ function unescapeBasic(s) {
8
+ return s.replace(/\\[ \t]*\n\s*/g, "").replace(/\\(u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|[\s\S])/g, (all, x) => {
9
+ var _a;
10
+ if (x.length > 1) {
11
+ const code = Number.parseInt(x.slice(1), 16);
12
+ return code <= 1114111 ? String.fromCodePoint(code) : all;
13
+ }
14
+ return (_a = ESCAPES[x]) != null ? _a : all;
15
+ });
16
+ }
17
+ function closing(text, from, quote, basic) {
18
+ for (let i = from; i < text.length; i++) {
19
+ if (basic && text[i] === "\\") {
20
+ i++;
21
+ continue;
22
+ }
23
+ if (quote.length === 1 && text[i] === "\n") return -1;
24
+ if (text.startsWith(quote, i)) {
25
+ if (quote.length === 3) while (text[i + 3] === quote[0]) i++;
26
+ return i;
27
+ }
28
+ }
29
+ return -1;
30
+ }
31
+ const HEADER = /^[ \t]*\[\[?[ \t]*([A-Za-z0-9_.-]+)[ \t]*\]\]?[ \t]*(?:#.*)?$/;
32
+ const KEY = /^[ \t]*([A-Za-z0-9_-]+)[ \t]*=[ \t]*/;
33
+ function tomlTables(input) {
34
+ const text = String(input || "").replace(/\r\n?/g, "\n");
35
+ let table = { name: "", strings: {}, bare: {} };
36
+ const tables = [table];
37
+ let at = 0;
38
+ while (at < text.length) {
39
+ const eol = text.indexOf("\n", at);
40
+ const line = text.slice(at, eol < 0 ? text.length : eol);
41
+ let next = eol < 0 ? text.length : eol + 1;
42
+ const header = HEADER.exec(line);
43
+ const m = header ? null : KEY.exec(line);
44
+ if (header) {
45
+ table = { name: header[1], strings: {}, bare: {} };
46
+ tables.push(table);
47
+ } else if (m) {
48
+ const start = at + m[0].length;
49
+ const quote = ['"""', "'''", '"', "'"].find((q) => text.startsWith(q, start));
50
+ if (quote) {
51
+ const basic = quote[0] === '"';
52
+ const end = closing(text, start + quote.length, quote, basic);
53
+ if (end >= 0) {
54
+ let raw = text.slice(start + quote.length, end);
55
+ if (quote.length === 3 && raw.startsWith("\n")) raw = raw.slice(1);
56
+ table.strings[m[1]] = basic ? unescapeBasic(raw) : raw;
57
+ const after = text.indexOf("\n", end + quote.length);
58
+ next = after < 0 ? text.length : after + 1;
59
+ }
60
+ } else {
61
+ const value = line.slice(m[0].length).split("#")[0].trim();
62
+ if (value) table.bare[m[1]] = value;
63
+ }
64
+ }
65
+ at = next;
66
+ }
67
+ return tables;
68
+ }
69
+ function tomlStrings(front) {
70
+ return Object.assign({}, ...tomlTables(front).map((t) => t.strings));
71
+ }
4
72
 
5
73
  const fold = (s) => s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
6
74
  const KINDS = [
@@ -21,8 +89,12 @@ function listKind(title) {
21
89
  }
22
90
  const THREAD_ID = /\bt-\d{4,}\b/i;
23
91
  const TASK_LINE = /^[-*+]\s+(?:\[([ xX])\](?:\s+|$))?(.*)$/;
24
- const OWNER = /\s*\(([^()]{1,48})\)\s*$/;
25
- const REFS = /\s*\[\s*t-\d{4,}(?:\s*[,;\s]\s*t-\d{4,})*\s*\]/gi;
92
+ const OWNER = /\(([^()]{1,48})\)\s*$/;
93
+ const REFS = /\s*\[\s*t-\d{4,}(?:(?:\s+|\s*[,;]\s*)t-\d{4,})*\s*\]/gi;
94
+ const CAUSE = /^\s*(?:bloqu[eé]e? par|blocked by)\s*:\s*(.+)$/i;
95
+ const ANSI = /\u001b\[[0-?]*[ -/]*[@-~]/g;
96
+ const CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2066-\u2069]/g;
97
+ const MAX_LINE = 2e3;
26
98
  const BADGE_COLORS = ["red", "orange", "amber", "green", "teal", "blue", "violet", "pink", "gray"];
27
99
  const BADGE = /\[b:\s*([#\w-]*)\s*\((.*?)\)\](?:\(\s*((?:[^\s()]|\([^\s()]*\))*)\s*\))?/gi;
28
100
  function badgeColor(raw) {
@@ -47,32 +119,36 @@ function badgeTarget(raw) {
47
119
  }
48
120
  function takeBadges(input) {
49
121
  const badges = [];
50
- const text = input.replace(BADGE, (all, color, label, target) => {
122
+ const text = input.replace(BADGE, (all, color, label, target2) => {
51
123
  const t = label.replace(/\s+/g, " ").trim();
52
124
  if (!t) return all;
53
- badges.push({ text: t, color: badgeColor(color), ...badgeTarget(target) || {} });
125
+ badges.push({ text: t, color: badgeColor(color), ...badgeTarget(target2) || {} });
54
126
  return " ";
55
127
  });
56
128
  return badges.length ? { text: text.replace(/\s{2,}/g, " ").trim(), badges } : { text: input, badges };
57
129
  }
58
130
  function parseTaskLine(line, kind = null) {
59
131
  var _a;
60
- const m = TASK_LINE.exec(line.trim());
132
+ if (line.length > MAX_LINE) return null;
133
+ const m = TASK_LINE.exec(line.replace(ANSI, "").replace(/\t/g, " ").replace(CONTROL, "").trim());
61
134
  if (!m) return null;
62
135
  const free = takeBadges(m[2].trim());
63
136
  let text = free.text.replace(REFS, "").trim();
64
137
  let owner = null;
65
138
  const o = OWNER.exec(text);
66
- if (o && o.index > 0 && !(text[o.index] === "(" && text[o.index - 1] === "]")) {
139
+ if (o && o.index > 0 && text[o.index - 1] !== "]") {
67
140
  owner = o[1].trim();
68
141
  text = text.slice(0, o.index).trim();
69
142
  }
70
143
  let reason;
71
144
  if (kind === "blocked") {
72
- const cause = /\s+—\s*(?:bloqu[eé]e? par|blocked by)\s*:\s*(.+)$/i.exec(text);
73
- if ((_a = cause == null ? void 0 : cause[1]) == null ? void 0 : _a.trim()) {
145
+ for (let at = text.indexOf("\u2014"); at > 0; at = text.indexOf("\u2014", at + 1)) {
146
+ if (!/\s/.test(text[at - 1])) continue;
147
+ const cause = CAUSE.exec(text.slice(at + 1));
148
+ if (!((_a = cause == null ? void 0 : cause[1]) == null ? void 0 : _a.trim())) continue;
74
149
  reason = cause[1].trim();
75
- text = text.slice(0, cause.index).trim();
150
+ text = text.slice(0, at).trim();
151
+ break;
76
152
  }
77
153
  }
78
154
  if (!text) text = free.badges.map((b) => b.text).join(" \xB7 ");
@@ -80,19 +156,27 @@ function parseTaskLine(line, kind = null) {
80
156
  const ref = owner && THREAD_ID.exec(owner);
81
157
  return { text, done: m[1] === "x" || m[1] === "X", owner, thread: ref ? ref[0].toLowerCase() : null, ...reason ? { reason } : {}, ...free.badges.length ? { badges: free.badges } : {} };
82
158
  }
159
+ function headingTitle(raw) {
160
+ var _a;
161
+ if (!raw.startsWith("##") || !/\s/.test((_a = raw[2]) != null ? _a : "")) return null;
162
+ const body = raw.slice(2).trim();
163
+ if (!body) return null;
164
+ return body.replace(/#+$/, "").trim() || body[0];
165
+ }
83
166
  function parseTasks(md) {
84
167
  const lists = [];
85
168
  let cur = null;
86
169
  let fence = false;
87
170
  for (const raw of String(md || "").replace(/\r\n?/g, "\n").split("\n")) {
171
+ if (raw.length > MAX_LINE) continue;
88
172
  if (/^\s*(```|~~~)/.test(raw)) {
89
173
  fence = !fence;
90
174
  continue;
91
175
  }
92
176
  if (fence) continue;
93
- const h = /^##\s+(.+?)\s*#*\s*$/.exec(raw);
94
- if (h) {
95
- cur = { title: h[1], kind: listKind(h[1]), tasks: [] };
177
+ const title = headingTitle(raw);
178
+ if (title) {
179
+ cur = { title, kind: listKind(title), tasks: [] };
96
180
  lists.push(cur);
97
181
  continue;
98
182
  }
@@ -128,6 +212,8 @@ function normalizeThreads(raw) {
128
212
  created: str(r.created),
129
213
  agentName: str(r.agent_name) || null,
130
214
  machine: str(r.machine),
215
+ profile: str(r.profile) || str(r.agent),
216
+ stateLine: str(r.state_line).trim(),
131
217
  activity: str(r.activity).trim(),
132
218
  percent: typeof r.percent === "number" && Number.isFinite(r.percent) ? Math.max(0, Math.min(100, Math.round(r.percent))) : null,
133
219
  pr: /^https:\/\//.test(str(r.pr)) ? str(r.pr) : "",
@@ -156,10 +242,9 @@ function splitThreads(list) {
156
242
  function maxParallelThreads(projectMd) {
157
243
  const m = /^\+\+\+\r?\n([\s\S]*?)\r?\n\+\+\+/.exec(String(projectMd || ""));
158
244
  if (!m) return null;
159
- const top = m[1].split(/^\s*\[/m)[0];
160
- const v = /^\s*max_parallel_threads\s*=\s*(\d+)\s*(?:#.*)?$/m.exec(top);
161
- if (!v) return 10;
162
- const n = Number(v[1]);
245
+ const v = tomlTables(m[1])[0].bare.max_parallel_threads;
246
+ if (v === void 0 || !/^\d+$/.test(v)) return 10;
247
+ const n = Number(v);
163
248
  return n >= 1 ? n : null;
164
249
  }
165
250
 
@@ -172,13 +257,9 @@ const PLUGIN_ID = "herdr-projects";
172
257
  function pluginBinary(plugins) {
173
258
  const p = (plugins || []).find((x) => x && x.plugin_id === PLUGIN_ID && x.enabled !== false);
174
259
  if (!p || typeof p.plugin_root !== "string" || !path.posix.isAbsolute(p.plugin_root)) return null;
175
- for (const c of [...p.startup || [], ...p.actions || [], ...p.panes || []]) {
176
- const argv = Array.isArray(c == null ? void 0 : c.command) ? c.command : [];
177
- const bin = typeof argv[0] === "string" ? argv[0] : "";
178
- if (!/(^|\/)herdr-projects$/.test(bin) || bin.split("/").includes("..")) continue;
179
- return path.posix.isAbsolute(bin) ? bin : path.posix.join(p.plugin_root, bin);
180
- }
181
- return null;
260
+ const bin = manifestBinary(p);
261
+ if (!bin) return null;
262
+ return path.posix.isAbsolute(bin) ? bin : path.posix.join(p.plugin_root, bin);
182
263
  }
183
264
  const bins = /* @__PURE__ */ new Map();
184
265
  function binaryOn(m) {
@@ -210,27 +291,34 @@ async function version(t) {
210
291
  const v = `${tasks ? `${tasks.size}.${tasks.mtimeMs}` : "-"}|${threads ? threads.mtimeMs : "-"}|${project ? project.mtimeMs : "-"}`;
211
292
  return { v, tasks: (tasks == null ? void 0 : tasks.isFile) ? tasks : null, project: (project == null ? void 0 : project.isFile) ? project : null };
212
293
  }
213
- function threadList(t) {
214
- const root = path.posix.dirname(t.dir);
294
+ const REMOTE_RUN = 'export HERDR_BIN_PATH="$1" HERDR_SESSION="$2"; bin="$3"; shift 3; exec "$bin" "$@"';
295
+ function runProjectCommand(t, args, timeoutMs = 15e3, maxBytes = 1024 * 1024) {
215
296
  const session = t.m.session && t.m.session !== "default" ? t.m.session : "";
216
297
  const remote = t.m.exec;
217
298
  if (remote) {
218
299
  const herdrBin = t.m.bin || "herdr";
219
- return remote('HERDR_BIN_PATH="$1" HERDR_SESSION="$2" exec "$3" --root "$4" thread list "$5" --json', [herdrBin, session, t.bin, root, t.slug], { timeoutMs: 15e3 }).then((r) => {
220
- if (r.code !== 0) throw new Error(r.stderr.trim().split("\n").pop() || `code ${r.code}`);
221
- if (r.stdout.length > THREADS_BYTES) throw new Error("thread list too long");
222
- return r.stdout.toString("utf8");
300
+ return remote(REMOTE_RUN, [herdrBin, session, t.bin, ...args], { timeoutMs }).then((r) => {
301
+ var _a;
302
+ return r.stdout.length > maxBytes ? { code: 1, stdout: "", stderr: "output too long" } : { code: (_a = r.code) != null ? _a : 1, stdout: r.stdout.toString("utf8"), stderr: r.stderr };
223
303
  });
224
304
  }
225
305
  const env = { ...HERDR_CHILD_ENV, HERDR_BIN_PATH: HERDR_BIN };
226
306
  if (session) env.HERDR_SESSION = session;
227
- return new Promise((resolve, reject) => {
228
- execFile(t.bin, ["--root", root, "thread", "list", t.slug, "--json"], { env, timeout: 15e3, maxBuffer: THREADS_BYTES }, (err, out, stderr) => {
229
- if (err) reject(new Error(String(stderr || err.message).trim().split("\n").pop()));
230
- else resolve(String(out));
231
- });
307
+ return new Promise((resolve) => {
308
+ execFile(t.bin, args, { env, timeout: timeoutMs, maxBuffer: maxBytes }, (err, out, stderr) => resolve({
309
+ code: err ? typeof err.code === "number" ? err.code : 1 : 0,
310
+ stdout: String(out),
311
+ stderr: String(stderr || (err ? err.message : ""))
312
+ }));
232
313
  });
233
314
  }
315
+ const projectsRoot = (t) => path.posix.dirname(t.dir);
316
+ async function threadList(t) {
317
+ const r = await runProjectCommand(t, ["--root", projectsRoot(t), "thread", "list", t.slug, "--json"], 15e3, THREADS_BYTES);
318
+ if (r.code !== 0) throw new Error(r.stderr.trim().split("\n").pop() || `code ${r.code}`);
319
+ if (r.stdout.length > THREADS_BYTES) throw new Error("thread list too long");
320
+ return r.stdout;
321
+ }
234
322
  const lastThreads = /* @__PURE__ */ new Map();
235
323
  async function readProjectBoard(pane, since) {
236
324
  const t = await target(pane);
@@ -271,5 +359,5 @@ async function readThreadReport(pane, id) {
271
359
  return { id, text, truncated: st.size > REPORT_BYTES };
272
360
  }
273
361
 
274
- export { readThreadReport as a, binaryOn as b, readProjectBoard as r };
362
+ export { target as a, binaryOn as b, runProjectCommand as c, tomlTables as d, readThreadReport as e, normalizeThreads as n, projectsRoot as p, readProjectBoard as r, tomlStrings as t };
275
363
  //# sourceMappingURL=projectBoard.mjs.map
@@ -0,0 +1,106 @@
1
+ import { d as tomlTables, a as target } from './projectBoard.mjs';
2
+ import { H as HerdrError } from '../nitro/nitro.mjs';
3
+
4
+ const front = (md) => {
5
+ var _a, _b;
6
+ return (_b = (_a = /^\+\+\+\r?\n([\s\S]*?)\r?\n\+\+\+/.exec(String(md || ""))) == null ? void 0 : _a[1]) != null ? _b : null;
7
+ };
8
+ const cut = (s, max) => (s || "").replace(/\s+/g, " ").trim().slice(0, max);
9
+ function parseProjectSettings(projectMd) {
10
+ var _a, _b;
11
+ const f = front(projectMd);
12
+ if (f === null) return null;
13
+ const tables = tomlTables(f);
14
+ const strings = tables[0].strings;
15
+ const raw = (key) => tables[0].bare[key];
16
+ const defaults = [];
17
+ const pick = (key, value, fallback) => {
18
+ if (value !== void 0) return value;
19
+ defaults.push(key);
20
+ return fallback;
21
+ };
22
+ const int = (key) => {
23
+ const v = raw(key);
24
+ return v !== void 0 && /^\d{1,6}$/.test(v) ? Number(v) : void 0;
25
+ };
26
+ const bool = (key) => {
27
+ const v = raw(key);
28
+ return v === "true" ? true : v === "false" ? false : void 0;
29
+ };
30
+ const settings = {
31
+ name: cut(pick("name", strings.name, ""), 200),
32
+ goal: cut(pick("goal", strings.goal, ""), 2e3),
33
+ // `*_agent`: the keys' names before profiles.
34
+ coordinatorProfile: cut(pick("coordinatorProfile", (_a = strings.coordinator_profile) != null ? _a : strings.coordinator_agent, "claude"), 60),
35
+ threadProfile: cut(pick("threadProfile", (_b = strings.thread_profile) != null ? _b : strings.thread_agent, "claude"), 60),
36
+ maxParallelThreads: pick("maxParallelThreads", int("max_parallel_threads"), 10),
37
+ autoResolveDays: pick("autoResolveDays", int("auto_resolve_days"), 7),
38
+ nudge: pick("nudge", bool("nudge"), true),
39
+ mute: pick("mute", bool("mute"), false),
40
+ repos: tables.filter((t) => t.name === "repos").slice(0, 50).map((t) => ({ path: cut(t.strings.path, 1024), machine: cut(t.strings.machine, 120) })).filter((r) => r.path),
41
+ defaults
42
+ };
43
+ return settings;
44
+ }
45
+ const MEMORY_INDEX = "MEMORY.md";
46
+ const MEMORY_FILE_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,119}\.md$/;
47
+ const memoryFileOk = (name) => typeof name === "string" && MEMORY_FILE_RE.test(name) && !name.includes("..");
48
+ function parseMemoryIndex(indexMd) {
49
+ const out = [];
50
+ for (const line of String(indexMd || "").replace(/\r\n?/g, "\n").split("\n")) {
51
+ const m = /^\s*[-*+]\s+\[([^\]]+)\]\((?:\.\/)?memory\/([^)\s]+)\)\s*(?:[:—–-]\s*)?(.*)$/.exec(line);
52
+ if (!m || !memoryFileOk(m[2]) || out.some((x) => x.name === m[2])) continue;
53
+ out.push({ name: m[2], title: cut(m[1], 160), hook: cut(m[3], 400) });
54
+ }
55
+ return out;
56
+ }
57
+ function memoryFiles(indexMd, names) {
58
+ const files = names.filter(memoryFileOk);
59
+ const listed = parseMemoryIndex(indexMd || "");
60
+ const out = [];
61
+ if (indexMd !== null) out.push({ name: MEMORY_INDEX, title: MEMORY_INDEX, hook: "", index: true });
62
+ for (const l of listed) out.push(files.includes(l.name) ? l : { ...l, missing: true });
63
+ for (const name of files.filter((n) => !listed.some((l) => l.name === n)).sort()) out.push({ name, title: name, hook: "" });
64
+ return out;
65
+ }
66
+
67
+ const PROJECT_HEAD_BYTES = 32 * 1024;
68
+ const INDEX_BYTES = 128 * 1024;
69
+ const MEMORY_BYTES = 256 * 1024;
70
+ const MEMORY_FILES_MAX = 300;
71
+ async function head(t, file, max) {
72
+ const st = await t.m.fs.stat(file).catch(() => null);
73
+ if (!st || !st.isFile) return null;
74
+ const text = (await t.m.fs.read(file, 0, Math.min(st.size, max))).toString("utf8");
75
+ return { text, truncated: st.size > max };
76
+ }
77
+ async function settingsOn(t) {
78
+ const file = await head(t, `${t.dir}/PROJECT.md`, PROJECT_HEAD_BYTES);
79
+ const settings = file && parseProjectSettings(file.text);
80
+ if (!settings) throw new HerdrError("no_settings", "project settings not found");
81
+ return { settings };
82
+ }
83
+ async function memoryOn(t) {
84
+ const [index, names] = await Promise.all([
85
+ head(t, `${t.dir}/${MEMORY_INDEX}`, INDEX_BYTES),
86
+ t.m.fs.readdir(`${t.dir}/memory`).catch(() => [])
87
+ ]);
88
+ return { files: memoryFiles(index ? index.text : null, names.slice(0, MEMORY_FILES_MAX * 4)).slice(0, MEMORY_FILES_MAX) };
89
+ }
90
+ async function memoryFileOn(t, name) {
91
+ if (name !== MEMORY_INDEX && !memoryFileOk(name)) throw new HerdrError("bad_memory", "invalid memory file");
92
+ const file = await head(t, name === MEMORY_INDEX ? `${t.dir}/${MEMORY_INDEX}` : `${t.dir}/memory/${name}`, MEMORY_BYTES);
93
+ if (!file) throw new HerdrError("no_memory", "memory file not found");
94
+ return { name, ...file };
95
+ }
96
+ async function need(pane) {
97
+ const t = await target(pane);
98
+ if (!t) throw new HerdrError("no_project", "project not found");
99
+ return t;
100
+ }
101
+ const readProjectSettings = async (pane) => settingsOn(await need(pane));
102
+ const readProjectMemory = async (pane) => memoryOn(await need(pane));
103
+ const readMemoryFile = async (pane, name) => memoryFileOn(await need(pane), name);
104
+
105
+ export { readMemoryFile as a, readProjectSettings as b, readProjectMemory as r };
106
+ //# sourceMappingURL=projectDocs.mjs.map
@@ -0,0 +1,135 @@
1
+ import { p as projectSlugOk, l as lastLine } from './projectThreads.mjs';
2
+ import { t as tomlStrings, a as target, p as projectsRoot, c as runProjectCommand } from './projectBoard.mjs';
3
+ import { H as HerdrError } from '../nitro/nitro.mjs';
4
+ import { s as stripAnsi } from './pluginPolicy.mjs';
5
+
6
+ const INBOX_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/;
7
+ const inboxIdOk = (id) => typeof id === "string" && INBOX_ID_RE.test(id) && !id.includes("..");
8
+ const INBOX_BODY_MAX = 2e3;
9
+ function parseInboxItem(fileText) {
10
+ const text = String(fileText || "").replace(/\r\n?/g, "\n");
11
+ if (!text.startsWith("+++\n")) return null;
12
+ const rest = text.slice(4);
13
+ const cut = rest.indexOf("\n+++\n");
14
+ const front = cut >= 0 ? rest.slice(0, cut) : rest.endsWith("\n+++") ? rest.slice(0, -4) : null;
15
+ if (front === null) return null;
16
+ const f = tomlStrings(front);
17
+ if (!inboxIdOk(f.id)) return null;
18
+ const oneLine = (s, max) => (s || "").replace(/\s+/g, " ").trim().slice(0, max);
19
+ const body = cut >= 0 ? rest.slice(cut + 5).replace(/^\n+|\n+$/g, "") : "";
20
+ return {
21
+ id: f.id,
22
+ kind: oneLine(f.kind, 40),
23
+ subject: oneLine(f.subject, 120),
24
+ event: oneLine(f.event, 120),
25
+ summary: oneLine(f.summary, 1e3),
26
+ created: oneLine(f.created, 40),
27
+ body: body.length > INBOX_BODY_MAX ? `${body.slice(0, INBOX_BODY_MAX)}\u2026` : body
28
+ };
29
+ }
30
+ const sortInbox = (items) => [...items].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
31
+ function inboxDoneArgs(root, slug, ids) {
32
+ if (!root.startsWith("/")) throw new Error("invalid projects root");
33
+ if (!projectSlugOk(slug)) throw new Error("invalid project");
34
+ if (!ids.length || ids.length > 50 || !ids.every(inboxIdOk)) throw new Error("invalid inbox item");
35
+ return ["--root", root, "inbox", "done", slug, ...ids];
36
+ }
37
+ function routineListArgs(root, slug) {
38
+ if (!root.startsWith("/")) throw new Error("invalid projects root");
39
+ if (!projectSlugOk(slug)) throw new Error("invalid project");
40
+ return ["--root", root, "routine", "list", slug];
41
+ }
42
+ function parseRoutineList(output) {
43
+ const routines = [];
44
+ const broken = [];
45
+ for (const line of String(output || "").replace(/\r\n?/g, "\n").split("\n")) {
46
+ const cols = line.split(" ");
47
+ if (cols.length === 2 && cols[1].startsWith("config-error:")) {
48
+ broken.push({ file: cols[0].trim().slice(0, 120), error: cols[1].slice("config-error:".length).trim().slice(0, 300) });
49
+ continue;
50
+ }
51
+ if (cols.length !== 5 || !cols[0].trim() || cols[2] !== "enabled" && cols[2] !== "disabled") continue;
52
+ routines.push({
53
+ name: cols[0].trim().slice(0, 120),
54
+ schedule: cols[1].trim().slice(0, 120),
55
+ enabled: cols[2] === "enabled",
56
+ kind: cols[3].trim().slice(0, 160),
57
+ when: cols[4].split(" \xB7 ").map((s) => s.trim().slice(0, 160)).filter(Boolean)
58
+ });
59
+ }
60
+ return { routines, broken };
61
+ }
62
+
63
+ const INBOX_MAX = 100;
64
+ const ITEM_BYTES = 8 * 1024;
65
+ const READS_AT_ONCE = 8;
66
+ const seen = /* @__PURE__ */ new Map();
67
+ const SEEN_MAX = 2e3;
68
+ async function inboxOn(t) {
69
+ var _a;
70
+ const dir = `${t.dir}/inbox`;
71
+ const names = await t.m.fs.readdir(dir).catch(() => []);
72
+ const ids = names.filter((n) => n.endsWith(".md")).map((n) => n.slice(0, -3)).filter(inboxIdOk).sort();
73
+ const items = [];
74
+ const wanted = ids.slice(0, INBOX_MAX);
75
+ const stats = await t.m.fs.statMany(wanted.map((id) => `${dir}/${id}.md`));
76
+ const base = `${(_a = t.m.key) != null ? _a : ""}|${dir}/`;
77
+ const todo = [];
78
+ const found = /* @__PURE__ */ new Map();
79
+ wanted.forEach((id, i) => {
80
+ const st = stats[i];
81
+ if (!st || !st.isFile || st.size <= 0) return;
82
+ const known = seen.get(base + id);
83
+ if (known && known.size === st.size && known.mtimeMs === st.mtimeMs) {
84
+ if (known.item) found.set(id, known.item);
85
+ } else todo.push({ id, size: st.size, mtimeMs: st.mtimeMs });
86
+ });
87
+ for (let i = 0; i < todo.length; i += READS_AT_ONCE) {
88
+ await Promise.all(todo.slice(i, i + READS_AT_ONCE).map(async ({ id, size, mtimeMs }) => {
89
+ const text = await t.m.fs.read(`${dir}/${id}.md`, 0, Math.min(size, ITEM_BYTES)).then((b) => b.toString("utf8")).catch(() => null);
90
+ if (text === null) return;
91
+ const parsed = parseInboxItem(text.length >= ITEM_BYTES && !text.includes("\n+++\n") ? "" : text);
92
+ const item = parsed && parsed.id === id ? parsed : null;
93
+ seen.set(base + id, { size, mtimeMs, item });
94
+ if (item) found.set(id, item);
95
+ }));
96
+ }
97
+ for (const item of found.values()) items.push(item);
98
+ const here = new Set(ids);
99
+ for (const key of seen.keys()) if (key.startsWith(base) && !here.has(key.slice(base.length))) seen.delete(key);
100
+ while (seen.size > SEEN_MAX) seen.delete(seen.keys().next().value);
101
+ return { items: sortInbox(items), total: items.length + (ids.length - wanted.length) };
102
+ }
103
+ async function inboxDoneOn(t, id) {
104
+ let args;
105
+ try {
106
+ args = inboxDoneArgs(projectsRoot(t), t.slug, [id]);
107
+ } catch (e) {
108
+ throw new HerdrError("bad_item", e.message);
109
+ }
110
+ const r = await runProjectCommand(t, args, 15e3);
111
+ if (r.code !== 0) throw new HerdrError("inbox_failed", lastLine(stripAnsi(r.stderr || r.stdout)) || `code ${r.code}`);
112
+ return { ok: true };
113
+ }
114
+ async function routinesOn(t) {
115
+ let args;
116
+ try {
117
+ args = routineListArgs(projectsRoot(t), t.slug);
118
+ } catch (e) {
119
+ throw new HerdrError("bad_project", e.message);
120
+ }
121
+ const r = await runProjectCommand(t, args, 15e3);
122
+ if (r.code !== 0) throw new HerdrError("routines_failed", lastLine(stripAnsi(r.stderr || r.stdout)) || `code ${r.code}`);
123
+ return parseRoutineList(stripAnsi(r.stdout));
124
+ }
125
+ async function need(pane) {
126
+ const t = await target(pane);
127
+ if (!t) throw new HerdrError("no_project", "project not found");
128
+ return t;
129
+ }
130
+ const readInbox = async (pane) => inboxOn(await need(pane));
131
+ const markInboxDone = async (pane, id) => inboxDoneOn(await need(pane), id);
132
+ const readRoutines = async (pane) => routinesOn(await need(pane));
133
+
134
+ export { readRoutines as a, markInboxDone as m, readInbox as r };
135
+ //# sourceMappingURL=projectInbox.mjs.map
@@ -0,0 +1,46 @@
1
+ import { n as normalizeThreads } from './projectBoard.mjs';
2
+
3
+ const THREAD_ID_RE = /^t-\d{4,}$/;
4
+ const PROJECT_SLUG_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,119}$/;
5
+ const projectSlugOk = (slug) => PROJECT_SLUG_RE.test(slug) && !slug.includes("..");
6
+ const THREAD_ACTIONS = ["stop", "restart", "ack", "resolve"];
7
+ const isThreadAction = (v) => THREAD_ACTIONS.includes(v);
8
+ const THREAD_TIMEOUT_MS = { show: 15e3, stop: 2e4, ack: 15e3, restart: 9e4, resolve: 18e4 };
9
+ function threadCommandArgs(command, root, slug, id) {
10
+ if (!root.startsWith("/")) throw new Error("invalid projects root");
11
+ if (!projectSlugOk(slug)) throw new Error("invalid project");
12
+ if (!THREAD_ID_RE.test(id)) throw new Error("invalid thread");
13
+ if (command !== "show" && !isThreadAction(command)) throw new Error("invalid action");
14
+ return ["--root", root, "thread", command, slug, id, ...command === "show" ? ["--json"] : []];
15
+ }
16
+ const str = (v) => typeof v === "string" ? v : "";
17
+ const NEXT_MAX = 20;
18
+ function normalizeThreadDetail(raw) {
19
+ const base = normalizeThreads([raw])[0];
20
+ if (!base) return null;
21
+ const r = raw;
22
+ const hash = str(r.report_hash);
23
+ return {
24
+ ...base,
25
+ note: str(r.note).trim(),
26
+ error: str(r.error).trim().slice(0, 600),
27
+ kind: str(r.kind),
28
+ agent: str(r.agent),
29
+ branch: str(r.branch),
30
+ base: str(r.base),
31
+ prState: str(r.pr_state),
32
+ prReview: str(r.pr_review),
33
+ next: (Array.isArray(r.next) ? r.next : []).filter((x) => typeof x === "string" && Boolean(x.trim())).slice(0, NEXT_MAX).map((x) => x.trim().slice(0, 300)),
34
+ acked: Boolean(hash) && str(r.acked_report_hash) === hash,
35
+ hasPane: Boolean(str(r.pane_id)),
36
+ keptWorktree: r.kept_worktree === true,
37
+ resolvedReason: str(r.resolved_reason)
38
+ };
39
+ }
40
+ function lastLine(text) {
41
+ const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
42
+ return (lines[lines.length - 1] || "").replace(/^error:\s*/i, "").slice(0, 400);
43
+ }
44
+
45
+ export { THREAD_ID_RE as T, THREAD_TIMEOUT_MS as a, isThreadAction as i, lastLine as l, normalizeThreadDetail as n, projectSlugOk as p, threadCommandArgs as t };
46
+ //# sourceMappingURL=projectThreads.mjs.map
@@ -0,0 +1,61 @@
1
+ import { i as isThreadAction, T as THREAD_ID_RE, l as lastLine, n as normalizeThreadDetail, a as THREAD_TIMEOUT_MS, t as threadCommandArgs } from './projectThreads.mjs';
2
+ import { H as HerdrError } from '../nitro/nitro.mjs';
3
+ import { s as stripAnsi } from './pluginPolicy.mjs';
4
+ import { a as target, c as runProjectCommand, p as projectsRoot } from './projectBoard.mjs';
5
+
6
+ function argsFor(command, t, id) {
7
+ try {
8
+ return threadCommandArgs(command, projectsRoot(t), t.slug, id);
9
+ } catch (e) {
10
+ throw new HerdrError("bad_thread", e.message);
11
+ }
12
+ }
13
+ async function threadDetailOn(t, id) {
14
+ const r = await runProjectCommand(t, argsFor("show", t, id), THREAD_TIMEOUT_MS.show);
15
+ if (r.code !== 0) throw new HerdrError("thread_failed", lastLine(stripAnsi(r.stderr || r.stdout)) || `code ${r.code}`);
16
+ let raw;
17
+ try {
18
+ raw = JSON.parse(r.stdout);
19
+ } catch {
20
+ throw new HerdrError("thread_failed", "unreadable thread record");
21
+ }
22
+ const detail = normalizeThreadDetail(raw);
23
+ if (!detail || detail.id !== id) throw new HerdrError("thread_failed", "unreadable thread record");
24
+ return detail;
25
+ }
26
+ const busy = /* @__PURE__ */ new Set();
27
+ async function threadActionOn(t, id, action) {
28
+ var _a;
29
+ const args = argsFor(action, t, id);
30
+ const key = `${(_a = t.m.key) != null ? _a : ""}|${t.dir}|${id}`;
31
+ if (busy.has(key)) throw new HerdrError("thread_busy", "an action is already running on this thread");
32
+ busy.add(key);
33
+ try {
34
+ const r = await runProjectCommand(t, args, THREAD_TIMEOUT_MS[action]);
35
+ const out = stripAnsi(r.stdout).trim();
36
+ if (r.code !== 0) {
37
+ throw new HerdrError("thread_failed", lastLine(stripAnsi(r.stderr || r.stdout)) || `code ${r.code}`);
38
+ }
39
+ return { output: out.slice(-2e3) };
40
+ } finally {
41
+ busy.delete(key);
42
+ }
43
+ }
44
+ async function where(pane, id) {
45
+ if (typeof id !== "string" || !THREAD_ID_RE.test(id)) throw new HerdrError("bad_thread", "invalid thread");
46
+ const t = await target(pane);
47
+ if (!t) throw new HerdrError("no_project", "project not found");
48
+ return { t, id };
49
+ }
50
+ async function readThreadDetail(pane, rawId) {
51
+ const { t, id } = await where(pane, rawId);
52
+ return { thread: await threadDetailOn(t, id) };
53
+ }
54
+ async function runThreadAction(pane, rawId, action, guard = (_t, run) => run()) {
55
+ if (!isThreadAction(action)) throw new HerdrError("bad_action", "Invalid action");
56
+ const { t, id } = await where(pane, rawId);
57
+ return guard(t, () => threadActionOn(t, id, action));
58
+ }
59
+
60
+ export { runThreadAction as a, readThreadDetail as r };
61
+ //# sourceMappingURL=projectThreads2.mjs.map
@@ -1,4 +1,4 @@
1
- import { o as fmt } from '../nitro/nitro.mjs';
1
+ import { G as fmt } from '../nitro/nitro.mjs';
2
2
 
3
3
  const PROJECT_INPUTS = {
4
4
  new: ["name"],