From 61eb8584727bb4c145a6ed7947d6d5d52f0dae72 Mon Sep 17 00:00:00 2001 From: poteat Date: Fri, 25 Sep 2026 15:37:42 -0700 Subject: [PATCH] mods: the declarations carry process.run's truncation flags and list entries' mtimeMs; the test fakes answer them --- mods/README.md | 8 ++- .../backend-of/recording-backend-host-of.ts | 6 ++- mods/diff/tests/fixtures/git-in.ts | 8 ++- mods/diff/tests/fixtures/not-a-repository.ts | 2 + mods/diff/tests/fixtures/old-files.ts | 4 +- .../tests/fixtures/scripted/git-timed-out.ts | 2 + mods/diff/tests/fixtures/scripted/ok.ts | 2 + .../tests/fixtures/scripted/probe-missed.ts | 2 + .../tests/fixtures/scripted/unscripted.ts | 2 + mods/telemetry/tests/fixtures/listing.ts | 2 +- mods/telemetry/tests/fixtures/probed.ts | 2 + mods/types/claude-code.d.ts | 51 ++++++++++++++++--- 12 files changed, 77 insertions(+), 14 deletions(-) diff --git a/mods/README.md b/mods/README.md index 308b44bfcb..3eb2fd9a74 100644 --- a/mods/README.md +++ b/mods/README.md @@ -48,7 +48,13 @@ describe('register', () => { on('session.start', ($, e) => ({ cwd: e.cwd })) on('command.register', ($, e) => ({ value: { command: e.name } })) on('process.run', () => ({ - value: { exitCode: 128, stdout: '', stderr: 'fatal: not a git repository' }, + value: { + exitCode: 128, + stdout: '', + stderr: 'fatal: not a git repository', + isStdoutTruncated: false, + isStderrTruncated: false, + }, })) on('ui.open', ($, e, next) => { opened.push(e.id) diff --git a/mods/diff/tests/fixtures/backend-of/recording-backend-host-of.ts b/mods/diff/tests/fixtures/backend-of/recording-backend-host-of.ts index 8f063b56bd..6600a16cfe 100644 --- a/mods/diff/tests/fixtures/backend-of/recording-backend-host-of.ts +++ b/mods/diff/tests/fixtures/backend-of/recording-backend-host-of.ts @@ -20,7 +20,11 @@ export function recordingBackendHostOf( const host = backendHostOf(([program, ...rest]) => { programs.push(program) - return scripted.run(rest) + return scripted.run(rest).then(ran => ({ + ...ran, + isStdoutTruncated: false, + isStderrTruncated: false, + })) }) return { host, programs } diff --git a/mods/diff/tests/fixtures/git-in.ts b/mods/diff/tests/fixtures/git-in.ts index 1a63b69fae..c964c8ad1d 100644 --- a/mods/diff/tests/fixtures/git-in.ts +++ b/mods/diff/tests/fixtures/git-in.ts @@ -22,6 +22,12 @@ export function gitIn( const found = Object.entries(script).find(([key]) => line.includes(key)) return found - ? { exitCode: 0, stdout: found[1], stderr: '' } + ? { + exitCode: 0, + stdout: found[1], + stderr: '', + isStdoutTruncated: false, + isStderrTruncated: false, + } : NOT_A_REPOSITORY } diff --git a/mods/diff/tests/fixtures/not-a-repository.ts b/mods/diff/tests/fixtures/not-a-repository.ts index 67ae31a837..b066d2f1cd 100644 --- a/mods/diff/tests/fixtures/not-a-repository.ts +++ b/mods/diff/tests/fixtures/not-a-repository.ts @@ -7,4 +7,6 @@ export const NOT_A_REPOSITORY: ProcessRunResult = { exitCode: 128, stdout: '', stderr: 'fatal: not a git repository', + isStdoutTruncated: false, + isStderrTruncated: false, } diff --git a/mods/diff/tests/fixtures/old-files.ts b/mods/diff/tests/fixtures/old-files.ts index a82ef4396b..9e43edb12b 100644 --- a/mods/diff/tests/fixtures/old-files.ts +++ b/mods/diff/tests/fixtures/old-files.ts @@ -9,8 +9,8 @@ import type { On } from 'claude-code' export function oldFiles(on: On) { on('fs.list', () => ({ value: [ - { name: 'old.ts', kind: 'file', size: 2, isLink: false }, - { name: 'moved.ts', kind: 'file', size: 2, isLink: false }, + { name: 'old.ts', kind: 'file', size: 2, mtimeMs: 0, isLink: false }, + { name: 'moved.ts', kind: 'file', size: 2, mtimeMs: 0, isLink: false }, ], })) diff --git a/mods/diff/tests/fixtures/scripted/git-timed-out.ts b/mods/diff/tests/fixtures/scripted/git-timed-out.ts index 2791a7f0ff..3a793cb8c5 100644 --- a/mods/diff/tests/fixtures/scripted/git-timed-out.ts +++ b/mods/diff/tests/fixtures/scripted/git-timed-out.ts @@ -8,4 +8,6 @@ export const GIT_TIMED_OUT: ProcessRunResult = { exitCode: -1, stdout: '', stderr: '', + isStdoutTruncated: false, + isStderrTruncated: false, } diff --git a/mods/diff/tests/fixtures/scripted/ok.ts b/mods/diff/tests/fixtures/scripted/ok.ts index ebf101e62c..430c6c8f24 100644 --- a/mods/diff/tests/fixtures/scripted/ok.ts +++ b/mods/diff/tests/fixtures/scripted/ok.ts @@ -11,4 +11,6 @@ export const ok = (stdout = ''): ProcessRunResult => ({ exitCode: 0, stdout, stderr: '', + isStdoutTruncated: false, + isStderrTruncated: false, }) diff --git a/mods/diff/tests/fixtures/scripted/probe-missed.ts b/mods/diff/tests/fixtures/scripted/probe-missed.ts index ae79b08da0..d3984142d2 100644 --- a/mods/diff/tests/fixtures/scripted/probe-missed.ts +++ b/mods/diff/tests/fixtures/scripted/probe-missed.ts @@ -8,4 +8,6 @@ export const PROBE_MISSED: ProcessRunResult = { exitCode: 1, stdout: '', stderr: '', + isStdoutTruncated: false, + isStderrTruncated: false, } diff --git a/mods/diff/tests/fixtures/scripted/unscripted.ts b/mods/diff/tests/fixtures/scripted/unscripted.ts index ec6ace8751..972ed3e56e 100644 --- a/mods/diff/tests/fixtures/scripted/unscripted.ts +++ b/mods/diff/tests/fixtures/scripted/unscripted.ts @@ -8,4 +8,6 @@ export const UNSCRIPTED: ProcessRunResult = { exitCode: 128, stdout: '', stderr: '', + isStdoutTruncated: false, + isStderrTruncated: false, } diff --git a/mods/telemetry/tests/fixtures/listing.ts b/mods/telemetry/tests/fixtures/listing.ts index ce527daa82..e0c83e828c 100644 --- a/mods/telemetry/tests/fixtures/listing.ts +++ b/mods/telemetry/tests/fixtures/listing.ts @@ -4,5 +4,5 @@ import type { FsEntry } from 'claude-code' * What the session's working directory lists: a git checkout. */ export const LISTING: FsEntry[] = [ - { name: '.git', kind: 'dir', size: 0, isLink: false }, + { name: '.git', kind: 'dir', size: 0, mtimeMs: 0, isLink: false }, ] diff --git a/mods/telemetry/tests/fixtures/probed.ts b/mods/telemetry/tests/fixtures/probed.ts index 262c06492c..ce4a9ac48a 100644 --- a/mods/telemetry/tests/fixtures/probed.ts +++ b/mods/telemetry/tests/fixtures/probed.ts @@ -8,4 +8,6 @@ export const PROBED: ProcessRunResult = { exitCode: 0, stdout: 'Darwin\n25.6.0\narm64\nnpm,pnpm,\nbun,node,\n', stderr: '', + isStdoutTruncated: false, + isStderrTruncated: false, } diff --git a/mods/types/claude-code.d.ts b/mods/types/claude-code.d.ts index 3af343874f..11cf650137 100644 --- a/mods/types/claude-code.d.ts +++ b/mods/types/claude-code.d.ts @@ -2751,11 +2751,17 @@ declare module 'claude-code' { */ write: (path: string, text: string) => Promise; /** - * Lists a directory: `{ name, kind, size, isLink }` per entry, by name, - * each entry as it stands (a symbolic link is `other` with `isLink`). + * Lists a directory by name: `{ name, kind, size, mtimeMs, isLink }` per + * entry, each entry as it stands. + * + * A symbolic link is `other` with `isLink`; `size` and `mtimeMs` are a + * regular file's own and 0 for every other kind (`$.fs.stat` has those). * * @param path the directory's path; absent, the working directory - * @returns the entries, `{ name, kind, size, isLink }` each + * @returns the entries, `{ name, kind, size, mtimeMs, isLink }` each + * @example + * const logs = await $.fs.list("logs") + * const newest = [...logs].sort((a, b) => b.mtimeMs - a.mtimeMs)[0] */ list: (path?: string) => Promise; /** @@ -2958,7 +2964,8 @@ declare module 'claude-code' { * @param argv the command and its arguments, `argv[0]` the executable * @param init `{ cwd, env, stdin, timeoutMs }` (cwd the session's by * default; timeout 30 s by default, ten minutes at most) - * @returns `{ exitCode, stdout, stderr }` once the child exits + * @returns `{ exitCode, stdout, stderr }`, each stream's first 4194304 + * bytes (`isStdoutTruncated`, `isStderrTruncated` say when cut) * @example * const { exitCode, stdout } = await $.process.run(["git", "status"]) */ @@ -4039,6 +4046,17 @@ declare module 'claude-code' { * Bytes, for a file. */ size: number; + /** + * Last modification, milliseconds since the epoch, for a file, as + * `$.fs.stat` spells it; 0 for a directory, a link or any other kind. + * + * The listing reads a regular file's size and time in its one call and + * looks no other entry up: `$.fs.stat` answers a directory's. + * + * @example + * const newest = [...entries].sort((a, b) => b.mtimeMs - a.mtimeMs)[0] + */ + mtimeMs: number; /** * True when the entry is a symbolic link. */ @@ -6493,6 +6511,9 @@ declare module 'claude-code' { /** * What `$.process.run` resolves with once the child has exited. + * + * @example + * const { stdout, isStdoutTruncated } = await $.process.run(["git", "log"]) */ export type ProcessRunResult = { /** @@ -6500,15 +6521,29 @@ declare module 'claude-code' { */ exitCode: number; /** - * What the child wrote to standard output, as text, cut at the output - * limit. + * What the child wrote to standard output, as text: its first 4194304 + * bytes (4 MiB), the rest read and dropped (`isStdoutTruncated`). + * + * The limit counts bytes, not characters, and is standard output's own: + * standard error has the same limit again. A cut inside a multi-byte + * character drops that character. */ stdout: string; /** - * What the child wrote to standard error, as text, cut at the output - * limit. + * What the child wrote to standard error, as text: its first 4194304 + * bytes (4 MiB), the rest read and dropped (`isStderrTruncated`). */ stderr: string; + /** + * True when the child wrote more than 4194304 bytes to standard output + * and `stdout` is only the first of them; false when `stdout` is whole. + */ + isStdoutTruncated: boolean; + /** + * True when the child wrote more than 4194304 bytes to standard error + * and `stderr` is only the first of them; false when `stderr` is whole. + */ + isStderrTruncated: boolean; }; /**