Skip to content

Diagnostics file and hook protocol

These formats are how agents and scripts talk to OpenFiles. They are versioned; breaking changes bump version.

Written by the extension, in each workspace folder, whenever problems in AI-edited files change.

{
"version": 1,
"updatedAt": 1789322228723, // epoch ms
"hook": {
"feedback": "errorsAndWarnings", // openfiles.hooks.feedback
"timeoutMs": 4000 // openfiles.hooks.feedbackTimeoutMs
},
"files": {
"src/auth.ts": { // workspace-relative, forward slashes
"path": "/home/me/app/src/auth.ts",
"checkedAt": 1789322228700, // when diagnostics settled after the last edit; 0 = not yet
"errors": 1,
"warnings": 0,
"items": [
{
"line": 42, // 1-based
"column": 7, // 1-based
"severity": "error", // error | warning | info | hint
"source": "ts",
"code": "2322",
"message": "Type 'string' is not assignable to type 'number'."
}
]
}
}
}

Reading it from an agent:

  • Only files changed in the current session are listed.
  • If checkedAt is older than your edit, the editor hasn’t finished checking yet. Wait and read again.
  • In queue mode files aren’t opened, so many language servers won’t have looked at them. An empty items there doesn’t mean clean.

Appended by hook.js, one JSON object per line. The extension reads new lines and truncates the file once it has read everything.

{"v":1,"ts":1789322228384,"agent":"claude","tool":"Edit","paths":["/home/me/app/src/auth.ts"]}

Anything can write here: a script, a Makefile, another tool. Use absolute paths.

node ~/.openfiles/hook.js --agent <id> [--event <name>] [--no-feedback]
Argument
--agent claude, codex, copilot, vscode, gemini, qwen, kimi, cursor, windsurf, kiro, cline, droid, goose, opencode, unknown
--event the hook event, when an agent has several (afterFileEdit, postToolUse)
--no-feedback only report the edit; don’t wait for or print diagnostics

Behavior:

  1. Reads the hook payload from stdin (3 s max).
  2. Finds edited paths. Tools whose name doesn’t look like a write (Read, Grep, Bash) are ignored.
  3. Walks up from the edited file to the nearest folder with a fresh .openfiles/vscode/*.json (less than 60 s old). If there isn’t one, it exits.
  4. Appends to queue.jsonl.
  5. Unless --no-feedback is set, it waits until every path has checkedAt ≥ the time of step 4, or until hook.timeoutMs passes.
  6. Prints feedback in the agent’s format, or nothing:
Agent Output
claude, codex, qwen, kimi, droid {"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"…"}}
gemini {"hookSpecificOutput":{"hookEventName":"AfterTool","additionalContext":"…"}}
copilot, vscode {"additionalContext":"…","hookSpecificOutput":{…}}
cursor (postToolUse) {"additional_context":"…"}
everyone else nothing

It always exits 0.

One file per VS Code window, refreshed every openfiles.heartbeatSeconds. It’s useful if an agent wants to know what you’re looking at.

{
"version": 1,
"workspaceRoot": "/home/me/app",
"sessionId": "",
"appName": "Visual Studio Code",
"updatedAt": 1789322228.72,
"files": ["/home/me/app/src/auth.ts"]
}

updatedAt is in seconds, kept that way for compatibility with 0.1 clients.