tillerhand 3.49.2 → 3.49.3

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.
@@ -261,11 +261,32 @@ export function scopeIssue(command, dir, root) {
261
261
  // .env in a shell command (git show :.env, curl -d @.env, cat apps/web/.env.local); .env.example, .env.sample (any middle part) are free
262
262
  const ENV_ACCESS = /(?<![\w.-])\.env(?![\w.-]*\.(example|sample)(?![\w.-]))(\.[\w.-]+|\*)?(?=$|[\s'";|&)<>])|(^|[\s'"/\\])secrets[/\\]/i;
263
263
  // A glob that can match .env (.e*, .en?, .e[n]v): the shell expands it to the file
264
- const ENV_NAMES = ['.env', '.env.local', '.env.production', '.env.development'];
265
- const envGlob = (s) => [...s.matchAll(/(?<![\w.-])\.[^\s'";|&)<>/\\]*[*?[][^\s'";|&)<>/\\]*/g)].some(([g]) => {
266
- const re = new RegExp(`^${g.replace(/[.+^${}()|\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.')}$`, 'i');
267
- try { return ENV_NAMES.some((n) => re.test(n)); } catch { return true; } // an unclosed [ : asked
268
- });
264
+ const ENV_NAMES = ['.env', '.env.local', '.env.production', '.env.development', '.env.staging', '.env.test', '.env.preview'];
265
+ // Glob match (* ? [..] [!..]) by dynamic programming, not a regex: no throw on an unclosed [ (bash reads it as a
266
+ // plain [) and no backtracking blow-up on ********. Case-insensitive, like the Windows file system.
267
+ export function globMatch(glob, name) {
268
+ const tokens = [];
269
+ for (let i = 0; i < glob.length; i++) {
270
+ const c = glob[i], end = c === '[' ? glob.indexOf(']', i + 2) : -1;
271
+ if (c === '*') { if (tokens.at(-1) !== '*') tokens.push('*'); }
272
+ else if (c === '?') tokens.push((x) => true);
273
+ else if (end > -1) {
274
+ const body = glob.slice(i + 1, end).toLowerCase(), neg = /^[!^]/.test(body), set = neg ? body.slice(1) : body;
275
+ tokens.push((x) => set.includes(x) !== neg);
276
+ i = end;
277
+ } else tokens.push((x) => x === c.toLowerCase());
278
+ }
279
+ const s = name.toLowerCase();
280
+ let row = [true, ...Array(s.length).fill(false)];
281
+ for (const k of tokens) {
282
+ const next = [k === '*' && row[0], ...Array(s.length).fill(false)];
283
+ for (let j = 1; j <= s.length; j++) next[j] = k === '*' ? row[j] || next[j - 1] : row[j - 1] && k(s[j - 1]);
284
+ row = next;
285
+ }
286
+ return row[s.length];
287
+ }
288
+ const envGlob = (s) => [...s.matchAll(/(?<![\w.-])\.[^\s'";|&)<>/\\]*[*?[][^\s'";|&)<>/\\]*/g)]
289
+ .some(([g]) => ENV_NAMES.some((n) => globMatch(g, n)));
269
290
  // vercel env pull writes the dev values into .env.local without showing them (a prod pull is denied above). Only the
270
291
  // pull's own part of the chain is free; a comment (#) after it is not.
271
292
  const envIssue = (k) => k.split(/;|&&|\|\||[|&\n]/).some((s) =>
@@ -406,8 +427,13 @@ function evaluateOne(command) {
406
427
 
407
428
  if (isMain(import.meta.url)) {
408
429
  const g = readInput();
409
- // Of the lists, the stricter wins (deny > ask)
410
- const results = g ? [evaluate(g.command, g), g.tool !== 'claude' ? permissionEquivalent(g.command) : null, scopeIssue(g.command, g.dir, g.root)].filter(Boolean) : [];
430
+ // Of the lists, the stricter wins (deny > ask). A guard that throws would let the command run: asked instead
431
+ let results;
432
+ try {
433
+ results = g ? [evaluate(g.command, g), g.tool !== 'claude' ? permissionEquivalent(g.command) : null, scopeIssue(g.command, g.dir, g.root)].filter(Boolean) : [];
434
+ } catch (e) {
435
+ results = [{ decision: 'ask', reason: tr('guard.destructive.internalError', { error: String(e?.message ?? e).slice(0, 200) }) }];
436
+ }
411
437
  const s = results.find((x) => x.decision === 'deny') ?? results[0];
412
438
  if (s) decide(g.tool, s.decision, s.decision === 'ask' ? tr('guard.destructive.prefix', { reason: s.reason }) : `Shield: ${s.reason}`);
413
439
  process.exit(0);
@@ -394,6 +394,7 @@ export const MESSAGES = {
394
394
  'guard.destructive.remoteDb': { en: 'The command connects to a database other than localhost (could be prod). User approval required; the AI must not use prod access credentials.' },
395
395
  'guard.destructive.testShell': { en: 'A test file is being changed, moved or deleted with a shell command. Weakening a test to make it pass is forbidden (AGENTS.md rule 7); edit tests with a file tool. User approval required.' },
396
396
  'guard.destructive.inlineTest': { en: 'An inline script names a test path; it may write to a test file. Only the test-writer agent writes to test files (AGENTS.md rule 7). User approval required.' },
397
+ 'guard.destructive.internalError': { en: 'The shell guard could not check this command ({error}). User approval required.' },
397
398
  'guard.destructive.obfuscated': { en: 'The command looks obfuscated (line ending \\, $\'..\', $(...), `...` or ${...}) and contains a guarded operation. User approval required.' },
398
399
  'guard.destructive.indirectHook': { en: 'A kit guard script is run with an indirect spelling ($VARIABLE, $(...), `...`, ${...} or * ?); the rule text cannot see inside it. User approval required.' },
399
400
  'guard.destructive.envShell': { en: 'The command names a .env file (secrets). The AI does not read or write it; if needed, the user does it. User approval required.' },
@@ -391,6 +391,7 @@
391
391
  "guard.destructive.remoteDb": "Komut localhost dışındaki bir veritabanına bağlanıyor (prod olabilir). Kullanıcı onayı gerekli; AI prod erişim bilgisi kullanmamalı.",
392
392
  "guard.destructive.testShell": "Test dosyası kabuk komutuyla değiştiriliyor, taşınıyor veya siliniyor. Testi geçirmek için testi zayıflatmak yasak (AGENTS.md kural 7); test düzenlemesini dosya aracıyla yap. Kullanıcı onayı gerekli.",
393
393
  "guard.destructive.inlineTest": "Satır içi betik bir test yolunu anıyor; test dosyasına yazıyor olabilir. Test dosyasına yalnız test-writer ajanı yazar (AGENTS.md kural 7). Kullanıcı onayı gerekli.",
394
+ "guard.destructive.internalError": "Kabuk kalkanı bu komutu denetleyemedi ({error}). Kullanıcı onayı gerekli.",
394
395
  "guard.destructive.obfuscated": "Komut gizlenmiş görünüyor (satır sonu \\, $'..', $(...), `...` veya ${...}) ve korunan bir işlem içeriyor. Kullanıcı onayı gerekli.",
395
396
  "guard.destructive.indirectHook": "Kitin koruma betiği dolaylı yazımla çalıştırılıyor ($DEĞİŞKEN, $(...), `...`, ${...} veya * ?); kural metni bunun içini göremez. Kullanıcı onayı gerekli.",
396
397
  "guard.destructive.envShell": "Komut bir .env dosyasını (sırlar) anıyor. AI onu okumaz, yazmaz; gerekirse kullanıcı yapar. Kullanıcı onayı gerekli.",
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  </p>
7
7
 
8
8
  <p align="center">
9
- <a href="kit/CHANGELOG.md"><img alt="Version" src="https://img.shields.io/badge/version-3.49.2-2a78d6?style=flat-square"></a>
9
+ <a href="kit/CHANGELOG.md"><img alt="Version" src="https://img.shields.io/badge/version-3.49.3-2a78d6?style=flat-square"></a>
10
10
  <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-2a78d6?style=flat-square"></a>
11
11
  <img alt="Claude Code" src="https://img.shields.io/badge/Claude%20Code-ready-d97757?style=flat-square">
12
12
  <img alt="Also Codex and Antigravity" src="https://img.shields.io/badge/also-Codex%20%C2%B7%20Antigravity-5b6577?style=flat-square">
package/README.tr.md CHANGED
@@ -6,7 +6,7 @@
6
6
  </p>
7
7
 
8
8
  <p align="center">
9
- <a href="kit/CHANGELOG.md"><img alt="Sürüm" src="https://img.shields.io/badge/s%C3%BCr%C3%BCm-3.49.2-2a78d6?style=flat-square"></a>
9
+ <a href="kit/CHANGELOG.md"><img alt="Sürüm" src="https://img.shields.io/badge/s%C3%BCr%C3%BCm-3.49.3-2a78d6?style=flat-square"></a>
10
10
  <a href="LICENSE"><img alt="Lisans: MIT" src="https://img.shields.io/badge/lisans-MIT-2a78d6?style=flat-square"></a>
11
11
  <img alt="Claude Code" src="https://img.shields.io/badge/Claude%20Code-haz%C4%B1r-d97757?style=flat-square">
12
12
  <img alt="Codex ve Antigravity" src="https://img.shields.io/badge/ayr%C4%B1ca-Codex%20%C2%B7%20Antigravity-5b6577?style=flat-square">
package/kit/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  The kit version is in `kit/VERSION`. To update a project, run `/kit-update` in it.
4
4
  Items with an "Adapt by hand" note concern files owned by the project; the update does not touch them.
5
5
 
6
+ ## [3.49.3] - 2026-10-30
7
+
8
+ ### Security
9
+ - **The shell guard no longer crashes on a glob with an unclosed `[`** (`…; echo .[`, `jq '.["a"]'`). The 3.49.2 `.env` glob check threw there, and a crashed hook lets the command run. Globs are now matched without regular expressions, so `.**********z` cannot stall the hook either, and any unexpected error in the shell guard asks you instead of passing.
10
+ - `.env` globs also cover `.env.staging`, `.env.test` and `.env.preview` (`cat .env.st*`).
11
+
6
12
  ## [3.49.2] - 2026-10-30
7
13
 
8
14
  ### Security
package/kit/VERSION CHANGED
@@ -1 +1 @@
1
- 3.49.2
1
+ 3.49.3
package/kit/guide.html CHANGED
@@ -361,7 +361,7 @@ footer .container { display: flex; flex-wrap: wrap; gap: 16px; justify-content:
361
361
  <section class="hero">
362
362
  <div class="container">
363
363
  <div>
364
- <span class="pill"><b>v3.49.2</b> Starter kit for Claude Code · MIT</span>
364
+ <span class="pill"><b>v3.49.3</b> Starter kit for Claude Code · MIT</span>
365
365
  <h1>The AI builds.<br><em>You steer.</em></h1>
366
366
  <p class="sub">TillerHand takes a product from idea to production and maintenance with Claude Code. At every important decision the AI gives you the options, its recommendation and the cost of each option. An expert board reviews the work, and guardrails stop mistakes before they happen.</p>
367
367
  <div class="setup">
@@ -1241,7 +1241,7 @@ Next: G-013 password reset · pending decision: email service</pre>
1241
1241
  <footer>
1242
1242
  <div class="container">
1243
1243
  <a class="brand" href="#top" style="font-size:16px"><svg class="mark" style="width:22px;height:22px"><use href="#mark"/></svg>TillerHand</a>
1244
- <span>Version 3.49.2 · <a href="https://github.com/burakdogrul/TillerHand">GitHub</a> · <a href="https://github.com/burakdogrul/TillerHand/blob/main/LICENSE">MIT license</a> · The KVKK section is a checklist, not legal advice.</span>
1244
+ <span>Version 3.49.3 · <a href="https://github.com/burakdogrul/TillerHand">GitHub</a> · <a href="https://github.com/burakdogrul/TillerHand/blob/main/LICENSE">MIT license</a> · The KVKK section is a checklist, not legal advice.</span>
1245
1245
  </div>
1246
1246
  </footer>
1247
1247
 
package/kit/guide.tr.html CHANGED
@@ -361,7 +361,7 @@ footer .container { display: flex; flex-wrap: wrap; gap: 16px; justify-content:
361
361
  <section class="hero">
362
362
  <div class="container">
363
363
  <div>
364
- <span class="pill"><b>v3.49.2</b> Claude Code için başlangıç kiti · MIT</span>
364
+ <span class="pill"><b>v3.49.3</b> Claude Code için başlangıç kiti · MIT</span>
365
365
  <h1>Kodu AI yazar.<br><em>Dümen sende.</em></h1>
366
366
  <p class="sub">TillerHand, Claude Code ile bir ürünü fikirden canlıya ve bakıma kadar götürür. Her önemli kararda AI sana seçenekleri, önerisini ve her seçeneğin bedelini sunar. Bir uzman kurulu işi denetler, korumalar hatayı daha olmadan durdurur.</p>
367
367
  <div class="setup">
@@ -1245,7 +1245,7 @@ Sıradaki: G-013 şifre sıfırlama · bekleyen karar: e-posta servisi</pre>
1245
1245
  <footer>
1246
1246
  <div class="container">
1247
1247
  <a class="brand" href="#top" style="font-size:16px"><svg class="mark" style="width:22px;height:22px"><use href="#mark"/></svg>TillerHand</a>
1248
- <span>Sürüm 3.49.2 · <a href="https://github.com/burakdogrul/TillerHand">GitHub</a> · <a href="https://github.com/burakdogrul/TillerHand/blob/main/LICENSE">MIT lisansı</a> · KVKK bölümü kontrol listesidir, hukuki görüş değildir.</span>
1248
+ <span>Sürüm 3.49.3 · <a href="https://github.com/burakdogrul/TillerHand">GitHub</a> · <a href="https://github.com/burakdogrul/TillerHand/blob/main/LICENSE">MIT lisansı</a> · KVKK bölümü kontrol listesidir, hukuki görüş değildir.</span>
1249
1249
  </div>
1250
1250
  </footer>
1251
1251
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tillerhand",
3
- "version": "3.49.2",
3
+ "version": "3.49.3",
4
4
  "description": "Vibe coding kit for Claude Code, Codex and Antigravity: rules, commands, guards and project templates.",
5
5
  "license": "MIT",
6
6
  "type": "module",