@spader/spall-cli 1.0.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 (123) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/scripts/postinstall.js +38 -0
  4. package/dist/scripts/postinstall.js.map +10 -0
  5. package/dist/shared/display.d.ts +47 -0
  6. package/dist/shared/display.d.ts.map +1 -0
  7. package/dist/shared/help.d.ts +5 -0
  8. package/dist/shared/help.d.ts.map +1 -0
  9. package/dist/shared/index.d.ts +14 -0
  10. package/dist/shared/index.d.ts.map +1 -0
  11. package/dist/shared/layout.d.ts +16 -0
  12. package/dist/shared/layout.d.ts.map +1 -0
  13. package/dist/shared/list.d.ts +25 -0
  14. package/dist/shared/list.d.ts.map +1 -0
  15. package/dist/shared/progress.d.ts +4 -0
  16. package/dist/shared/progress.d.ts.map +1 -0
  17. package/dist/shared/search.d.ts +27 -0
  18. package/dist/shared/search.d.ts.map +1 -0
  19. package/dist/shared/status.d.ts +26 -0
  20. package/dist/shared/status.d.ts.map +1 -0
  21. package/dist/shared/theme.d.ts +20 -0
  22. package/dist/shared/theme.d.ts.map +1 -0
  23. package/dist/shared/tree.d.ts +37 -0
  24. package/dist/shared/tree.d.ts.map +1 -0
  25. package/dist/shared/vsearch.d.ts +28 -0
  26. package/dist/shared/vsearch.d.ts.map +1 -0
  27. package/dist/shared/workspace.d.ts +28 -0
  28. package/dist/shared/workspace.d.ts.map +1 -0
  29. package/dist/shared/yargs.d.ts +34 -0
  30. package/dist/shared/yargs.d.ts.map +1 -0
  31. package/dist/spall/commands/add.d.ts +20 -0
  32. package/dist/spall/commands/add.d.ts.map +1 -0
  33. package/dist/spall/commands/commit.d.ts +3 -0
  34. package/dist/spall/commands/commit.d.ts.map +1 -0
  35. package/dist/spall/commands/corpus/create.d.ts +3 -0
  36. package/dist/spall/commands/corpus/create.d.ts.map +1 -0
  37. package/dist/spall/commands/corpus/delete.d.ts +3 -0
  38. package/dist/spall/commands/corpus/delete.d.ts.map +1 -0
  39. package/dist/spall/commands/corpus/index.d.ts +3 -0
  40. package/dist/spall/commands/corpus/index.d.ts.map +1 -0
  41. package/dist/spall/commands/get.d.ts +3 -0
  42. package/dist/spall/commands/get.d.ts.map +1 -0
  43. package/dist/spall/commands/hook.d.ts +3 -0
  44. package/dist/spall/commands/hook.d.ts.map +1 -0
  45. package/dist/spall/commands/index.d.ts +15 -0
  46. package/dist/spall/commands/index.d.ts.map +1 -0
  47. package/dist/spall/commands/integrate/index.d.ts +8 -0
  48. package/dist/spall/commands/integrate/index.d.ts.map +1 -0
  49. package/dist/spall/commands/integrate/opencode.d.ts +3 -0
  50. package/dist/spall/commands/integrate/opencode.d.ts.map +1 -0
  51. package/dist/spall/commands/integrate/shell.d.ts +6 -0
  52. package/dist/spall/commands/integrate/shell.d.ts.map +1 -0
  53. package/dist/spall/commands/list.d.ts +3 -0
  54. package/dist/spall/commands/list.d.ts.map +1 -0
  55. package/dist/spall/commands/review/comments.d.ts +3 -0
  56. package/dist/spall/commands/review/comments.d.ts.map +1 -0
  57. package/dist/spall/commands/review/create.d.ts +3 -0
  58. package/dist/spall/commands/review/create.d.ts.map +1 -0
  59. package/dist/spall/commands/review/index.d.ts +3 -0
  60. package/dist/spall/commands/review/index.d.ts.map +1 -0
  61. package/dist/spall/commands/review/latest.d.ts +3 -0
  62. package/dist/spall/commands/review/latest.d.ts.map +1 -0
  63. package/dist/spall/commands/review/list.d.ts +3 -0
  64. package/dist/spall/commands/review/list.d.ts.map +1 -0
  65. package/dist/spall/commands/review/patches.d.ts +3 -0
  66. package/dist/spall/commands/review/patches.d.ts.map +1 -0
  67. package/dist/spall/commands/search.d.ts +3 -0
  68. package/dist/spall/commands/search.d.ts.map +1 -0
  69. package/dist/spall/commands/serve.d.ts +3 -0
  70. package/dist/spall/commands/serve.d.ts.map +1 -0
  71. package/dist/spall/commands/status.d.ts +3 -0
  72. package/dist/spall/commands/status.d.ts.map +1 -0
  73. package/dist/spall/commands/sync.d.ts +3 -0
  74. package/dist/spall/commands/sync.d.ts.map +1 -0
  75. package/dist/spall/commands/tui.d.ts +3 -0
  76. package/dist/spall/commands/tui.d.ts.map +1 -0
  77. package/dist/spall/commands/vsearch.d.ts +3 -0
  78. package/dist/spall/commands/vsearch.d.ts.map +1 -0
  79. package/dist/spall/commands/workspace/add.d.ts +3 -0
  80. package/dist/spall/commands/workspace/add.d.ts.map +1 -0
  81. package/dist/spall/commands/workspace/edit.d.ts +3 -0
  82. package/dist/spall/commands/workspace/edit.d.ts.map +1 -0
  83. package/dist/spall/commands/workspace/index.d.ts +3 -0
  84. package/dist/spall/commands/workspace/index.d.ts.map +1 -0
  85. package/dist/spall/commands/workspace/init.d.ts +3 -0
  86. package/dist/spall/commands/workspace/init.d.ts.map +1 -0
  87. package/dist/spall/commands/workspace/remove.d.ts +3 -0
  88. package/dist/spall/commands/workspace/remove.d.ts.map +1 -0
  89. package/dist/spall/e2e.preload.d.ts +2 -0
  90. package/dist/spall/e2e.preload.d.ts.map +1 -0
  91. package/dist/spall/index.d.ts +3 -0
  92. package/dist/spall/index.d.ts.map +1 -0
  93. package/dist/spallm/commands/add.d.ts +3 -0
  94. package/dist/spallm/commands/add.d.ts.map +1 -0
  95. package/dist/spallm/commands/fetch.d.ts +3 -0
  96. package/dist/spallm/commands/fetch.d.ts.map +1 -0
  97. package/dist/spallm/commands/get.d.ts +3 -0
  98. package/dist/spallm/commands/get.d.ts.map +1 -0
  99. package/dist/spallm/commands/list.d.ts +3 -0
  100. package/dist/spallm/commands/list.d.ts.map +1 -0
  101. package/dist/spallm/commands/prime.d.ts +3 -0
  102. package/dist/spallm/commands/prime.d.ts.map +1 -0
  103. package/dist/spallm/commands/query.d.ts +3 -0
  104. package/dist/spallm/commands/query.d.ts.map +1 -0
  105. package/dist/spallm/commands/review.d.ts +3 -0
  106. package/dist/spallm/commands/review.d.ts.map +1 -0
  107. package/dist/spallm/commands/search.d.ts +3 -0
  108. package/dist/spallm/commands/search.d.ts.map +1 -0
  109. package/dist/spallm/commands/status.d.ts +3 -0
  110. package/dist/spallm/commands/status.d.ts.map +1 -0
  111. package/dist/spallm/commands/vsearch.d.ts +3 -0
  112. package/dist/spallm/commands/vsearch.d.ts.map +1 -0
  113. package/dist/spallm/index.d.ts +3 -0
  114. package/dist/spallm/index.d.ts.map +1 -0
  115. package/dist/src/index.js +1648 -0
  116. package/dist/src/index.js.map +32 -0
  117. package/dist/src/shared/index.js +1164 -0
  118. package/dist/src/shared/index.js.map +21 -0
  119. package/dist/src/spall/index.js +1648 -0
  120. package/dist/src/spall/index.js.map +32 -0
  121. package/dist/src/spallm/index.js +294 -0
  122. package/dist/src/spallm/index.js.map +17 -0
  123. package/package.json +72 -0
