hanoman 0.2.0 → 0.2.1

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.2.0",
3
- "sha": "2389cd8a",
4
- "builtAt": "2026-08-25T15:36:51.946Z"
2
+ "version": "0.2.1",
3
+ "sha": "0aa68209",
4
+ "builtAt": "2026-08-26T04:13:10.476Z"
5
5
  }
package/dist/cli.js CHANGED
@@ -6060,7 +6060,15 @@ var init_api = __esm({
6060
6060
  breakdown: (id, prd) => `${API}/projects/${id}/breakdown?prd=${encodeURIComponent(prd)}`,
6061
6061
  specsBatch: `${API}/specs/batch`,
6062
6062
  // SPEC-182 · IDE Visual
6063
- ideTree: (id, ref = "") => `${API}/projects/${id}/tree${ref ? `?ref=${encodeURIComponent(ref)}` : ""}`,
6063
+ // `hidden` menyertakan entri .gitignore; `under` membuka satu tingkat isi sebuah direktori.
6064
+ ideTree: (id, ref = "", opts) => {
6065
+ const q = new URLSearchParams();
6066
+ if (ref) q.set("ref", ref);
6067
+ if (opts?.hidden) q.set("hidden", "1");
6068
+ if (opts?.under) q.set("under", opts.under);
6069
+ const s2 = q.toString();
6070
+ return `${API}/projects/${id}/tree${s2 ? `?${s2}` : ""}`;
6071
+ },
6064
6072
  ideFile: (id, path, ref = "") => `${API}/projects/${id}/file${path ? `?path=${encodeURIComponent(path)}${ref ? `&ref=${encodeURIComponent(ref)}` : ""}` : ""}`,
6065
6073
  ideGraph: (id, limit = 200, opts) => {
6066
6074
  const p3 = new URLSearchParams({ limit: String(limit) });
@@ -8205,16 +8213,25 @@ var init_ide = __esm({
8205
8213
  {
8206
8214
  name: "hanoman_ide_tree",
8207
8215
  title: "Pohon berkas repo project",
8208
- description: "Daftar berkas repo project pada sebuah ref. Tanpa `ref` ia membaca working tree apa adanya, termasuk perubahan yang belum di-commit.",
8216
+ description: "Daftar berkas repo project pada sebuah ref. Tanpa `ref` ia membaca working tree apa adanya, termasuk perubahan yang belum di-commit. `hidden` ikut menyertakan yang .gitignore sembunyikan \u2014 direktori yang seluruhnya diabaikan (mis. `node_modules`) diruntuhkan jadi satu entri di `dirs`; buka isinya dengan `under`, satu tingkat per panggilan.",
8209
8217
  inputSchema: obj({
8210
- properties: { project: PROJECT2, ref: str("Ref git (branch/tag/sha). Kosongkan untuk working tree.") },
8218
+ properties: {
8219
+ project: PROJECT2,
8220
+ ref: str("Ref git (branch/tag/sha). Kosongkan untuk working tree."),
8221
+ hidden: bool("Sertakan berkas & direktori yang diabaikan .gitignore. Tak berlaku bersama `ref`."),
8222
+ under: str("Jalur direktori; balasannya berisi SATU tingkat isinya. Dipakai untuk membuka direktori terabaikan yang diruntuhkan.")
8223
+ },
8211
8224
  required: ["project"]
8212
8225
  }),
8213
8226
  mode: "read",
8214
8227
  capability: "ide:read",
8215
8228
  samplePath: "/projects/hanoman/tree",
8216
8229
  sampleMethod: "GET",
8217
- build: (a) => ({ method: "GET", path: `${p(a.project)}/tree`, query: query({ ref: s(a.ref) }) }),
8230
+ build: (a) => ({
8231
+ method: "GET",
8232
+ path: `${p(a.project)}/tree`,
8233
+ query: query({ ref: s(a.ref), hidden: a.hidden ? "1" : "", under: s(a.under) })
8234
+ }),
8218
8235
  shape: (raw) => raw
8219
8236
  },
8220
8237
  {
package/dist/server.js CHANGED
@@ -6520,7 +6520,15 @@ var init_api = __esm({
6520
6520
  breakdown: (id, prd) => `${API}/projects/${id}/breakdown?prd=${encodeURIComponent(prd)}`,
6521
6521
  specsBatch: `${API}/specs/batch`,
6522
6522
  // SPEC-182 · IDE Visual
6523
- ideTree: (id, ref = "") => `${API}/projects/${id}/tree${ref ? `?ref=${encodeURIComponent(ref)}` : ""}`,
6523
+ // `hidden` menyertakan entri .gitignore; `under` membuka satu tingkat isi sebuah direktori.
6524
+ ideTree: (id, ref = "", opts) => {
6525
+ const q = new URLSearchParams();
6526
+ if (ref) q.set("ref", ref);
6527
+ if (opts?.hidden) q.set("hidden", "1");
6528
+ if (opts?.under) q.set("under", opts.under);
6529
+ const s2 = q.toString();
6530
+ return `${API}/projects/${id}/tree${s2 ? `?${s2}` : ""}`;
6531
+ },
6524
6532
  ideFile: (id, path, ref = "") => `${API}/projects/${id}/file${path ? `?path=${encodeURIComponent(path)}${ref ? `&ref=${encodeURIComponent(ref)}` : ""}` : ""}`,
6525
6533
  ideGraph: (id, limit = 200, opts) => {
6526
6534
  const p3 = new URLSearchParams({ limit: String(limit) });
@@ -8815,16 +8823,25 @@ var init_ide = __esm({
8815
8823
  {
8816
8824
  name: "hanoman_ide_tree",
8817
8825
  title: "Pohon berkas repo project",
8818
- description: "Daftar berkas repo project pada sebuah ref. Tanpa `ref` ia membaca working tree apa adanya, termasuk perubahan yang belum di-commit.",
8826
+ description: "Daftar berkas repo project pada sebuah ref. Tanpa `ref` ia membaca working tree apa adanya, termasuk perubahan yang belum di-commit. `hidden` ikut menyertakan yang .gitignore sembunyikan \u2014 direktori yang seluruhnya diabaikan (mis. `node_modules`) diruntuhkan jadi satu entri di `dirs`; buka isinya dengan `under`, satu tingkat per panggilan.",
8819
8827
  inputSchema: obj({
8820
- properties: { project: PROJECT2, ref: str("Ref git (branch/tag/sha). Kosongkan untuk working tree.") },
8828
+ properties: {
8829
+ project: PROJECT2,
8830
+ ref: str("Ref git (branch/tag/sha). Kosongkan untuk working tree."),
8831
+ hidden: bool("Sertakan berkas & direktori yang diabaikan .gitignore. Tak berlaku bersama `ref`."),
8832
+ under: str("Jalur direktori; balasannya berisi SATU tingkat isinya. Dipakai untuk membuka direktori terabaikan yang diruntuhkan.")
8833
+ },
8821
8834
  required: ["project"]
8822
8835
  }),
8823
8836
  mode: "read",
8824
8837
  capability: "ide:read",
8825
8838
  samplePath: "/projects/hanoman/tree",
8826
8839
  sampleMethod: "GET",
8827
- build: (a) => ({ method: "GET", path: `${p(a.project)}/tree`, query: query({ ref: s(a.ref) }) }),
8840
+ build: (a) => ({
8841
+ method: "GET",
8842
+ path: `${p(a.project)}/tree`,
8843
+ query: query({ ref: s(a.ref), hidden: a.hidden ? "1" : "", under: s(a.under) })
8844
+ }),
8828
8845
  shape: (raw) => raw
8829
8846
  },
8830
8847
  {
@@ -26508,6 +26525,7 @@ function ownsWorktree(repoDir, cwd) {
26508
26525
  import { execFile as execFile7 } from "node:child_process";
26509
26526
  import { promisify as promisify6 } from "node:util";
26510
26527
  import { existsSync as existsSync7 } from "node:fs";
26528
+ import { readdir as readdir2 } from "node:fs/promises";
26511
26529
  import { resolve as resolve14 } from "node:path";
26512
26530
  var exec6 = promisify6(execFile7);
26513
26531
  var GIT4 = { maxBuffer: 1 << 24 };
@@ -26527,6 +26545,43 @@ async function listRepoTree(repoDir, ref = "") {
26527
26545
  return [];
26528
26546
  }
26529
26547
  }
26548
+ async function listIgnoredEntries(repoDir) {
26549
+ const empty = { files: [], dirs: [] };
26550
+ if (!repoDir || !existsSync7(repoDir)) return empty;
26551
+ try {
26552
+ const { stdout } = await exec6(
26553
+ "git",
26554
+ ["ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "--no-empty-directory", "-z"],
26555
+ { cwd: repoDir, ...GIT4 }
26556
+ );
26557
+ const files = [], dirs = [];
26558
+ for (const e of new Set(splitZ2(stdout))) {
26559
+ if (e.endsWith("/")) dirs.push(e.slice(0, -1));
26560
+ else files.push(e);
26561
+ }
26562
+ return { files: files.sort(), dirs: dirs.sort() };
26563
+ } catch {
26564
+ return empty;
26565
+ }
26566
+ }
26567
+ var DIR_ENTRY_MAX = 5e3;
26568
+ async function listDirEntries(repoDir, rel) {
26569
+ const empty = { files: [], dirs: [], truncated: false };
26570
+ if (!repoDir) return empty;
26571
+ const abs = repoAbsPath(repoDir, rel);
26572
+ let raw;
26573
+ try {
26574
+ raw = await readdir2(abs, { withFileTypes: true });
26575
+ } catch {
26576
+ return empty;
26577
+ }
26578
+ const files = [], dirs = [];
26579
+ for (const d of raw.slice(0, DIR_ENTRY_MAX)) {
26580
+ if (d.name === ".git") continue;
26581
+ (d.isDirectory() ? dirs : files).push(`${rel}/${d.name}`);
26582
+ }
26583
+ return { files: files.sort(), dirs: dirs.sort(), truncated: raw.length > DIR_ENTRY_MAX };
26584
+ }
26530
26585
  async function readRepoFile2(repoDir, rel, ref = "") {
26531
26586
  if (!repoDir) return null;
26532
26587
  repoAbsPath(repoDir, rel, !!ref);
@@ -31222,7 +31277,7 @@ async function dropSpecAttachments(specId) {
31222
31277
 
31223
31278
  // src/services/spec-attachment-dir.ts
31224
31279
  init_db();
31225
- import { mkdir as mkdir4, readdir as readdir2, readFile as readFile3, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
31280
+ import { mkdir as mkdir4, readdir as readdir3, readFile as readFile3, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
31226
31281
  import { join as join13 } from "node:path";
31227
31282
  init_session_id();
31228
31283
  init_uploads();
@@ -31287,7 +31342,7 @@ async function syncSpecAttachmentsDir(specId, projectId) {
31287
31342
  }
31288
31343
  await writeFile3(join13(dir2, INDEX), renderIndex(specId, items), { mode: 384 });
31289
31344
  const keep = /* @__PURE__ */ new Set([INDEX, ...items.map((a) => a.filename)]);
31290
- for (const name2 of await readdir2(dir2)) {
31345
+ for (const name2 of await readdir3(dir2)) {
31291
31346
  if (!keep.has(name2)) await rm4(join13(dir2, name2), { recursive: true, force: true }).catch(() => {
31292
31347
  });
31293
31348
  }
@@ -35153,8 +35208,25 @@ async function ide_default(app2) {
35153
35208
  app2.get("/projects/:id/tree", async (req, reply) => {
35154
35209
  const repoDir = await repoOf(req.params.id);
35155
35210
  if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
35156
- const ref = req.query.ref ?? "";
35157
- return { ref, files: await listRepoTree(repoDir, ref) };
35211
+ const q = req.query;
35212
+ const ref = q.ref ?? "";
35213
+ const hidden = q.hidden === "1" || q.hidden === "true";
35214
+ if (q.under) {
35215
+ try {
35216
+ return { ref: "", ...await listDirEntries(repoDir, q.under), ignored: [] };
35217
+ } catch (e) {
35218
+ return entryError(reply, e);
35219
+ }
35220
+ }
35221
+ const files = await listRepoTree(repoDir, ref);
35222
+ if (!hidden || ref) return { ref, files, dirs: [], ignored: [] };
35223
+ const ign = await listIgnoredEntries(repoDir);
35224
+ return {
35225
+ ref,
35226
+ files: [...files, ...ign.files].sort(),
35227
+ dirs: ign.dirs,
35228
+ ignored: [...ign.files, ...ign.dirs]
35229
+ };
35158
35230
  });
35159
35231
  app2.get("/projects/:id/file", async (req, reply) => {
35160
35232
  const id = req.params.id;
@@ -35597,7 +35669,7 @@ async function finishGraphOp(reply, id, repoDir, r, verb) {
35597
35669
  }
35598
35670
 
35599
35671
  // src/routes/fs.ts
35600
- import { readdir as readdir3 } from "node:fs/promises";
35672
+ import { readdir as readdir4 } from "node:fs/promises";
35601
35673
  import { homedir as homedir4 } from "node:os";
35602
35674
  import { resolve as resolve17, dirname as dirname9, join as join16 } from "node:path";
35603
35675
  async function fs_default(app2) {
@@ -35605,7 +35677,7 @@ async function fs_default(app2) {
35605
35677
  const q = req.query.path;
35606
35678
  const dir2 = q && q.trim() ? resolve17(q.trim()) : homedir4();
35607
35679
  try {
35608
- const ents = await readdir3(dir2, { withFileTypes: true });
35680
+ const ents = await readdir4(dir2, { withFileTypes: true });
35609
35681
  const entries3 = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join16(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
35610
35682
  const parent = dirname9(dir2);
35611
35683
  return { path: dir2, parent: parent === dir2 ? null : parent, entries: entries3 };
@@ -39011,7 +39083,7 @@ async function getLimits() {
39011
39083
  }
39012
39084
 
39013
39085
  // src/services/codex-limits.ts
39014
- import { open as open3, readdir as readdir4, stat as stat2 } from "node:fs/promises";
39086
+ import { open as open3, readdir as readdir5, stat as stat2 } from "node:fs/promises";
39015
39087
  import { homedir as homedir8 } from "node:os";
39016
39088
  import { join as join24 } from "node:path";
39017
39089
  var TTL_MS3 = 3e4;
@@ -39048,7 +39120,7 @@ function toWindow(slot, raw, reached) {
39048
39120
  async function recentRollouts(dir2) {
39049
39121
  let entries3;
39050
39122
  try {
39051
- entries3 = (await readdir4(dir2, { recursive: true })).filter((f) => f.endsWith(".jsonl"));
39123
+ entries3 = (await readdir5(dir2, { recursive: true })).filter((f) => f.endsWith(".jsonl"));
39052
39124
  } catch {
39053
39125
  return [];
39054
39126
  }
@@ -40764,7 +40836,7 @@ import { randomUUID as randomUUID9 } from "node:crypto";
40764
40836
  init_config2();
40765
40837
  init_src2();
40766
40838
  import { randomUUID as randomUUID8 } from "node:crypto";
40767
- import { mkdir as mkdir5, writeFile as writeFile4, readFile as readFile4, unlink as unlink4, readdir as readdir5, stat as stat3 } from "node:fs/promises";
40839
+ import { mkdir as mkdir5, writeFile as writeFile4, readFile as readFile4, unlink as unlink4, readdir as readdir6, stat as stat3 } from "node:fs/promises";
40768
40840
  import { join as join25, resolve as resolve19, basename as basename6 } from "node:path";
40769
40841
  var MAX_TRANSCRIPT_BYTES = 1024 * 1024;
40770
40842
  function transcriptDir() {
@@ -40809,7 +40881,7 @@ async function listTranscripts() {
40809
40881
  const dir2 = transcriptDir();
40810
40882
  let names;
40811
40883
  try {
40812
- names = await readdir5(dir2);
40884
+ names = await readdir6(dir2);
40813
40885
  } catch (e) {
40814
40886
  if (e.code === "ENOENT") return [];
40815
40887
  throw e;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hanoman",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Orchestrator + dashboard workflow docs-driven untuk sesi Claude Code / Codex",
5
5
  "type": "module",
6
6
  "repository": {