Skip to content

fix(lua-agent-builder): deploy hook stops treating a /lua/ folder as a lua command (F3), 1.19.1 - #34

Merged
lua-stefan-kruger merged 2 commits into
mainfrom
qa/deploy-hook-path-match
Nov 5, 2026
Merged

lua-stefan-kruger merged 2 commits into
mainfrom
qa/deploy-hook-path-match

Conversation

@lua-stefan-kruger

Copy link
Copy Markdown
Contributor

Summary

Fixes F3 from the live build-session QA (2026-11-04): the confirm-deploy PreToolUse hook blocked harmless commands as lua <unresolved command> whenever any folder in the command was named lua (for example a working directory under ~/Documents/lua/). Examples blocked during QA: a plain script call (T=./tools/x.sh; $T $SID '{}'), a Python heredoc run from a /lua/ folder, and read-only status loops.

Release: lua-agent-builder 1.19.1.

What changes

  • lua, heylua, lua-ai and lua-cli count as a lua mention only as a whole word, a path's last segment, or an entry point, not as any folder in a path.
  • A computed head with a literal verb and no lua token anywhere is now unresolvedHead. Interactively (permission mode default, acceptEdits or plan) the hook answers ask, so a person decides. In every other case it still blocks with DEPLOY_DENIED_BARE (fail closed): bypassPermissions, dontAsk, auto, a missing or unknown mode, headless runs and build sessions.
  • Heredoc and herestring bodies fed to python/node/ruby/perl/awk are still parsed as shell and searched as text, as before. Backticks and ${…} in them can run a computed binary.
  • --help is not relaxed. Classifying L=lua; $L deploy all, $(printf lua), "$DIR/lua" and npx as unresolved is unchanged: still blocked.

Safety

  • No command that was blocked under an unattended mode becomes allowed. The only relaxation is block → ask, for a person, interactively.
  • classify's public shape is unchanged (classifyProductionCommandDetailed carries the new flag).
  • Known follow-ups, not regressions: SECURITY.md line 35 still describes the old wording. Stale PR fix(hooks): 1.7.1 — production gate classifies what a command runs, not text it mentions #18 also touches lib/tokenizer.mjs and should be rebased or closed after this lands.

Tests

  • npm test: 72 suites, 2632/2632 pass. npm run lint: all lints pass, with release version 1.19.1 consistent.
  • New tests:
    • tokenizer-hardening: /lua/ cwd cases now allowed; the QA heredoc; node ${x} ${y}; L=lua, $(printf lua), which lua, npx, -- --help and piped prefixed promote still blocked; perl/node/ruby heredocs running a computed binary still gated.
    • confirm-deploy unit and integration: ask vs block for each permission mode, headless and session.

Both commits are SSH-signed (verified).

🤖 Generated with Claude Code

lua-stefan-kruger and others added 2 commits November 4, 2026 20:06
…n a path as a lua command (F3)

Live build session QA: `cd ~/Documents/lua/... && T=./tools/x.sh; $T $SID '{}'`
and a Python heredoc run from a /lua/ folder were blocked as
`lua <unresolved command>`. "Mentions lua" was a substring regex over the whole
command, so a /lua/ path segment let any computed head + computed word match.

- mentionsLuaToken(): lua/heylua/lua-ai/lua-cli counts only as a whole word,
  a path's last segment or an entry point; the regex stays a fast negative.
  This alone fixes the QA heredoc: Python's quotes and braces no longer read
  as `$BIN $VERB` once nothing in the command is a lua token.
- Heredoc/herestring bodies fed to python/node/ruby/perl/awk are still parsed
  as shell AND searched as text, unchanged from origin/main: their backticks
  and `${...}` templates run a computed binary (perl/ruby backticks, node
  template literals), so text-only search would have weakened the gate.
- A computed head with a literal verb and no lua token anywhere is
  `unresolvedHead`: confirm-deploy answers permissionDecision "ask"
  interactively, and only when the PreToolUse permission_mode is default,
  acceptEdits or plan. bypassPermissions, dontAsk, auto (a classifier settles
  prompts and only some Claude Code versions stop it approving a hook ask; the
  plugin cannot enforce a minimum version), a missing field (older Claude
  Code) or an unknown value keep the DEPLOY_DENIED_BARE block (fail closed);
  headless and build sessions still block. classify's public shape is
  unchanged (classifyProductionCommandDetailed carries the flag).
- --help is not relaxed.

Tests: tokenizer-hardening (allow /lua/ cwd cases, the QA heredoc and a node
`${x} ${y}` heredoc; block L=lua, $(printf lua), which lua, "$DIR/lua", npx,
-- --help, piped prefixed promote; perl/node/ruby heredocs running a computed
binary stay gated), confirm-deploy (ask under default/acceptEdits/plan vs block
under bypassPermissions/dontAsk/auto/missing/unknown mode and headless/session,
ask envelope), confirm-deploy.integration (spawned hook: ask envelope + exit 0
under default, exit 2 DEPLOY_DENIED_BARE under bypassPermissions and auto).
npm test 2632/2632, npm run lint, test:coverage thresholds green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bump every version the release workflow and lint-release-version read to
1.19.1: plugin package.json, .claude-plugin/plugin.json, marketplace.json,
mcp/lua-platform package.json and package-lock.json, src/api-client.mjs
(X-Lua-Client) and src/server.mjs (server version, plugin_version x2).
dist/server.js is rebuilt with `npm ci && npm run build` in mcp/lua-platform
(Node 22), so the bundle and its inline sourcemap both carry 1.19.1 and CI's
`git diff --exit-code -- dist/` after a rebuild stays clean.
CHANGELOG heading "Unreleased (next: 1.19.1)" becomes "1.19.1 — 2026-11-04".
release-prod.yml cuts v<package.json version> on merge and fails if the tag
exists, so 1.19.0 would fail the release.

Test: npm run lint prints "Release version 1.19.1 is consistent"; a second
`npm run build` leaves `git diff --exit-code -- dist/` clean; mcp/lua-platform
npm test 149/149 and npm run typecheck green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lua-stefan-kruger
lua-stefan-kruger merged commit 6f65244 into main Nov 5, 2026
7 checks passed
@lua-stefan-kruger

Copy link
Copy Markdown
Contributor Author

Correction (post-merge): the description says unresolved commands keep the block in headless mode and build sessions. That holds for every gated verb, but one narrow class of fully shell-computed commands is now allowed where it was previously blocked only incidentally. The server-side session proxy remains the enforcement boundary; details are recorded with the session-image plugin bump in the platform repo.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant