teapot-coding-agent 0.16.1 → 0.16.2

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.
@@ -300,6 +300,54 @@ export function buildApp(master) {
300
300
  return c.json({ error: "cannot read" }, 400);
301
301
  }
302
302
  });
303
+ /**
304
+ * Batched ignore lookup: runs `git check-ignore --stdin` once per directory
305
+ * and returns the subset of names that are gitignored. Never throws — a
306
+ * non-git workspace simply yields an empty set.
307
+ */
308
+ async function checkIgnoredBatch(workspace, dirAbs, dirents) {
309
+ const out = new Set();
310
+ try {
311
+ const relPaths = dirents.filter((d) => !d.name.startsWith(".")).map((d) => path.relative(workspace, path.join(dirAbs, d.name)));
312
+ if (relPaths.length === 0)
313
+ return out;
314
+ const { execFile } = await import("node:child_process");
315
+ const res = await new Promise((resolve) => {
316
+ let timer;
317
+ const p = execFile("git",
318
+ // NOTE: no --quiet — it suppresses the stdout we parse
319
+ ["-C", workspace, "check-ignore", "--stdin", "-z", "--verbose"], { encoding: "utf8", maxBuffer: 1 << 20 }, (err, stdout) => {
320
+ if (timer)
321
+ clearTimeout(timer);
322
+ resolve(err || !stdout ? "" : stdout);
323
+ });
324
+ // end stdin only AFTER the write flushes (ending immediately raced
325
+ // the write and git saw an empty list), and hard-cap in case git
326
+ // wedges — a tree listing must never hang
327
+ p.stdin?.write(relPaths.join("\0") + "\0", () => p.stdin?.end());
328
+ timer = setTimeout(() => {
329
+ try {
330
+ p.kill("SIGKILL");
331
+ }
332
+ catch { /* */ }
333
+ resolve("");
334
+ }, 5_000);
335
+ });
336
+ // -z --verbose emits records as
337
+ // "<source> NUL <line> NUL <pattern> NUL <path> NUL"
338
+ // so the ignored path is every 4th field starting at index 3
339
+ const fields = res.split("\0");
340
+ for (let i = 3; i < fields.length; i += 4) {
341
+ const p = fields[i];
342
+ if (p)
343
+ out.add(path.basename(p));
344
+ }
345
+ }
346
+ catch {
347
+ /* no git, not a repo, or spawn failed → treat all as unignored */
348
+ }
349
+ return out;
350
+ }
303
351
  // ---- workspace file tree (read-only, powers the 🗂 files panel) ----
304
352
  app.get("/api/agents/:id/tree", async (c) => {
305
353
  const a = master.agents.get(c.req.param("id"));
@@ -320,10 +368,14 @@ export function buildApp(master) {
320
368
  catch {
321
369
  return c.json({ error: "cannot read directory" }, 400);
322
370
  }
371
+ const showHidden = c.req.query("hidden") === "1";
323
372
  const entries = [];
373
+ // one batched `git check-ignore` for the whole directory (respects
374
+ // nested .gitignore files); absent repo / no git → nothing is "ignored"
375
+ const ignoredNames = await checkIgnoredBatch(a.workspace, abs, dirents);
324
376
  for (const d of dirents) {
325
- if (d.name.startsWith("."))
326
- continue; // hidden files stay out of the tree
377
+ if (d.name.startsWith(".") && !(showHidden && d.name !== "." && d.name !== ".."))
378
+ continue; // hidden files stay out unless the caller opted in
327
379
  const isDir = d.isDirectory();
328
380
  let size;
329
381
  if (!isDir) {
@@ -334,7 +386,12 @@ export function buildApp(master) {
334
386
  /* vanished mid-scan — fine */
335
387
  }
336
388
  }
337
- entries.push({ name: d.name, dir: isDir, ...(size !== undefined ? { size } : {}) });
389
+ entries.push({
390
+ name: d.name,
391
+ dir: isDir,
392
+ ...(size !== undefined ? { size } : {}),
393
+ ...(ignoredNames.has(d.name) ? { ignored: true } : {}),
394
+ });
338
395
  }
339
396
  entries.sort((x, y) => (x.dir !== y.dir ? (x.dir ? -1 : 1) : x.name.localeCompare(y.name)));
340
397
  return c.json({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teapot-coding-agent",
3
- "version": "0.16.1",
3
+ "version": "0.16.2",
4
4
  "description": "A lightweight, always-on multi-agent harness for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-or-later",