@@ -0,0 +1,1164 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ // src/shared/theme.ts
5
+ var colorEnabled = true;
6
+ function setColorEnabled(enabled) {
7
+ colorEnabled = enabled;
8
+ }
9
+ function wrap(fn) {
10
+ return (s) => colorEnabled ? fn(s) : s;
11
+ }
12
+ function rgb(r, g, b) {
13
+ return wrap((s) => `\x1B[38;2;${r};${g};${b}m${s}\x1B[39m`);
14
+ }
15
+ function dim(s) {
16
+ if (!colorEnabled)
17
+ return s;
18
+ return gray(128)(s);
19
+ }
20
+ var gray = (n) => rgb(n, n, n);
21
+ var primary = rgb(114, 161, 136);
22
+ var option = rgb(212, 212, 161);
23
+ var code = rgb(212, 212, 161);
24
+ var path = rgb(114, 161, 136);
25
+ var guide = rgb(162, 125, 111);
26
+ var search = (s) => {
27
+ const prefix = s?.prefix ?? "";
28
+ const suffix = s?.suffix ?? "";
29
+ const base = `${prefix}search${suffix}`;
30
+ return colorEnabled ? guide(base) : base;
31
+ };
32
+ var defaultTheme = {
33
+ primary,
34
+ code,
35
+ path,
36
+ guide,
37
+ header: dim,
38
+ command: primary,
39
+ arg: rgb(161, 212, 212),
40
+ option,
41
+ type: dim,
42
+ description: (s) => s,
43
+ dim,
44
+ search
45
+ };
46
+
47
+ // src/shared/layout.ts
48
+ var CLEAR = "\x1B[K";
49
+ var ANSI_RE = /\x1b\[[0-9;]*m/g;
50
+ function stripAnsi(s) {
51
+ return s.replace(ANSI_RE, "");
52
+ }
53
+ function truncateMiddle(s, max) {
54
+ if (max <= 0)
55
+ return "";
56
+ if (s.length <= max)
57
+ return s;
58
+ if (max <= 3)
59
+ return s.slice(0, max);
60
+ const half = max - 3 >> 1;
61
+ return s.slice(0, half + (max - 3 & 1)) + "..." + s.slice(-half);
62
+ }
63
+ function truncateStart(s, max) {
64
+ if (max <= 0)
65
+ return "";
66
+ if (s.length <= max)
67
+ return s;
68
+ if (max <= 3)
69
+ return "...".slice(0, max);
70
+ return "..." + s.slice(-(max - 3));
71
+ }
72
+ function truncateEnd(s, max) {
73
+ if (max <= 0)
74
+ return "";
75
+ if (s.length <= max)
76
+ return s;
77
+ if (max <= 3)
78
+ return "...".slice(0, max);
79
+ return s.slice(0, max - 3) + "...";
80
+ }
81
+ function cleanEscapes(s) {
82
+ return s.replace(/[\t\n]/g, " ");
83
+ }
84
+ function displayText(s) {
85
+ return cleanEscapes(stripAnsi(s));
86
+ }
87
+ function displayLen(s) {
88
+ return displayText(s).length;
89
+ }
90
+ function table(headers, columns, opts) {
91
+ const numCols = headers.length;
92
+ const gap = 2;
93
+ if (numCols === 0)
94
+ return;
95
+ const flex = opts?.flex ?? [];
96
+ const noTruncate = opts?.noTruncate ?? [];
97
+ const min = opts?.min ?? [];
98
+ const truncate = opts?.truncate ?? [];
99
+ const format = opts?.format ?? [];
100
+ const termWidth = opts?.maxWidth ?? process.stdout.columns;
101
+ const maxWidth = termWidth - 3;
102
+ const numRows = columns.reduce((m, c) => Math.max(m, c.length), 0);
103
+ const maxRows = opts?.maxRows ?? numRows;
104
+ const visibleRows = Math.min(numRows, maxRows);
105
+ const natural = [];
106
+ for (let i = 0;i < numCols; i++) {
107
+ const col = columns[i] ?? [];
108
+ const headerLen = displayText(headers[i] ?? "").length;
109
+ let maxCellLen = 0;
110
+ for (let r = 0;r < visibleRows; r++) {
111
+ const len = displayText(col[r] ?? "").length;
112
+ if (len > maxCellLen)
113
+ maxCellLen = len;
114
+ }
115
+ natural[i] = Math.max(headerLen, maxCellLen);
116
+ }
117
+ const widths = [...natural];
118
+ if (maxWidth != null && Number.isFinite(maxWidth) && maxWidth > 0 && (opts?.flex != null || opts?.noTruncate != null || opts?.min != null || opts?.truncate != null || opts?.maxWidth != null)) {
119
+ const totalGap = gap * (numCols - 1);
120
+ const available = Math.max(0, maxWidth - totalGap);
121
+ const totalNatural = natural.reduce((a, b) => a + b, 0);
122
+ if (totalNatural <= available) {} else {
123
+ let fixed = 0;
124
+ let totalWeight = 0;
125
+ const flexCols = [];
126
+ for (let i = 0;i < numCols; i++) {
127
+ const isFixed = noTruncate[i] === true || flex[i] === 0;
128
+ if (isFixed) {
129
+ widths[i] = natural[i];
130
+ fixed += widths[i];
131
+ } else {
132
+ const weight = flex[i] ?? 1;
133
+ totalWeight += weight;
134
+ flexCols.push({ i, weight });
135
+ }
136
+ }
137
+ const remaining = Math.max(0, available - fixed);
138
+ if (flexCols.length > 0) {
139
+ const mins = flexCols.map(({ i }) => Math.max(0, min[i] ?? 0));
140
+ const minSum = mins.reduce((a, b) => a + b, 0);
141
+ const baseMins = minSum > 0 && remaining < minSum ? mins.map((m) => Math.floor(m * remaining / minSum)) : mins;
142
+ const usedByMins = baseMins.reduce((a, b) => a + b, 0);
143
+ const extraSpace = Math.max(0, remaining - usedByMins);
144
+ if (totalWeight <= 0 || extraSpace === 0) {
145
+ for (let idx = 0;idx < flexCols.length; idx++) {
146
+ widths[flexCols[idx].i] = baseMins[idx];
147
+ }
148
+ } else {
149
+ const alloc = flexCols.map(({ i, weight }, idx) => {
150
+ const exact = extraSpace * weight / totalWeight;
151
+ const base = Math.floor(exact);
152
+ return {
153
+ i,
154
+ base: baseMins[idx] + base,
155
+ frac: exact - base,
156
+ weight,
157
+ natural: natural[i],
158
+ capped: false
159
+ };
160
+ });
161
+ let changed = true;
162
+ while (changed) {
163
+ changed = false;
164
+ let surplus = 0;
165
+ let uncappedWeight = 0;
166
+ for (const a of alloc) {
167
+ if (!a.capped && a.base > a.natural) {
168
+ surplus += a.base - a.natural;
169
+ a.base = a.natural;
170
+ a.capped = true;
171
+ changed = true;
172
+ }
173
+ if (!a.capped)
174
+ uncappedWeight += a.weight;
175
+ }
176
+ if (surplus > 0 && uncappedWeight > 0) {
177
+ for (const a of alloc) {
178
+ if (!a.capped) {
179
+ const share = Math.floor(surplus * a.weight / uncappedWeight);
180
+ a.base += share;
181
+ }
182
+ }
183
+ }
184
+ }
185
+ let used = alloc.reduce((s, a) => s + a.base, 0);
186
+ let extra = Math.max(0, available - fixed - used);
187
+ alloc.sort((a, b) => b.frac - a.frac || a.i - b.i);
188
+ for (let k = 0;k < alloc.length && extra > 0; k++) {
189
+ if (!alloc[k].capped || alloc[k].base < alloc[k].natural) {
190
+ alloc[k].base += 1;
191
+ extra--;
192
+ }
193
+ }
194
+ for (const a of alloc)
195
+ widths[a.i] = a.base;
196
+ }
197
+ }
198
+ }
199
+ }
200
+ const header = headers.map((h, i) => {
201
+ const w = widths[i] ?? 0;
202
+ const cell = truncateMiddle(displayText(h), w);
203
+ return w > 0 ? cell.padEnd(w) : cell;
204
+ }).join(" ");
205
+ console.log(defaultTheme.dim(header));
206
+ for (let row = 0;row < visibleRows; row++) {
207
+ const parts = [];
208
+ for (let i = 0;i < numCols; i++) {
209
+ const w = widths[i] ?? 0;
210
+ const col = columns[i] ?? [];
211
+ const raw = col[row] ?? "";
212
+ const cell = displayText(raw);
213
+ if (noTruncate[i]) {
214
+ const padded2 = w > 0 ? cell.padEnd(w) : cell;
215
+ parts.push(format[i] ? format[i](padded2, row, i) : padded2);
216
+ continue;
217
+ }
218
+ const mode = truncate[i] ?? "middle";
219
+ const truncated = mode === "start" ? truncateStart(cell, w) : mode === "end" ? truncateEnd(cell, w) : truncateMiddle(cell, w);
220
+ const padded = w > 0 ? truncated.padEnd(w) : truncated;
221
+ parts.push(format[i] ? format[i](padded, row, i) : padded);
222
+ }
223
+ const line = parts.join(" ");
224
+ console.log(line);
225
+ }
226
+ if (numRows > maxRows) {
227
+ console.log(defaultTheme.dim("(...truncated)"));
228
+ }
229
+ }
230
+ function cols(rows, colorFns) {
231
+ if (rows.length === 0)
232
+ return;
233
+ const widths = rows[0].map((_, i) => Math.max(...rows.map((r) => r[i].length)));
234
+ for (const row of rows) {
235
+ const line = row.map((c, i) => {
236
+ const padded = c.padEnd(widths[i]);
237
+ return colorFns?.[i] ? colorFns[i](padded) : padded;
238
+ });
239
+ console.log(line.join(" "));
240
+ }
241
+ }
242
+ // src/shared/display.ts
243
+ function formatPath(s) {
244
+ const bodyLen = s.trimEnd().length;
245
+ const body = s.slice(0, bodyLen);
246
+ const pad = s.slice(bodyLen);
247
+ const slash = body.lastIndexOf("/");
248
+ if (slash === -1)
249
+ return defaultTheme.primary(body) + pad;
250
+ return defaultTheme.dim(body.slice(0, slash + 1)) + defaultTheme.primary(body.slice(slash + 1)) + pad;
251
+ }
252
+ function highlightSnippet(s) {
253
+ const parts = s.split(/[\x01\x02]/);
254
+ let result = "";
255
+ let inside = false;
256
+ for (const part of parts) {
257
+ result += inside ? defaultTheme.code(part) : part;
258
+ inside = !inside;
259
+ }
260
+ return result;
261
+ }
262
+ function makePathTreeNode(name) {
263
+ return { name, children: new Map };
264
+ }
265
+ function buildPathTree(items, path2, initLeaf, addToLeaf) {
266
+ const root = makePathTreeNode("");
267
+ for (const item of items) {
268
+ const parts = path2(item).split("/");
269
+ let current = root;
270
+ for (const part of parts) {
271
+ let child = current.children.get(part);
272
+ if (!child) {
273
+ child = makePathTreeNode(part);
274
+ current.children.set(part, child);
275
+ }
276
+ current = child;
277
+ }
278
+ current.leaf ??= initLeaf();
279
+ addToLeaf(current.leaf, item);
280
+ }
281
+ return root;
282
+ }
283
+ function sortedChildren(node) {
284
+ const entries = Array.from(node.children.entries());
285
+ entries.sort((a, b) => {
286
+ const aDir = a[1].children.size > 0;
287
+ const bDir = b[1].children.size > 0;
288
+ if (aDir !== bDir)
289
+ return aDir ? -1 : 1;
290
+ return a[0].localeCompare(b[0]);
291
+ });
292
+ return entries;
293
+ }
294
+ function displayResults(items, opts) {
295
+ if (items.length === 0) {
296
+ console.log(defaultTheme.dim(opts.empty ?? "(no results)"));
297
+ return;
298
+ }
299
+ if (opts.output === "json") {
300
+ console.log(JSON.stringify(items, null, 2));
301
+ return;
302
+ }
303
+ if (opts.output === "tree") {
304
+ displayTree(items, opts);
305
+ return;
306
+ }
307
+ if (opts.output === "list") {
308
+ displayList(items, opts);
309
+ return;
310
+ }
311
+ const columns = [
312
+ {
313
+ header: "path",
314
+ value: opts.path,
315
+ flex: 1,
316
+ truncate: "start",
317
+ format: formatPath
318
+ },
319
+ {
320
+ header: "id",
321
+ value: opts.id,
322
+ flex: 0,
323
+ noTruncate: true,
324
+ format: (s) => defaultTheme.code(s)
325
+ },
326
+ ...opts.extraColumns ?? [],
327
+ {
328
+ header: "preview",
329
+ value: opts.preview,
330
+ flex: 2,
331
+ min: 3,
332
+ truncate: "end",
333
+ format: opts.previewFormat
334
+ }
335
+ ];
336
+ const termRows = process.stdout.rows ?? 24;
337
+ const maxRows = opts.showAll ? Infinity : Math.max(1, termRows - 4);
338
+ table(columns.map((c) => c.header), columns.map((c) => items.map((item) => c.value(item))), {
339
+ flex: columns.map((c) => c.flex ?? 1),
340
+ noTruncate: columns.map((c) => c.noTruncate ?? false),
341
+ min: columns.map((c) => c.min ?? 0),
342
+ truncate: columns.map((c) => c.truncate ?? "middle"),
343
+ format: columns.map((c) => c.format ? (s, row, col) => c.format(s, row, col) : undefined),
344
+ maxRows
345
+ });
346
+ }
347
+ function displayTree(items, opts) {
348
+ const termRows = process.stdout.rows ?? 24;
349
+ const maxRows = opts.showAll ? Infinity : Math.max(1, termRows - 3);
350
+ const truncated = items.length > maxRows;
351
+ const visible = truncated ? items.slice(0, maxRows) : items;
352
+ const root = buildPathTree(visible, (item) => opts.path(item), () => [], (leaf, item) => {
353
+ leaf.push(item);
354
+ });
355
+ const MAX_PER_LEAF = 3;
356
+ const termWidth = process.stdout.columns ?? 80;
357
+ const showAll = opts.showAll ?? false;
358
+ function printNode(node, indent) {
359
+ for (const [name, child] of sortedChildren(node)) {
360
+ if (child.children.size > 0) {
361
+ console.log(`${defaultTheme.dim(indent)}${defaultTheme.dim(name + "/")}`);
362
+ printNode(child, indent + " ");
363
+ } else {
364
+ const toShow = showAll ? child.leaf ?? [] : (child.leaf ?? []).slice(0, MAX_PER_LEAF);
365
+ for (let i = 0;i < toShow.length; i++) {
366
+ const item = toShow[i];
367
+ const index = toShow.length > 1 || (child.leaf?.length ?? 0) > 1 ? `[${i + 1}/${child.leaf?.length ?? 0}] ` : "";
368
+ const left = `${indent}${index}${name}`;
369
+ const leftStyled = defaultTheme.dim(indent + index) + defaultTheme.primary(name);
370
+ const content = cleanEscapes(opts.preview(item));
371
+ const contentBudget = termWidth - displayLen(left) - 1;
372
+ const preview = contentBudget > 0 ? truncateMiddle(content, contentBudget) : "";
373
+ console.log(preview ? `${leftStyled} ${preview}` : leftStyled);
374
+ }
375
+ if (!showAll && (child.leaf?.length ?? 0) > MAX_PER_LEAF) {
376
+ const remaining = (child.leaf?.length ?? 0) - MAX_PER_LEAF;
377
+ console.log(`${defaultTheme.dim(indent)} ${defaultTheme.dim(`( ... ${remaining} more note${remaining > 1 ? "s" : ""} )`)}`);
378
+ }
379
+ }
380
+ }
381
+ }
382
+ printNode(root, "");
383
+ if (truncated)
384
+ console.log(defaultTheme.dim("..."));
385
+ }
386
+ function displayList(items, opts) {
387
+ const termRows = process.stdout.rows ?? 24;
388
+ const maxItems = opts.showAll ? items.length : Math.max(1, termRows - 3);
389
+ for (let i = 0;i < Math.min(items.length, maxItems); i++) {
390
+ const item = items[i];
391
+ if (items.length > 1) {
392
+ console.log(defaultTheme.command(opts.path(item)));
393
+ }
394
+ console.log(opts.preview(item));
395
+ if (i < Math.min(items.length, maxItems) - 1)
396
+ console.log("");
397
+ }
398
+ if (items.length > maxItems && !opts.showAll) {
399
+ const remaining = items.length - maxItems;
400
+ console.log(defaultTheme.dim(`( ... ${remaining} more note${remaining > 1 ? "s" : ""} )`));
401
+ }
402
+ }
403
+ function printQueryId(queryId) {
404
+ console.log("");
405
+ console.log(`Your query ID is ${queryId}`);
406
+ }
407
+ function scoreBucket(score) {
408
+ if (score >= 0.9)
409
+ return "perfect";
410
+ if (score >= 0.85)
411
+ return "very high";
412
+ if (score >= 0.75)
413
+ return "high";
414
+ if (score >= 0.6)
415
+ return "medium";
416
+ return "low";
417
+ }
418
+ function truncatePreview(s, maxChars = 200) {
419
+ const collapsed = s.replace(/\s+/g, " ").trim();
420
+ if (collapsed.length <= maxChars)
421
+ return collapsed;
422
+ return collapsed.slice(0, maxChars - 3) + "...";
423
+ }
424
+ function displayLlmSearch(items, opts) {
425
+ if (items.length === 0) {
426
+ console.log(opts.empty ?? "(no results)");
427
+ printQueryId(opts.queryId);
428
+ return;
429
+ }
430
+ for (let i = 0;i < items.length; i++) {
431
+ const item = items[i];
432
+ const path2 = opts.path(item);
433
+ const id = opts.id(item);
434
+ const bucket = scoreBucket(opts.score(item));
435
+ const preview = truncatePreview(opts.preview(item));
436
+ console.log(`[${bucket}] (id: ${id}) ${path2}`);
437
+ console.log(preview);
438
+ if (i < items.length - 1)
439
+ console.log("");
440
+ }
441
+ printQueryId(opts.queryId);
442
+ }
443
+ function displayLlmFetch(items, opts) {
444
+ if (items.length === 0) {
445
+ console.log(opts.empty ?? "(no notes found)");
446
+ return;
447
+ }
448
+ for (let i = 0;i < items.length; i++) {
449
+ const item = items[i];
450
+ const path2 = opts.path(item);
451
+ const id = opts.id(item);
452
+ const content = opts.content(item);
453
+ console.log(`${path2} (id: ${id})`);
454
+ console.log(content);
455
+ if (i < items.length - 1)
456
+ console.log();
457
+ }
458
+ }
459
+ function displayPathTree(paths, opts) {
460
+ if (paths.length === 0) {
461
+ console.log(defaultTheme.dim(opts?.empty ?? "(no notes)"));
462
+ return;
463
+ }
464
+ const root = buildPathTree(paths, (p) => p, () => ({ count: 0 }), (leaf) => {
465
+ leaf.count++;
466
+ });
467
+ const showFiles = opts?.showAll ?? false;
468
+ function printNode(node, indent) {
469
+ const sorted = sortedChildren(node);
470
+ const dirs = [];
471
+ const files = [];
472
+ for (const entry of sorted) {
473
+ if (entry[1].children.size > 0)
474
+ dirs.push(entry);
475
+ else
476
+ files.push(entry);
477
+ }
478
+ for (const [name, child] of dirs) {
479
+ console.log(`${defaultTheme.dim(indent)}${defaultTheme.dim(name + "/")}`);
480
+ printNode(child, indent + " ");
481
+ }
482
+ if (files.length > 0) {
483
+ if (showFiles) {
484
+ for (const [name, child] of files) {
485
+ const n = child.leaf?.count ?? 0;
486
+ const suffix = n > 1 ? defaultTheme.dim(` (x${n})`) : "";
487
+ console.log(`${defaultTheme.dim(indent)}${defaultTheme.primary(name)}${suffix}`);
488
+ }
489
+ } else {
490
+ const count = files.reduce((sum, [, child]) => sum + (child.leaf?.count ?? 0), 0);
491
+ console.log(`${defaultTheme.dim(indent)}${defaultTheme.primary(`(${count} note${count !== 1 ? "s" : ""})`)}`);
492
+ }
493
+ }
494
+ }
495
+ printNode(root, "");
496
+ }
497
+ // src/shared/yargs.ts
498
+ import yargs from "yargs";
499
+ import { hideBin } from "yargs/helpers";
500
+ import pc from "picocolors";
501
+ function usage(def, path2, t) {
502
+ const parts = [];
503
+ const last = path2.length - 1;
504
+ for (let i = 0;i < path2.length; i++) {
505
+ const fmt = t.command;
506
+ parts.push(i === last ? fmt(path2[i]) : path2[i]);
507
+ }
508
+ if ("positionals" in def && def.positionals) {
509
+ for (const [k, v] of Object.entries(def.positionals)) {
510
+ const name = t.arg(`$${k}`);
511
+ parts.push(v.required ? name : `[${name}]`);
512
+ }
513
+ }
514
+ if (def.options && Object.keys(def.options).length > 0) {
515
+ parts.push(t.dim("[options]"));
516
+ }
517
+ if ("commands" in def && def.commands && Object.keys(def.commands).length > 0) {
518
+ parts.push(t.arg("$command"));
519
+ }
520
+ return parts.join(" ");
521
+ }
522
+ function help(def, name, path2 = [], t = defaultTheme) {
523
+ let prev = false;
524
+ if (def.description) {
525
+ console.log(t.description(def.description));
526
+ prev = true;
527
+ }
528
+ if (prev)
529
+ console.log("");
530
+ console.log(t.header("usage:"));
531
+ console.log(` ${usage(def, [name, ...path2], t)}`);
532
+ prev = true;
533
+ const pos = "positionals" in def ? def.positionals : undefined;
534
+ if (pos && Object.keys(pos).length > 0) {
535
+ if (prev)
536
+ console.log("");
537
+ console.log(t.header("arguments"));
538
+ prev = true;
539
+ const rows = [];
540
+ for (const [k, v] of Object.entries(pos)) {
541
+ let desc = v.description;
542
+ if (v.default !== undefined)
543
+ desc += ` ${t.dim(`(default: ${v.default})`)}`;
544
+ if (v.required)
545
+ desc += ` ${t.dim("(required)")}`;
546
+ rows.push([` ${k}`, v.type, desc]);
547
+ }
548
+ cols(rows, [t.arg, t.type, t.description]);
549
+ }
550
+ const opts = {
551
+ ...def.options ?? {},
552
+ help: { alias: "h", type: "boolean", description: "Show help" }
553
+ };
554
+ if (Object.keys(opts).length > 0) {
555
+ if (prev)
556
+ console.log("");
557
+ console.log(t.header("options"));
558
+ prev = true;
559
+ const rows = [];
560
+ for (const [k, v] of Object.entries(opts)) {
561
+ const short = v.alias ? `-${v.alias}, ` : " ";
562
+ let desc = v.description;
563
+ if (v.default !== undefined && v.type !== "boolean") {
564
+ desc += ` ${t.dim(`(default: ${v.default})`)}`;
565
+ }
566
+ rows.push([` ${short}--${k}`, v.type, desc]);
567
+ }
568
+ cols(rows, [t.option, t.type, t.description]);
569
+ }
570
+ const cmds = "commands" in def ? def.commands : undefined;
571
+ if (cmds && Object.keys(cmds).length > 0) {
572
+ if (prev)
573
+ console.log("");
574
+ console.log(t.header("commands"));
575
+ const rows = [];
576
+ const rowCmdFmt = [];
577
+ for (const [k, v] of Object.entries(cmds)) {
578
+ if (v.hidden)
579
+ continue;
580
+ const args = v.positionals ? Object.keys(v.positionals).join(" ") : "";
581
+ rows.push([` ${k}`, args, v.summary ?? v.description]);
582
+ rowCmdFmt.push(t.command);
583
+ }
584
+ let ri = 0;
585
+ cols(rows, [(s) => rowCmdFmt[ri++](s), t.arg, t.description]);
586
+ }
587
+ }
588
+ function fail(def, name, path2 = []) {
589
+ return (msg, err) => {
590
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
591
+ help(def, name, path2);
592
+ process.exit(0);
593
+ }
594
+ if (msg?.includes("You must specify") || msg?.includes("Not enough non-option arguments")) {
595
+ help(def, name, path2);
596
+ process.exit(1);
597
+ }
598
+ const detail = msg ?? (err instanceof globalThis.Error ? err.message : undefined) ?? (typeof err === "string" ? err : undefined) ?? "Unknown error";
599
+ console.error(pc.red(detail));
600
+ process.exit(1);
601
+ };
602
+ }
603
+ function check(def, name, path2 = []) {
604
+ return (argv) => {
605
+ if (argv.help && argv._.length === path2.length) {
606
+ help(def, name, path2);
607
+ process.exit(0);
608
+ }
609
+ return true;
610
+ };
611
+ }
612
+ function configure(y, def, root, path2) {
613
+ if ("positionals" in def && def.positionals) {
614
+ for (const [k, v] of Object.entries(def.positionals)) {
615
+ y.positional(k, {
616
+ type: v.type,
617
+ describe: v.description,
618
+ demandOption: v.required,
619
+ default: v.default
620
+ });
621
+ }
622
+ }
623
+ if (def.options) {
624
+ for (const [k, v] of Object.entries(def.options)) {
625
+ y.option(k, {
626
+ alias: v.alias,
627
+ type: v.type,
628
+ describe: v.description,
629
+ demandOption: v.required,
630
+ default: v.default
631
+ });
632
+ }
633
+ }
634
+ if ("commands" in def && def.commands) {
635
+ for (const [k, v] of Object.entries(def.commands)) {
636
+ command(y, k, v, root, path2);
637
+ }
638
+ y.demandCommand(1, "You must specify a command");
639
+ }
640
+ y.help(false).option("help", { alias: "h", type: "boolean", describe: "Show help" }).check(check(def, root, path2)).fail(fail(def, root, path2));
641
+ }
642
+ function command(y, name, def, root, path2) {
643
+ let cmd = name;
644
+ if (def.positionals) {
645
+ for (const [k, v] of Object.entries(def.positionals)) {
646
+ cmd += v.required ? ` <${k}>` : ` [${k}]`;
647
+ }
648
+ }
649
+ y.command(cmd, def.description, (yargs2) => configure(yargs2, def, root, [...path2, name]), def.handler);
650
+ }
651
+ function build(def) {
652
+ const y = yargs(hideBin(process.argv)).scriptName(def.name);
653
+ configure(y, def, def.name, []);
654
+ y.strict();
655
+ return y;
656
+ }
657
+ // src/shared/help.ts
658
+ var activeCli = null;
659
+ function setActiveCli(def) {
660
+ activeCli = def;
661
+ }
662
+ function printHelp(t = defaultTheme) {
663
+ if (!activeCli) {
664
+ throw new Error("printHelp() called before setActiveCli(). Set it in your CLI entrypoint.");
665
+ }
666
+ help(activeCli, activeCli.name, [], t);
667
+ }
668
+ // src/shared/progress.ts
669
+ import pc2 from "picocolors";
670
+ import consola from "consola";
671
+ var BAR_WIDTH = 20;
672
+ function renderProgressBar(percent) {
673
+ const filled = Math.round(percent / 100 * BAR_WIDTH);
674
+ const empty = BAR_WIDTH - filled;
675
+ return defaultTheme.primary("\u2588".repeat(filled) + "\u2591".repeat(empty));
676
+ }
677
+ function createModelProgressHandler() {
678
+ let model = "";
679
+ return (event) => {
680
+ switch (event.tag) {
681
+ case "model.load":
682
+ consola.info(`Loading model ${defaultTheme.primary(event.info.name)}`);
683
+ break;
684
+ case "model.download":
685
+ model = event.info.name;
686
+ consola.info(`Downloading model ${defaultTheme.primary(event.info.name)}`);
687
+ break;
688
+ case "model.progress": {
689
+ const percent = event.downloaded / event.total * 100;
690
+ const bar = renderProgressBar(percent);
691
+ const percentStr = percent.toFixed(0).padStart(3);
692
+ process.stderr.write(`\r${bar} ${pc2.bold(percentStr + "%")} ${defaultTheme.primary(model)} ${CLEAR}`);
693
+ break;
694
+ }
695
+ case "model.downloaded":
696
+ process.stderr.write(`\r${CLEAR}`);
697
+ consola.success(`Downloaded ${defaultTheme.primary(event.info.name)}`);
698
+ break;
699
+ }
700
+ };
701
+ }
702
+ function formatStreamError(e, path2) {
703
+ let code2 = "";
704
+ let msg = "";
705
+ if (e && typeof e === "object") {
706
+ code2 = e.code ?? "";
707
+ msg = e.message ?? String(e);
708
+ } else {
709
+ msg = String(e);
710
+ }
711
+ if (code2 === "note.exists") {
712
+ return `Note already exists at ${defaultTheme.primary(path2)}. Use ${defaultTheme.option("--update")} if you meant to update the note.`;
713
+ }
714
+ if (code2 === "note.duplicate") {
715
+ return `Duplicate content detected for ${defaultTheme.primary(path2)}. Use ${defaultTheme.option("--dupe")} to allow duplicates.`;
716
+ }
717
+ if (code2)
718
+ return `${code2}: ${msg}`;
719
+ return msg;
720
+ }
721
+ // src/shared/workspace.ts
722
+ import consola2 from "consola";
723
+ import { WorkspaceConfig } from "@spader/spall-core";
724
+ import { Client } from "@spader/spall-sdk/client";
725
+ function fail2(message) {
726
+ consola2.error(message);
727
+ process.exit(1);
728
+ }
729
+ async function gitRoot(start) {
730
+ try {
731
+ const proc = Bun.spawn(["git", "rev-parse", "--show-toplevel"], {
732
+ cwd: start,
733
+ stdout: "pipe",
734
+ stderr: "ignore",
735
+ stdin: "ignore"
736
+ });
737
+ const exit = await proc.exited;
738
+ if (exit !== 0)
739
+ return null;
740
+ const out = (await new Response(proc.stdout).text()).trim();
741
+ return out || null;
742
+ } catch {
743
+ return null;
744
+ }
745
+ }
746
+ async function resolveScope(input) {
747
+ const cwd = input.cwd ?? process.cwd();
748
+ const config = WorkspaceConfig.load(cwd);
749
+ const located = WorkspaceConfig.locate(cwd);
750
+ const names = input.corpus ? [input.corpus] : config.scope.read;
751
+ const tracked = input.tracked ?? false;
752
+ let viewer = { id: 1, name: "default" };
753
+ if (tracked && located) {
754
+ const ensured = await input.client.workspace.create({ name: config.workspace.name }).then(Client.unwrap);
755
+ viewer = { id: ensured.id, name: ensured.name };
756
+ if (config.workspace.id !== viewer.id) {
757
+ WorkspaceConfig.patch(located.root, {
758
+ workspace: { name: config.workspace.name, id: viewer.id }
759
+ });
760
+ }
761
+ }
762
+ const corpora = await input.client.corpus.list().then(Client.unwrap);
763
+ const byName = new Map(corpora.map((c) => [c.name, c.id]));
764
+ const ids = names.map((name) => {
765
+ const id = byName.get(name);
766
+ if (id === undefined) {
767
+ fail2(`Corpus not found: ${defaultTheme.command(name)}`);
768
+ }
769
+ return id;
770
+ });
771
+ return { config, located, viewer, names, ids };
772
+ }
773
+ async function createQuery(input) {
774
+ const scope = await resolveScope({
775
+ client: input.client,
776
+ cwd: input.cwd,
777
+ corpus: input.corpus,
778
+ tracked: input.tracked
779
+ });
780
+ const tracked = Boolean(input.tracked && scope.located);
781
+ const query = await input.client.query.create({
782
+ viewer: scope.viewer.id,
783
+ tracked,
784
+ corpora: scope.ids
785
+ }).then(Client.unwrap);
786
+ return { ...scope, query };
787
+ }
788
+ // src/shared/tree.ts
789
+ function noteTreeEntries(notes, maxDepth) {
790
+ const unlimited = maxDepth === undefined;
791
+ const entries = [];
792
+ const seenDirs = new Map;
793
+ for (const note of notes) {
794
+ const parts = note.path.split("/");
795
+ const fileDepth = parts.length - 1;
796
+ let prefix = "";
797
+ for (let i = 0;i < parts.length - 1; i++) {
798
+ prefix = prefix ? `${prefix}/${parts[i]}` : parts[i];
799
+ if (!unlimited && i > maxDepth)
800
+ break;
801
+ if (!seenDirs.has(prefix)) {
802
+ const entry = {
803
+ type: "dir",
804
+ depth: i,
805
+ name: parts[i] + "/"
806
+ };
807
+ seenDirs.set(prefix, entry);
808
+ entries.push(entry);
809
+ }
810
+ }
811
+ if (unlimited || fileDepth <= maxDepth) {
812
+ entries.push({
813
+ type: "file",
814
+ depth: fileDepth,
815
+ name: parts[parts.length - 1],
816
+ id: note.id
817
+ });
818
+ } else {
819
+ const cutoffKey = parts.slice(0, maxDepth + 1).join("/");
820
+ const dir = seenDirs.get(cutoffKey);
821
+ if (dir && dir.type === "dir") {
822
+ dir.noteCount = (dir.noteCount ?? 0) + 1;
823
+ }
824
+ }
825
+ }
826
+ return entries;
827
+ }
828
+ function noteDirEntries(notes, maxDepth) {
829
+ const unlimited = maxDepth === undefined;
830
+ const dirs = new Map;
831
+ const ensure = (key, depth, name) => {
832
+ const existing = dirs.get(key);
833
+ if (existing)
834
+ return existing;
835
+ const created = { hasSubdir: false, noteCount: 0, depth, name };
836
+ dirs.set(key, created);
837
+ return created;
838
+ };
839
+ for (const note of notes) {
840
+ const parts = note.path.split("/");
841
+ if (parts.length <= 1)
842
+ continue;
843
+ let prefix = "";
844
+ for (let i = 0;i < parts.length - 1; i++) {
845
+ if (!unlimited && i > maxDepth)
846
+ break;
847
+ const part = parts[i];
848
+ const key = prefix ? `${prefix}/${part}` : part;
849
+ ensure(key, i, part + "/");
850
+ if (prefix) {
851
+ const parent = dirs.get(prefix);
852
+ if (parent && (unlimited || i <= maxDepth)) {
853
+ parent.hasSubdir = true;
854
+ }
855
+ }
856
+ prefix = key;
857
+ }
858
+ if (!unlimited && parts.length - 2 > maxDepth) {
859
+ const cutoffKey = parts.slice(0, maxDepth + 1).join("/");
860
+ const cutoff = dirs.get(cutoffKey);
861
+ if (cutoff)
862
+ cutoff.noteCount++;
863
+ } else {
864
+ const parentKey = parts.slice(0, -1).join("/");
865
+ const parent = dirs.get(parentKey);
866
+ if (parent)
867
+ parent.noteCount++;
868
+ }
869
+ }
870
+ const keys = Array.from(dirs.keys());
871
+ keys.sort((a, b) => a.localeCompare(b));
872
+ return keys.filter((key) => {
873
+ const info = dirs.get(key);
874
+ return unlimited || info.depth <= maxDepth;
875
+ }).map((key) => {
876
+ const info = dirs.get(key);
877
+ const leaf = !info.hasSubdir;
878
+ return {
879
+ type: "dir",
880
+ depth: info.depth,
881
+ name: info.name,
882
+ noteCount: leaf ? info.noteCount : undefined
883
+ };
884
+ });
885
+ }
886
+ // src/shared/vsearch.ts
887
+ import { Client as Client2 } from "@spader/spall-sdk/client";
888
+ var Vsearch;
889
+ ((Vsearch) => {
890
+ Vsearch.summary = `Semantic vector ${defaultTheme.search()}`;
891
+ function description(cliName) {
892
+ return `Semantic vector ${defaultTheme.search()} using embeddings. Returns truncated previews with note IDs.
893
+
894
+ Use \`fetch\` with the returned Query ID and note IDs to get full content.
895
+
896
+ Example:
897
+ ${cliName} ${defaultTheme.search({ prefix: "v" })} "how to configure r2 bindings"
898
+ `;
899
+ }
900
+ Vsearch.description = description;
901
+ Vsearch.positionals = {
902
+ query: {
903
+ type: "string",
904
+ description: `Natural language ${defaultTheme.search()} query`,
905
+ required: true
906
+ }
907
+ };
908
+ Vsearch.options = {
909
+ corpus: {
910
+ alias: "c",
911
+ type: "string",
912
+ description: "Corpus name (default: from spall.json)"
913
+ },
914
+ path: {
915
+ type: "string",
916
+ description: "Path glob filter"
917
+ },
918
+ limit: {
919
+ alias: "n",
920
+ type: "number",
921
+ description: "Max results",
922
+ default: 10
923
+ }
924
+ };
925
+ function collapseWhitespace(s) {
926
+ return s.replace(/\s+/g, " ").trim();
927
+ }
928
+ Vsearch.collapseWhitespace = collapseWhitespace;
929
+ async function run(argv) {
930
+ const client = await Client2.connect();
931
+ const { query } = await createQuery({
932
+ client,
933
+ corpus: argv.corpus,
934
+ tracked: argv.tracked
935
+ });
936
+ const res = await client.query.vsearch({
937
+ id: String(query.id),
938
+ q: argv.query,
939
+ path: argv.path,
940
+ limit: argv.limit
941
+ }).then(Client2.unwrap);
942
+ return { client, query, results: res.results };
943
+ }
944
+ Vsearch.run = run;
945
+ })(Vsearch ||= {});
946
+ // src/shared/search.ts
947
+ import { Client as Client3 } from "@spader/spall-sdk/client";
948
+ var Search;
949
+ ((Search) => {
950
+ Search.summary = `Keyword ${defaultTheme.search()} (FTS)`;
951
+ function description(cliName) {
952
+ return `Full-text keyword ${defaultTheme.search()}. Returns truncated previews with note IDs.
953
+
954
+ Use \`fetch\` with the returned Query ID and note IDs to get full content.
955
+
956
+ Example:
957
+ ${cliName} ${defaultTheme.search()} "R2Bucket"
958
+ `;
959
+ }
960
+ Search.description = description;
961
+ Search.positionals = {
962
+ query: {
963
+ type: "string",
964
+ description: "Search keywords",
965
+ required: true
966
+ }
967
+ };
968
+ Search.options = {
969
+ corpus: {
970
+ alias: "c",
971
+ type: "string",
972
+ description: "Corpus name (default: from spall.json)"
973
+ },
974
+ path: {
975
+ type: "string",
976
+ description: "Path glob filter"
977
+ },
978
+ limit: {
979
+ alias: "n",
980
+ type: "number",
981
+ description: "Max results (default: 20)"
982
+ }
983
+ };
984
+ async function run(argv) {
985
+ const client = await Client3.connect();
986
+ const { query } = await createQuery({
987
+ client,
988
+ corpus: argv.corpus,
989
+ tracked: argv.tracked
990
+ });
991
+ const res = await client.query.search({
992
+ id: String(query.id),
993
+ q: argv.query,
994
+ path: argv.path,
995
+ limit: argv.limit,
996
+ ...argv.mode ? { mode: argv.mode } : {}
997
+ }).then(Client3.unwrap);
998
+ return { client, query, results: res.results };
999
+ }
1000
+ Search.run = run;
1001
+ })(Search ||= {});
1002
+ // src/shared/list.ts
1003
+ import { Client as Client4 } from "@spader/spall-sdk/client";
1004
+ var List;
1005
+ ((List) => {
1006
+ List.positionals = {
1007
+ path: {
1008
+ type: "string",
1009
+ description: "Path or glob to filter notes",
1010
+ default: "*"
1011
+ }
1012
+ };
1013
+ List.options = {
1014
+ corpus: {
1015
+ alias: "c",
1016
+ type: "string",
1017
+ description: "Corpus name"
1018
+ },
1019
+ all: {
1020
+ type: "boolean",
1021
+ description: "List all files (default: directories only)",
1022
+ default: false
1023
+ },
1024
+ depth: {
1025
+ alias: "d",
1026
+ type: "string",
1027
+ description: 'Tree depth (0 = top-level only, 1 = one level deep, "max" = unlimited)'
1028
+ }
1029
+ };
1030
+ function normalizePath(rawInput, completion) {
1031
+ let path2 = String(rawInput ?? "*");
1032
+ if (!/[*?\]]$/.test(path2)) {
1033
+ if (completion && !path2.endsWith("/")) {
1034
+ path2 = path2 + "*";
1035
+ } else {
1036
+ path2 = path2.replace(/\/?$/, "/*");
1037
+ }
1038
+ }
1039
+ return path2;
1040
+ }
1041
+ List.normalizePath = normalizePath;
1042
+ function parseDepth(raw) {
1043
+ if (raw === undefined || raw === null)
1044
+ return 0;
1045
+ const s = String(raw).trim().toLowerCase();
1046
+ if (s === "max" || s === "all")
1047
+ return;
1048
+ const n = parseInt(s, 10);
1049
+ if (isNaN(n) || n < 0)
1050
+ return 0;
1051
+ return n;
1052
+ }
1053
+ List.parseDepth = parseDepth;
1054
+ async function run(argv) {
1055
+ const client = await Client4.connect();
1056
+ const {
1057
+ query,
1058
+ located,
1059
+ names: includeNames
1060
+ } = await createQuery({
1061
+ client,
1062
+ corpus: argv.corpus,
1063
+ tracked: argv.tracked
1064
+ });
1065
+ const path2 = normalizePath(argv.path, Boolean(argv.completion));
1066
+ const notes = [];
1067
+ let cursor = undefined;
1068
+ while (true) {
1069
+ const page = await client.query.notes({ id: String(query.id), path: path2, limit: 100, after: cursor }).then(Client4.unwrap);
1070
+ for (const n of page.notes)
1071
+ notes.push({ id: n.id, path: n.path });
1072
+ if (!page.nextCursor)
1073
+ break;
1074
+ cursor = page.nextCursor;
1075
+ }
1076
+ notes.sort((a, b) => a.path.localeCompare(b.path));
1077
+ return { client, query, notes, path: path2, located, includeNames };
1078
+ }
1079
+ List.run = run;
1080
+ })(List ||= {});
1081
+ // src/shared/status.ts
1082
+ import { Client as Client5 } from "@spader/spall-sdk/client";
1083
+ import { WorkspaceConfig as WorkspaceConfig2 } from "@spader/spall-core";
1084
+ var Status;
1085
+ ((Status) => {
1086
+ Status.summary = "List available corpora and workspace status";
1087
+ Status.description = `List available corpora, and which will be included by default in searches (i.e. included in workspace)`;
1088
+ Status.formatTime = (ts) => new Date(ts).toISOString().slice(0, 19).replace("T", " ");
1089
+ async function run() {
1090
+ const client = await Client5.connect();
1091
+ const url = Client5.url(client);
1092
+ const result = await client.corpus.list();
1093
+ if (result.error || !result.data) {
1094
+ return { error: result.error };
1095
+ }
1096
+ const corpora = result.data;
1097
+ const config = WorkspaceConfig2.load(process.cwd());
1098
+ const included = new Set(config.scope.read);
1099
+ return { url, corpora, included };
1100
+ }
1101
+ Status.run = run;
1102
+ function print(result, opts) {
1103
+ const { url, corpora, included } = result;
1104
+ console.log(`${defaultTheme.dim("server")} ${url}`);
1105
+ if (corpora.length === 0) {
1106
+ console.log("No corpora found.");
1107
+ return;
1108
+ }
1109
+ table(["name", "id", "notes", "workspace", "created", "updated"], [
1110
+ corpora.map((p) => p.name),
1111
+ corpora.map((p) => String(p.id)),
1112
+ corpora.map((p) => String(p.noteCount)),
1113
+ corpora.map((p) => included.has(p.name) ? "yes" : "no"),
1114
+ corpora.map((p) => Status.formatTime(p.createdAt)),
1115
+ corpora.map((p) => Status.formatTime(p.updatedAt))
1116
+ ], opts?.highlightWorkspace ? {
1117
+ format: [
1118
+ (s) => s,
1119
+ (s) => s,
1120
+ (s) => s,
1121
+ (s) => {
1122
+ const trimmed = s.trimEnd();
1123
+ const pad = s.slice(trimmed.length);
1124
+ return trimmed === "yes" ? defaultTheme.primary("yes") + pad : "no" + pad;
1125
+ },
1126
+ (s) => s,
1127
+ (s) => s
1128
+ ]
1129
+ } : undefined);
1130
+ }
1131
+ Status.print = print;
1132
+ })(Status ||= {});
1133
+ export {
1134
+ table,
1135
+ setColorEnabled,
1136
+ setActiveCli,
1137
+ resolveScope,
1138
+ renderProgressBar,
1139
+ printQueryId,
1140
+ printHelp,
1141
+ noteTreeEntries,
1142
+ noteDirEntries,
1143
+ highlightSnippet,
1144
+ help,
1145
+ gitRoot,
1146
+ formatStreamError,
1147
+ displayResults,
1148
+ displayPathTree,
1149
+ displayLlmSearch,
1150
+ displayLlmFetch,
1151
+ defaultTheme,
1152
+ createQuery,
1153
+ createModelProgressHandler,
1154
+ cols,
1155
+ cleanEscapes,
1156
+ build,
1157
+ Vsearch,
1158
+ Status,
1159
+ Search,
1160
+ List,
1161
+ CLEAR
1162
+ };
1163
+
1164
+ //# debugId=D1FB096FF010260C64756E2164756E21