tillerhand 3.49.7 → 3.49.8
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.
- package/.claude/hooks/messages.mjs +2 -0
- package/.claude/hooks/package-gate.mjs +34 -18
- package/.claude/i18n/tr.json +2 -0
- package/README.md +1 -1
- package/README.tr.md +1 -1
- package/kit/CHANGELOG.md +6 -0
- package/kit/VERSION +1 -1
- package/kit/guide.html +2 -2
- package/kit/guide.tr.html +2 -2
- package/package.json +1 -1
|
@@ -444,6 +444,8 @@ export const MESSAGES = {
|
|
|
444
444
|
'guard.package.newPypi': { en: 'first release was published {age} days ago' },
|
|
445
445
|
'guard.package.timeout': { en: 'could not be verified (timeout)' },
|
|
446
446
|
'guard.package.network': { en: 'could not be verified (network error)' },
|
|
447
|
+
'guard.package.tooMany': { en: 'Package gate: {count} packages in one command (more than {max}) are not checked one by one. User approval required.' },
|
|
448
|
+
'guard.package.error': { en: 'The package gate could not check this command ({error}). User approval required.' },
|
|
447
449
|
'guard.package.ask': { en: 'Package gate warning: {list}. Before installing, make sure the package is really the one you want.' },
|
|
448
450
|
'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).' },
|
|
449
451
|
'guard.structure.prefix': { en: 'Structure: {reason}' },
|
|
@@ -24,19 +24,20 @@ const POPULAR = {
|
|
|
24
24
|
'langchain', 'transformers', 'torch', 'scikit-learn', 'aiohttp', 'tenacity', 'ruff', 'black', 'mypy'],
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
// Case-insensitive (the Windows shell is); npm's install aliases (in, ins, isntall, it…) count as install
|
|
27
28
|
const COMMANDS = [
|
|
28
|
-
[/^(npm|pnpm|bun)\s+(i|install|add)\b
|
|
29
|
-
[/^uv\s+(add|pip\s+install)\b
|
|
29
|
+
[/^(npm|pnpm|bun)\s+(i|in|ins|inst|insta|instal|install|isnt|isnta|isntal|isntall|it|add)\b/i, 'npm'], [/^yarn\s+(global\s+)?add\b/i, 'npm'], [/^npx\s+expo\s+install\b/i, 'npm'],
|
|
30
|
+
[/^uv\s+(add|pip\s+install)\b/i, 'pypi'], [/^(python3?\s+-m\s+)?pip3?\s+install\b/i, 'pypi'], [/^poetry\s+add\b/i, 'pypi'],
|
|
30
31
|
];
|
|
31
32
|
|
|
32
33
|
// Download-and-run: the first positional is the package (the rest are its arguments), or the values of the package flags.
|
|
33
34
|
// local: the runner uses a bin the project already has before downloading (npx, npm exec, bunx); pnpm/yarn dlx always download.
|
|
34
35
|
// positional: false for uv run (its positional is a script; only --with names packages).
|
|
35
36
|
const EXEC = [
|
|
36
|
-
{ re: /^(npx|npm\s+(exec|x)|bunx|bun\s+x)\b
|
|
37
|
-
{ re: /^(pnpx|pnpm\s+dlx|yarn\s+dlx)\b
|
|
38
|
-
{ re: /^(uvx|uv\s+tool\s+run|pipx\s+run)\b
|
|
39
|
-
{ re: /^uv\s+run\b
|
|
37
|
+
{ re: /^(npx|npm\s+(exec|x)|bunx|bun\s+x)\b/i, eco: 'npm', local: true, positional: true },
|
|
38
|
+
{ re: /^(pnpx|pnpm\s+dlx|yarn\s+dlx)\b/i, eco: 'npm', local: false, positional: true },
|
|
39
|
+
{ re: /^(uvx|uv\s+tool\s+run|pipx\s+run)\b/i, eco: 'pypi', local: false, positional: true },
|
|
40
|
+
{ re: /^uv\s+run\b/i, eco: 'pypi', local: false, positional: false },
|
|
40
41
|
];
|
|
41
42
|
const FLAGS = {
|
|
42
43
|
npm: { pkg: /^(-p|--package)$/, value: /^(-p|--package|-c|--call|--registry|--prefix|--cache|--userconfig|--globalconfig|-w|--workspace|--node-options|--script-shell|--shell|--loglevel|-C|--dir|--filter)$/ },
|
|
@@ -68,14 +69,17 @@ function execArgs(args, eco, positional) {
|
|
|
68
69
|
|
|
69
70
|
// What runs a segment: wrappers, VAR=x, a path or .cmd/.exe on the tool, and the manager's own flags before its subcommand are
|
|
70
71
|
// dropped (FOO=1 npx x, cmd /c npx x, bash -c "npx x", /usr/bin/npx x, npx.cmd x, pnpm -C . dlx x, npm --silent exec x).
|
|
71
|
-
|
|
72
|
+
// Also timeout/nice/stdbuf, shell keywords (then, do, else, !) and combined shell flags (bash -lc).
|
|
73
|
+
const WRAPPER = /^(?:\(|\{|!\s*|[A-Za-z_]\w*=\S*\s+|(?:sudo|env|time|nohup|exec|command|xargs|corepack|call|start|then|do|else|ionice|stdbuf)\s+|timeout\s+(?:-\S+\s+)*\S+\s+|nice\s+(?:-n\s*\S+\s+|-\d+\s+)?|(?:bash|sh|zsh|dash|cmd|pwsh|powershell)(?:\.exe)?\s+(?:-\w*c|\/c|\/k|-Command)\s+["']?)/i;
|
|
72
74
|
const SUBCOMMAND = /^(install|i|add|exec|x|dlx|tool|pip|run|global)$/;
|
|
73
75
|
export function normalize(part) {
|
|
74
76
|
let s = part.trim(), prev;
|
|
75
77
|
do { prev = s; s = s.replace(WRAPPER, '').trim(); } while (s !== prev);
|
|
76
|
-
|
|
78
|
+
// A path (Windows \ too, quoted with spaces) and .cmd/.exe on the tool: "C:\Program Files\nodejs\npm.cmd" i x → npm i x
|
|
79
|
+
s = s.replace(/^"(?:[^"]*[\\/])?([\w.-]+?)(?:\.cmd|\.exe|\.ps1)?"(?=\s|$)/i, '$1')
|
|
80
|
+
.replace(/^["']?(?:[^\s"']*[\\/])?([\w.-]+?)(?:\.cmd|\.exe|\.ps1)?["']?(?=\s|$)/i, '$1');
|
|
77
81
|
const t = s.split(/\s+/);
|
|
78
|
-
if (/^(npm|pnpm|yarn|bun|uv)
|
|
82
|
+
if (/^(npm|pnpm|yarn|bun|uv)$/i.test(t[0])) {
|
|
79
83
|
let i = 1;
|
|
80
84
|
while (i < t.length && t[i].startsWith('-')) i += !t[i].includes('=') && t[i + 1] && !t[i + 1].startsWith('-') && !SUBCOMMAND.test(t[i + 1]) ? 2 : 1;
|
|
81
85
|
s = [t[0], ...t.slice(i)].join(' ');
|
|
@@ -85,13 +89,15 @@ export function normalize(part) {
|
|
|
85
89
|
|
|
86
90
|
const NOT_REGISTRY = /^(\.|\/|file:|link:|git\+|git:|https?:|github:|workspace:)/;
|
|
87
91
|
function npmName(arg) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
// name@npm:other installs "other": the alias target is checked (a loop: a deep a@npm:a@npm:… chain must not overflow
|
|
93
|
+
// the stack). name@github:x/y, @https:… are not from the registry (like a bare URL, not checked here).
|
|
94
|
+
for (;;) {
|
|
95
|
+
if (NOT_REGISTRY.test(arg)) return null;
|
|
96
|
+
const at = arg.indexOf('@', 1), spec = at > 0 ? arg.slice(at + 1) : '';
|
|
97
|
+
if (spec.startsWith('npm:')) { arg = spec.slice(4); continue; }
|
|
98
|
+
if (/[:/]/.test(spec)) return null;
|
|
99
|
+
return (at > 0 ? arg.slice(0, at) : arg) || null;
|
|
100
|
+
}
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
function pypiName(arg) {
|
|
@@ -101,7 +107,9 @@ function pypiName(arg) {
|
|
|
101
107
|
|
|
102
108
|
export function findPackages(command) {
|
|
103
109
|
const result = [];
|
|
104
|
-
|
|
110
|
+
// Every place a new command can start: chains, a newline, a background &, $( ), backticks, ( ) and { } groups.
|
|
111
|
+
// A redirect's & (2>&1, &>) is not a separator.
|
|
112
|
+
for (const part of command.split(/\n|&&|\|\||;|\||(?<![>&])&(?![>&])|\$\(|`|[(){}]/)) {
|
|
105
113
|
const s = normalize(part);
|
|
106
114
|
const match = COMMANDS.find(([re]) => re.test(s));
|
|
107
115
|
const exec = match ? null : EXEC.find((e) => e.re.test(s));
|
|
@@ -182,15 +190,23 @@ export async function check({ eco, name }, get = fetch, now = Date.now()) {
|
|
|
182
190
|
}
|
|
183
191
|
}
|
|
184
192
|
|
|
193
|
+
// More names than this in one command are asked without checking each one (unbounded fetches would hit the hook timeout)
|
|
194
|
+
const MAX_PACKAGES = 50;
|
|
185
195
|
async function hook() {
|
|
186
196
|
const g = readInput();
|
|
187
197
|
if (!g) process.exit(0);
|
|
198
|
+
// A gate that throws or times out lets the command run: any error asks instead
|
|
199
|
+
try { await gate(g); } catch (e) { decide(g.tool, 'ask', t(g.root, 'guard.package.error', { error: String(e?.message ?? e).slice(0, 200) })); }
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
async function gate(g) {
|
|
188
203
|
// npx/bunx run a bin the project already installed without downloading: a local bin (tsc, eas) is not a package name
|
|
189
204
|
const bin = (name) => /^\w[\w.-]*$/.test(name) && [g.dir, g.root].some((d) => fs.existsSync(path.join(d, 'node_modules', '.bin', name)));
|
|
190
205
|
// The kit's own package (/start and /adopt run npx -y tillerhand@<version> install): the kit already trusts itself
|
|
191
206
|
// 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
|
|
192
207
|
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 ?? '')));
|
|
193
|
-
if (!packages.length)
|
|
208
|
+
if (!packages.length) return;
|
|
209
|
+
if (packages.length > MAX_PACKAGES) decide(g.tool, 'ask', t(g.root, 'guard.package.tooMany', { count: packages.length, max: MAX_PACKAGES }));
|
|
194
210
|
const results = await Promise.all(packages.map(async (p) => ({ ...p, ...(await check(p)) })));
|
|
195
211
|
const missing = results.filter((s) => s.status === 'missing' && !s.unsure);
|
|
196
212
|
if (missing.length) {
|
package/.claude/i18n/tr.json
CHANGED
|
@@ -441,6 +441,8 @@
|
|
|
441
441
|
"guard.package.newPypi": "ilk sürümü {age} gün önce yayımlanmış",
|
|
442
442
|
"guard.package.timeout": "doğrulanamadı (zaman aşımı)",
|
|
443
443
|
"guard.package.network": "doğrulanamadı (ağ hatası)",
|
|
444
|
+
"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.",
|
|
445
|
+
"guard.package.error": "Paket kapısı bu komutu denetleyemedi ({error}). Kullanıcı onayı gerekli.",
|
|
444
446
|
"guard.package.ask": "Paket kapısı uyarısı: {list}. Kurmadan önce paketin gerçekten istenen paket olduğundan emin ol.",
|
|
445
447
|
"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).",
|
|
446
448
|
"guard.structure.prefix": "Yapı: {reason}",
|
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.
|
|
9
|
+
<a href="kit/CHANGELOG.md"><img alt="Version" src="https://img.shields.io/badge/version-3.49.8-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.
|
|
9
|
+
<a href="kit/CHANGELOG.md"><img alt="Sürüm" src="https://img.shields.io/badge/s%C3%BCr%C3%BCm-3.49.8-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.8] - 2026-10-30
|
|
7
|
+
|
|
8
|
+
### Security
|
|
9
|
+
- **The package gate sees installs in more places.** An `npm i` after a newline, a background `&`, inside `$( )`, backticks, `if … then`, `( )` or `{ }` is checked, and so are `timeout`/`nice`/`bash -lc` in front, a quoted Windows path (`"C:\Program Files\nodejs\npm.cmd" i`), upper case (`NPM i`) and npm's install aliases (`npm in`, `npm isntall`, `npm it`…).
|
|
10
|
+
- A command the package gate cannot check (a parser error, more than 50 packages at once) asks you instead of running unchecked.
|
|
11
|
+
|
|
6
12
|
## [3.49.7] - 2026-10-30
|
|
7
13
|
|
|
8
14
|
### Security
|
package/kit/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.49.
|
|
1
|
+
3.49.8
|
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.
|
|
364
|
+
<span class="pill"><b>v3.49.8</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.
|
|
1244
|
+
<span>Version 3.49.8 · <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.
|
|
364
|
+
<span class="pill"><b>v3.49.8</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.
|
|
1248
|
+
<span>Sürüm 3.49.8 · <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
|
|