(q: GbrainManifestQuery, args: CliArgs)
| 270 | } |
| 271 | |
| 272 | function dispatchFilesystem(q: GbrainManifestQuery, args: CliArgs): QueryResult { |
| 273 | const t0 = Date.now(); |
| 274 | if (!q.glob) { |
| 275 | return { query: q, ok: false, rendered: "", bytes: 0, duration_ms: Date.now() - t0, reason: "filesystem kind missing glob" }; |
| 276 | } |
| 277 | const { resolved: glob, unresolved } = substituteTemplateVars(q.glob, args); |
| 278 | if (unresolved.length > 0) { |
| 279 | return { |
| 280 | query: q, |
| 281 | ok: false, |
| 282 | rendered: "", |
| 283 | bytes: 0, |
| 284 | duration_ms: Date.now() - t0, |
| 285 | reason: `template vars unresolved: ${unresolved.join(",")}`, |
| 286 | }; |
| 287 | } |
| 288 | // Expand ~ to home dir |
| 289 | const expanded = glob.replace(/^~/, HOME); |
| 290 | |
| 291 | // Simple glob: match against filesystem |
| 292 | const matches = simpleGlob(expanded); |
| 293 | if (matches.length === 0) { |
| 294 | return { query: q, ok: false, rendered: "", bytes: 0, duration_ms: Date.now() - t0, reason: "no matches" }; |
| 295 | } |
| 296 | |
| 297 | // Sort + limit |
| 298 | let sorted = matches; |
| 299 | if (q.sort === "mtime_desc") { |
| 300 | sorted = matches |
| 301 | .map((p) => ({ p, mtime: tryStatMtime(p) })) |
| 302 | .sort((a, b) => b.mtime - a.mtime) |
| 303 | .map((x) => x.p); |
| 304 | } |
| 305 | const limit = q.limit ?? args.limit; |
| 306 | const limited = q.tail !== undefined ? sorted.slice(-q.tail) : sorted.slice(0, limit); |
| 307 | |
| 308 | const lines = limited.map((p) => { |
| 309 | const mt = new Date(tryStatMtime(p)).toISOString().slice(0, 10); |
| 310 | return `- ${mt} — ${basename(p)}`; |
| 311 | }); |
| 312 | const rendered = wrapDatamarked(q.render_as, capBody(lines.join("\n"))); |
| 313 | return { query: q, ok: true, rendered, bytes: rendered.length, duration_ms: Date.now() - t0 }; |
| 314 | } |
| 315 | |
| 316 | // ── Helpers ──────────────────────────────────────────────────────────────── |
| 317 |
no test coverage detected