tillerhand 3.49.10 → 3.49.11

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.
@@ -446,6 +446,7 @@ export const MESSAGES = {
446
446
  'guard.package.timeout': { en: 'could not be verified (timeout)' },
447
447
  'guard.package.network': { en: 'could not be verified (network error)' },
448
448
  'guard.package.tooMany': { en: 'Package gate: {count} packages in one command (more than {max}) are not checked one by one. User approval required.' },
449
+ 'guard.package.depFileUnclear': { en: 'Package gate: the new dependencies in {path} could not be read (the file does not parse or the edit cannot be placed). User approval required.' },
449
450
  'guard.package.error': { en: 'The package gate could not check this command ({error}). User approval required.' },
450
451
  'guard.package.ask': { en: 'Package gate warning: {list}. Before installing, make sure the package is really the one you want.' },
451
452
  'guard.structure.offMap': { en: '"{rel}" is not in the structure map (.claude/structure.json). The right place is probably one of: {top}. If it is temporary or a trial, write it under sandbox/. If a new permanent place is needed, ask it as a technical decision and update the map (docs/architecture/structure.md).' },
@@ -8,6 +8,7 @@
8
8
  import fs from 'node:fs';
9
9
  import path from 'node:path';
10
10
  import { readInput, decide, isMain, findRoot } from './shared.mjs';
11
+ import { boardAfter } from './freeze-guard.mjs';
11
12
  import { t } from './lang.mjs';
12
13
 
13
14
  export const THRESHOLD = { newDays: 30, npmWeeklyDownloads: 500 };
@@ -161,6 +162,48 @@ export function findPackages(command) {
161
162
  return result;
162
163
  }
163
164
 
165
+ // Dependency files written with a file tool: the names the write ADDS are checked like an install (user decision
166
+ // 2026-10-30), so "edit package.json, then a bare npm ci" cannot skip the gate. null: the result does not parse (asked).
167
+ const DEP_FILE = /(^|\/)(package\.json|pyproject\.toml|requirements[^/]*\.txt|requirements\/[^/]+\.txt)$/i;
168
+ const NPM_SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
169
+ // An npm: alias installs its target: "x": "npm:evil@1" adds evil
170
+ const npmDeps = (j) => new Set(NPM_SECTIONS.flatMap((k) => Object.entries(j?.[k] && typeof j[k] === 'object' ? j[k] : {})
171
+ .map(([n, v]) => (typeof v === 'string' && /^npm:/i.test(v) ? npmName(v.slice(4)) : n))).filter(Boolean));
172
+ const requirementNames = (text) => new Set(text.split(/\r?\n/).map((s) => s.trim())
173
+ .filter((s) => s && !s.startsWith('#') && !s.startsWith('-')).map(pypiName).filter(Boolean));
174
+ function pyprojectNames(text) {
175
+ const names = new Set();
176
+ let section = '', inArray = false;
177
+ for (const raw of text.split(/\r?\n/)) {
178
+ const line = raw.trim();
179
+ const head = line.match(/^\[([^\]"']+)\]$/);
180
+ if (head) { section = head[1].trim(); inArray = false; continue; }
181
+ if (section === 'project') {
182
+ if (/^dependencies\s*=\s*\[/.test(line)) inArray = true;
183
+ if (!inArray) continue;
184
+ for (const m of line.matchAll(/["']([^"']+)["']/g)) { const n = pypiName(m[1]); if (n) names.add(n); }
185
+ if (line.includes(']')) inArray = false;
186
+ } else if (/^tool\.poetry(\.group\.[\w-]+)?\.(dev-)?dependencies$/.test(section)) {
187
+ const k = line.match(/^["']?([A-Za-z0-9][\w.-]*)["']?\s*=/);
188
+ if (k && k[1].toLowerCase() !== 'python') names.add(k[1].toLowerCase());
189
+ }
190
+ }
191
+ return names;
192
+ }
193
+ export function addedDependencies(file, before, after) {
194
+ const rel = file.replace(/\\/g, '/');
195
+ const added = (eco, a, b) => [...a].filter((n) => !b.has(n)).map((name) => ({ eco, name }));
196
+ if (/(^|\/)package\.json$/i.test(rel)) {
197
+ let a, b = {};
198
+ try { a = JSON.parse(after); } catch { return null; }
199
+ try { b = JSON.parse(before || '{}'); } catch { /* a broken file before: every name counts as added */ }
200
+ return added('npm', npmDeps(a), npmDeps(b));
201
+ }
202
+ if (/(^|\/)requirements[^/]*\.txt$|(^|\/)requirements\/[^/]+\.txt$/i.test(rel)) return added('pypi', requirementNames(after), requirementNames(before));
203
+ if (/(^|\/)pyproject\.toml$/i.test(rel)) return added('pypi', pyprojectNames(after), pyprojectNames(before));
204
+ return [];
205
+ }
206
+
164
207
  function distance(a, b) {
165
208
  const d = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);
166
209
  for (let j = 1; j <= b.length; j++) d[0][j] = j;
@@ -229,6 +272,15 @@ async function gate(g) {
229
272
  // The kit's own package (/start and /adopt run npx -y tillerhand@<version> install): the kit already trusts itself
230
273
  // Both only for the plain spelling: name@1.2 or name@npm:other downloads, it does not run the local bin or the kit's release
231
274
  const packages = findPackages(g.command).filter((p) => !(p.local && p.spec === p.name && bin(p.name)) && !(p.eco === 'npm' && /^tillerhand(@\d+\.\d+\.\d+)?$/.test(p.spec ?? '')));
275
+ for (const f of g.files) {
276
+ if (!DEP_FILE.test(String(f.path).replace(/\\/g, '/'))) continue;
277
+ const full = path.resolve(g.dir, f.path);
278
+ let disk = ''; try { disk = fs.readFileSync(full, 'utf8'); } catch { /* new file */ }
279
+ const after = boardAfter(disk, f);
280
+ const added = after === null ? null : addedDependencies(f.path, disk, after);
281
+ if (added === null) decide(g.tool, 'ask', t(g.root, 'guard.package.depFileUnclear', { path: f.path }));
282
+ packages.push(...added);
283
+ }
232
284
  if (!packages.length) return;
233
285
  if (packages.length > MAX_PACKAGES) decide(g.tool, 'ask', t(g.root, 'guard.package.tooMany', { count: packages.length, max: MAX_PACKAGES }));
234
286
  const results = await Promise.all(packages.map(async (p) => ({ ...p, ...(await check(p)) })));
@@ -443,6 +443,7 @@
443
443
  "guard.package.timeout": "doğrulanamadı (zaman aşımı)",
444
444
  "guard.package.network": "doğrulanamadı (ağ hatası)",
445
445
  "guard.package.tooMany": "Paket kapısı: tek komutta {count} paket var ({max} sınırının üstünde), tek tek denetlenmiyor. Kullanıcı onayı gerekli.",
446
+ "guard.package.depFileUnclear": "Paket kapısı: {path} içindeki yeni bağımlılıklar okunamadı (dosya ayrıştırılamıyor ya da düzenleme yerleştirilemiyor). Kullanıcı onayı gerekli.",
446
447
  "guard.package.error": "Paket kapısı bu komutu denetleyemedi ({error}). Kullanıcı onayı gerekli.",
447
448
  "guard.package.ask": "Paket kapısı uyarısı: {list}. Kurmadan önce paketin gerçekten istenen paket olduğundan emin ol.",
448
449
  "guard.structure.offMap": "\"{rel}\" yapı haritasında yok (.claude/structure.json). Doğru yer muhtemelen şunlardan biri: {top}. Geçici veya deneme ise sandbox/ altına yaz. Kalıcı yeni bir yer gerekiyorsa bunu teknik karar olarak sor ve haritayı güncelle (docs/architecture/structure.md).",
@@ -113,6 +113,11 @@
113
113
  {
114
114
  "type": "command",
115
115
  "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/test-guard.mjs\""
116
+ },
117
+ {
118
+ "type": "command",
119
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/package-gate.mjs\"",
120
+ "timeout": 30
116
121
  }
117
122
  ]
118
123
  },
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.10-2a78d6?style=flat-square"></a>
9
+ <a href="kit/CHANGELOG.md"><img alt="Version" src="https://img.shields.io/badge/version-3.49.11-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.10-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.11-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,11 @@
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.11] - 2026-10-30
7
+
8
+ ### Security
9
+ - **The package gate also checks dependency files.** When the AI adds a dependency to `package.json` (any section, an `npm:` alias's target too), `requirements*.txt` or `pyproject.toml` (`[project] dependencies`, Poetry dependency tables) with a file tool, the new names are checked like an install: a missing package is blocked, a new, rarely downloaded or look-alike one is asked. Before, editing `package.json` and then running a bare `npm ci` skipped the gate. Version changes and other fields are not checked. Runs on Claude Code, Codex and Antigravity; a file that does not parse after the write asks.
10
+
6
11
  ## [3.49.10] - 2026-10-30
7
12
 
8
13
  ### Security
package/kit/VERSION CHANGED
@@ -1 +1 @@
1
- 3.49.10
1
+ 3.49.11
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.10</b> Starter kit for Claude Code · MIT</span>
364
+ <span class="pill"><b>v3.49.11</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.10 · <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.11 · <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.10</b> Claude Code için başlangıç kiti · MIT</span>
364
+ <span class="pill"><b>v3.49.11</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.10 · <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.11 · <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
 
@@ -12,7 +12,8 @@
12
12
  { "type": "command", "command": "node .claude/hooks/guard-secrets.mjs" },
13
13
  { "type": "command", "command": "node .claude/hooks/freeze-guard.mjs" },
14
14
  { "type": "command", "command": "node .claude/hooks/structure-guard.mjs" },
15
- { "type": "command", "command": "node .claude/hooks/test-guard.mjs" }
15
+ { "type": "command", "command": "node .claude/hooks/test-guard.mjs" },
16
+ { "type": "command", "command": "node .claude/hooks/package-gate.mjs", "timeout": 30 }
16
17
  ]
17
18
  }
18
19
  ]
@@ -15,7 +15,8 @@
15
15
  { "type": "command", "command": "node \"$(git rev-parse --show-toplevel)/.claude/hooks/guard-secrets.mjs\"", "commandWindows": "node .claude/hooks/guard-secrets.mjs" },
16
16
  { "type": "command", "command": "node \"$(git rev-parse --show-toplevel)/.claude/hooks/freeze-guard.mjs\"", "commandWindows": "node .claude/hooks/freeze-guard.mjs" },
17
17
  { "type": "command", "command": "node \"$(git rev-parse --show-toplevel)/.claude/hooks/structure-guard.mjs\"", "commandWindows": "node .claude/hooks/structure-guard.mjs" },
18
- { "type": "command", "command": "node \"$(git rev-parse --show-toplevel)/.claude/hooks/test-guard.mjs\"", "commandWindows": "node .claude/hooks/test-guard.mjs" }
18
+ { "type": "command", "command": "node \"$(git rev-parse --show-toplevel)/.claude/hooks/test-guard.mjs\"", "commandWindows": "node .claude/hooks/test-guard.mjs" },
19
+ { "type": "command", "command": "node \"$(git rev-parse --show-toplevel)/.claude/hooks/package-gate.mjs\"", "commandWindows": "node .claude/hooks/package-gate.mjs", "timeout": 30 }
19
20
  ]
20
21
  },
21
22
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tillerhand",
3
- "version": "3.49.10",
3
+ "version": "3.49.11",
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",