arcroom 0.0.5-beta → 0.0.6-beta

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.
package/dist/mcp.js CHANGED
@@ -2,9 +2,9 @@
2
2
  // Hand-rolled instead of the SDK so it runs from the copied runtime with no node_modules.
3
3
  import { git, projectOf } from './git.js';
4
4
  import { createHash } from 'node:crypto';
5
- import { existsSync, readFileSync } from 'node:fs';
5
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
6
6
  import { hostname } from 'node:os';
7
- import { relative, resolve } from 'node:path';
7
+ import { join, relative, resolve } from 'node:path';
8
8
  import { createInterface } from 'node:readline';
9
9
  import { api, isOff, keyRefused, loadConfig } from './home.js';
10
10
  import { hooksHere, setUpHere } from './scope.js';
@@ -229,11 +229,27 @@ const PATHS = {
229
229
  code_note: '/v1/code/note',
230
230
  };
231
231
  const sha256 = (file) => createHash('sha256').update(lf(readFileSync(file))).digest('hex');
232
- /** {"file:<path>": sha256} for files the answer depends on; a missing file is recorded as "missing". */
232
+ // ponytail: a file added to the folder later is not noticed, only changed or removed ones; hash the listing if that matters
233
+ /** The files in a folder an answer depends on (agents name "feedback/" as often as a file), at most 200, no .git or node_modules. */
234
+ function filesIn(dir, out = []) {
235
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
236
+ if (out.length >= 200)
237
+ break;
238
+ if (e.name === '.git' || e.name === 'node_modules')
239
+ continue;
240
+ if (e.isDirectory())
241
+ filesIn(join(dir, e.name), out);
242
+ else if (e.isFile())
243
+ out.push(join(dir, e.name));
244
+ }
245
+ return out;
246
+ }
247
+ /** {"file:<path>": sha256} for files the answer depends on (a folder: each file in it); a missing file is recorded as "missing". */
233
248
  export function fileDeps(paths, cwd) {
234
- return Object.fromEntries(paths.map((p) => {
235
- const rel = relative(cwd, resolve(cwd, p)).replaceAll('\\', '/');
236
- return [`file:${rel}`, existsSync(resolve(cwd, rel)) ? sha256(resolve(cwd, rel)) : 'missing'];
249
+ return Object.fromEntries(paths.flatMap((p) => {
250
+ const full = resolve(cwd, p);
251
+ const files = existsSync(full) && statSync(full).isDirectory() ? filesIn(full) : [full];
252
+ return files.map((f) => [`file:${relative(cwd, f).replaceAll('\\', '/')}`, existsSync(f) ? sha256(f) : 'missing']);
237
253
  }));
238
254
  }
239
255
  /** Paths among depends_on whose current content differs from when the answer was stored. */
@@ -374,7 +390,8 @@ export async function mcp() {
374
390
  process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: msg.id, result }) + '\n');
375
391
  }
376
392
  catch (err) {
377
- const error = { code: err?.code ?? -32603, message: err?.message ?? String(err) };
393
+ // JSON-RPC codes are numbers: a Node error's "EISDIR" makes Claude Code drop the reply and wait for ever
394
+ const error = { code: Number.isInteger(err?.code) ? err.code : -32603, message: err?.message ?? String(err) };
378
395
  process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: msg.id, error }) + '\n');
379
396
  }
380
397
  }
package/dist/session.js CHANGED
@@ -29,13 +29,17 @@ export function lastRunContext(run, now = new Date(), root) {
29
29
  : `ended ${run.status === 'partial' ? 'PARTIAL: its done-checks were still failing' : run.status}`;
30
30
  const lines = [
31
31
  `ArcRoom: ${whose} in this project (${ago(run.started_at, now)}, on ${run.machine ?? 'another machine'}${run.branch ? `, branch ${run.branch}` : ''}) ${ended}.`,
32
- `Asked: "${first.slice(0, 200)}"`,
33
- last && last !== first ? `Last asked: "${last.slice(0, 200)}"` : '',
32
+ // the whole ask as captured (at most 1,000 characters): a cut ask loses its last steps, the ones left to do
33
+ `Asked: "${first.slice(0, 1000)}"`,
34
+ last && last !== first ? `Last asked: "${last.slice(0, 1000)}"` : '',
34
35
  run.failing_check ? `Still failing: \`${String(run.failing_check).slice(0, 200)}\`` : '',
35
36
  edited.length ? `Edited: ${edited.slice(0, 10).join(', ')}${edited.length > 10 ? ` (+${edited.length - 10} more)` : ''}` : '',
36
- `${r.tool_calls ?? 0} tool calls` +
37
- (r.blocked ? `, ${r.blocked} blocked by the loop guard` : '') +
38
- (memory.find ? `, memory: ${memory.find} lookups, ${memory.complete ?? 0} stored` : ''),
37
+ // a run that never closed has no report: no count rather than a wrong "0 tool calls"
38
+ r.tool_calls == null
39
+ ? ''
40
+ : `${r.tool_calls} tool calls` +
41
+ (r.blocked ? `, ${r.blocked} blocked by the loop guard` : '') +
42
+ (memory.find ? `, memory: ${memory.find} lookups, ${memory.complete ?? 0} stored` : ''),
39
43
  'Use this to pick up where it left off if the user continues that work; ignore it otherwise.',
40
44
  ];
41
45
  return lines.filter(Boolean).join('\n');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcroom",
3
- "version": "0.0.5-beta",
3
+ "version": "0.0.6-beta",
4
4
  "description": "Shared team memory and run control for coding agents: Claude Code, Codex, Cursor, OpenCode",
5
5
  "license": "Apache-2.0",
6
6
  "author": "ArcRoom AI",