tillerhand 3.31.0 → 3.31.2

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.
@@ -151,6 +151,8 @@ alerts.sort((x, y) => SEVERITY_ORDER[x.sev] - SEVERITY_ORDER[y.sev]);
151
151
  const health = alerts.some((x) => x.sev === 'critical') ? 'critical' : alerts.some((x) => x.sev === 'warn') ? 'warn' : 'good';
152
152
 
153
153
  // Activity: commit count for the last 14 days and the merged feed
154
+ // "2026-10-26" alone is read as UTC midnight (the day before west of UTC): a date-only text is local midnight
155
+ const toDate = (t) => new Date(/^\d{4}-\d{2}-\d{2}$/.test(String(t).trim()) ? `${String(t).trim()}T00:00` : t);
154
156
  const dayKey = (t) => { const z = new Date(t); return `${z.getFullYear()}-${String(z.getMonth() + 1).padStart(2, '0')}-${String(z.getDate()).padStart(2, '0')}`; };
155
157
  const days = [...Array(14)].map((_, i) => { const z = new Date(); z.setDate(z.getDate() - (13 - i)); return dayKey(z); });
156
158
  const dayCounts = Object.fromEntries(days.map((g) => [g, 0]));
@@ -165,8 +167,8 @@ const feed = [
165
167
  ...d.decisions.map((k) => ({ time: k.date, kind: 'karar', title: `${k.question} → ${k.chosen}`, extra: `${k.topic} · ${k.decidedBy}` })),
166
168
  ...d.board.meetings.map((t) => ({ time: t.date, kind: 'kurul', title: `${t.kind}: ${t.scope} → ${t.result}`, extra: t.attendees })),
167
169
  ...maintenance.map((b) => ({ time: b.date, kind: 'bakım', title: `${b.kind}: ${b.findings}`, extra: b.work })),
168
- ].filter((x) => x.time && !Number.isNaN(new Date(x.time).getTime()))
169
- .sort((x, y) => new Date(y.time) - new Date(x.time)).slice(0, 50);
170
+ ].filter((x) => x.time && !Number.isNaN(toDate(x.time).getTime()))
171
+ .sort((x, y) => toDate(y.time) - toDate(x.time)).slice(0, 50);
170
172
 
171
173
  const autoDecisions = d.decisions.filter((k) => /otomatik/i.test(k.decidedBy)).length;
172
174
  const rejectCount = d.board.meetings.filter((t) => /RET/.test(t.result) && !/KOŞULLU/.test(t.result)).length;
@@ -184,7 +186,7 @@ const progressBar = (b, t, cls = '') => `<div class="bar ${cls}" role="img" aria
184
186
  const empty = (text, action = '') => `<p class="empty">${md(text)}${action ? ` <code>${e(action)}</code>` : ''}</p>`;
185
187
  const SEV = { critical: ['⛔', 'Kritik'], warn: ['⚠', 'Dikkat'], info: ['ℹ', 'Bilgi'], good: ['✓', 'Yolunda'] };
186
188
  const badge = (sev, caption) => `<span class="badge ${sev}"><span class="ic" aria-hidden="true">${SEV[sev][0]}</span> ${e(caption ?? SEV[sev][1])}</span>`;
187
- const fmtDate = (t) => { const z = new Date(t); return Number.isNaN(z.getTime()) ? e(t) : z.toLocaleString('tr-TR', { day: '2-digit', month: 'short', hour: t.length > 10 ? '2-digit' : undefined, minute: t.length > 10 ? '2-digit' : undefined }); };
189
+ const fmtDate = (t) => { const z = toDate(t); return Number.isNaN(z.getTime()) ? e(t) : z.toLocaleString('tr-TR', { day: '2-digit', month: 'short', hour: t.length > 10 ? '2-digit' : undefined, minute: t.length > 10 ? '2-digit' : undefined }); };
188
190
  const limitSeverity = (p, k) => (p == null ? 'info' : p >= THRESHOLDS[k].red ? 'critical' : p >= THRESHOLDS[k].yellow ? 'warn' : 'good');
189
191
  const TOPICS = ['urun', 'teknik', 'tasarim', 'icerik', 'veri', 'guvenlik', 'maliyet'];
190
192
 
@@ -256,8 +258,8 @@ ${lastRetro ? `<p class="muted small">Son retro: ${e(lastRetro)}</p>` : ''}</sec
256
258
  const maxDay = Math.max(1, ...Object.values(dayCounts));
257
259
  const activity = `
258
260
  <section class="card"><h3>Commit · son 14 gün <span class="muted small">(toplam ${days.reduce((t, g) => t + dayCounts[g], 0)})</span></h3>
259
- <div class="spark" role="img" aria-label="Son 14 günde günlük commit sayısı">${days.map((g) => `<div class="sb" data-tip="${new Date(g).toLocaleDateString('tr-TR', { day: 'numeric', month: 'short', weekday: 'short' })}: ${dayCounts[g]} commit"><span style="height:${dayCounts[g] ? Math.max(6, (dayCounts[g] / maxDay) * 100) : 0}%"></span></div>`).join('')}</div>
260
- <div class="spark-x muted small"><span>${new Date(days[0]).toLocaleDateString('tr-TR', { day: 'numeric', month: 'short' })}</span><span>bugün</span></div>
261
+ <div class="spark" role="img" aria-label="Son 14 günde günlük commit sayısı">${days.map((g) => `<div class="sb" data-tip="${toDate(g).toLocaleDateString('tr-TR', { day: 'numeric', month: 'short', weekday: 'short' })}: ${dayCounts[g]} commit"><span style="height:${dayCounts[g] ? Math.max(6, (dayCounts[g] / maxDay) * 100) : 0}%"></span></div>`).join('')}</div>
262
+ <div class="spark-x muted small"><span>${toDate(days[0]).toLocaleDateString('tr-TR', { day: 'numeric', month: 'short' })}</span><span>bugün</span></div>
261
263
  <details class="tablo-gor"><summary class="muted small">Tablo olarak göster</summary><div class="tw"><table><thead><tr><th>Gün</th><th>Commit</th></tr></thead><tbody>${days.map((g) => `<tr><td class="num">${g}</td><td class="num">${dayCounts[g]}</td></tr>`).join('')}</tbody></table></div></details>
262
264
  </section>
263
265
  <section class="card"><h3>Akış</h3>${feed.length ? `<ul class="feed">${feed.map(feedRow).join('')}</ul>` : empty('Henüz hareket yok.')}</section>`;
@@ -254,7 +254,7 @@ const PERMISSION_EQUIVALENT = [
254
254
  ['ask', /freeze-guard\.mjs['"]?\s+['"]?--plan-add\b/i, 'Onaylı planın dosya listesi genişliyor (kapsam kalkanı)'],
255
255
  ['ask', /freeze-guard\.mjs['"]?\s+['"]?--plan-save\b/i, 'Kapsam kalkanının dosya listesi yazılıyor'],
256
256
  ['ask', /freeze-guard\.mjs['"]?\s+['"]?--plan-end\b/i, 'Kapsam kalkanı kapatılıyor'],
257
- ['ask', /commit-gate\.mjs['"]?\s+['"]?--skip\b/i, 'Commit kapısı ve kurul denetimi atlanıyor'],
257
+ ['ask', /commit-gate\.mjs\b[^;&|\n]*--skip\b/i, 'Commit kapısı ve kurul denetimi atlanıyor'],
258
258
  ['ask', /prefs\.mjs['"]?\s+['"]?--(add|remove)\b/i, 'Kişisel tercih dosyası (~/.vibe-kit/tercihler.md) değişiyor'],
259
259
  ['ask', /(?<!(?:^|[\s'"=])(?:\.[\\/]|\$\{?CLAUDE_PROJECT_DIR\}?[\\/]?)?\.claude[\\/]?hooks[\\/]?commit-)gate\.mjs['"]?\s+['"]?--approve\b/i, 'Deterministik kapının çalıştıracağı komutlar onaylanıyor'],
260
260
  // Shell counterpart of the PROTECTED rule on file tools: a command that names a rule or guard file and writes/deletes is asked
@@ -270,8 +270,16 @@ export const unquote = (s) => s.replace(/\$'((?:[^'\\]|\\.)*)'/g, '$1').replace(
270
270
  const RANK = { deny: 2, ask: 1 };
271
271
  const strictest = (...s) => s.filter(Boolean).sort((a, b) => RANK[b.decision] - RANK[a.decision])[0] ?? null;
272
272
 
273
+ // A commit message that only names a command (git commit -m "commit-gate.mjs --skip anlatıldı") is text, not a call.
274
+ // Emptied only when the WHOLE command is one plain commit (optionally after "git add … &&") and the message is its last
275
+ // word: then nothing else can hide in it. No quote, $, `, (, #, <, > before the message: a quote opened earlier
276
+ // would make a real command look like the message. The message must be safe in bash, PowerShell and cmd alike: no
277
+ // $ ` (code), no \ (PowerShell and cmd do not escape with it, "a\" ends the string), no typographic quotes U+2018-U+201F
278
+ // (PowerShell ends strings at them), no newline and no ; & | < > ^ % !. Anything else keeps its text and is checked.
279
+ const PLAIN_COMMIT = /^(\s*(?:git(?:\.exe)?\s+add\s[^;&|\n$`()"'\\#<>\u2018-\u201f]*&&\s*)?git(?:\.exe)?\s[^;&|\n$`()"'\\#<>\u2018-\u201f]*?\bcommit(?![\w.-])[^;&|\n$`()"'\\#<>\u2018-\u201f]*?\s(?:-[a-zA-Z]*m|--message)(?:\s+|=))("[^"\\$`\u2018-\u201f\r\n;&|<>^%!]*"|'[^'\u2018-\u201f\r\n;&|<>^%!]*')\s*$/;
273
280
  export function permissionEquivalent(command) {
274
- return strictest(permissionOne(command), permissionOne(unquote(command)), permissionOne(blankQuoted(command)));
281
+ const c = command.replace(PLAIN_COMMIT, '$1""');
282
+ return strictest(permissionOne(c), permissionOne(unquote(c)), permissionOne(blankQuoted(c)));
275
283
  }
276
284
  function permissionOne(command) {
277
285
  const k = command.replace(/\s+/g, ' ').replace(/\b2>&1|\b2>\s*(\/dev\/null|nul)\b/gi, ''); // an error redirect is not a write
package/README.en.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.31.0-2a78d6?style=flat-square"></a>
9
+ <a href="kit/CHANGELOG.md"><img alt="Version" src="https://img.shields.io/badge/version-3.31.2-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.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.31.0-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.31.2-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,18 @@
3
3
  Kit sürümü `kit/VERSION` dosyasında. Bir projeyi güncellemek: projede `/kit-update`.
4
4
  "Elle uyarla" notu olan maddeler projeye ait dosyalardadır; güncelleme bunlara dokunmaz.
5
5
 
6
+ ## [3.31.2] - 2026-10-26
7
+
8
+ ### Düzeltilen
9
+ - **Codex ve Antigravity: commit mesajı yoluyla koruma atlatılamıyor.** 3.31.1'de düz commit mesajı komut sayılmıyordu. PowerShell akıllı tırnakları (`“ ” ‘ ’ „`) da dizge sınırı saydığı için böyle tırnaklı bir mesajın arkasına gizlenen komut onaysız geçebiliyordu. Artık mesaj yalnız bash, PowerShell ve cmd'de aynı şekilde tek dizge kalıyorsa boşaltılır: akıllı tırnak, `\`, `$`, ters tırnak, satır sonu ya da `; & | < > ^ % !` içeren mesaj yine denetlenir.
10
+
11
+ ## [3.31.1] - 2026-10-26
12
+
13
+ ### Düzeltilen
14
+ - **Pano tarihleri saat dilimine göre kaymıyor.** Karar, kurul ve bakım günlüğündeki yalnız tarih olan satırlar (`2026-10-26`) UTC gece yarısı sayılıyordu; UTC'nin batısında bir önceki gün görünüyordu. Artık yerel gün olarak okunur. Commit grafiğinin gün etiketleri de aynı şekilde düzeldi.
15
+ - **Codex ve Antigravity: commit mesajında geçen `--skip` onay sormuyor.** `git commit -m "… commit-gate.mjs --skip …"` gibi bir mesaj, "commit kapısı atlanıyor" diye boşuna onaya düşüyordu. Tek başına (ya da `git add … &&` ardından) çalışan bir commit'in düz metin mesajı artık komut sayılmaz. İçinde `$(…)`, ters tırnak ya da `\` olan mesaj ve başka komutla zincirlenen commit yine sorulur; gerçek `--skip` çağrısı her yazımda sorulmaya devam eder.
16
+ - **Codex ve Antigravity: `commit-gate.mjs` ile `--skip` arasına başka argüman girince de soruluyor** (`commit-gate.mjs -m '--skip' x` gibi). Önceden bu yazım onaysız geçiyordu; Claude Code'daki kapı zaten soruyordu.
17
+
6
18
  ## [3.31.0] - 2026-10-26
7
19
 
8
20
  ### Yeni
package/kit/VERSION CHANGED
@@ -1 +1 @@
1
- 3.31.0
1
+ 3.31.2
package/kit/guide.html CHANGED
@@ -287,7 +287,7 @@ footer .container { display: flex; flex-wrap: wrap; gap: 16px; justify-content:
287
287
  <section class="hero">
288
288
  <div class="container">
289
289
  <div>
290
- <span class="pill"><b>v3.31.0</b> Claude Code için başlangıç kiti · MIT</span>
290
+ <span class="pill"><b>v3.31.2</b> Claude Code için başlangıç kiti · MIT</span>
291
291
  <h1>Kodu AI yazar.<br><em>Dümen sende.</em></h1>
292
292
  <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>
293
293
  <div class="install"><span id="cmd">npx tillerhand init</span><button class="copy" type="button" data-copy="cmd">Kopyala</button></div>
@@ -1055,7 +1055,7 @@ Sıradaki: G-013 şifre sıfırlama · bekleyen karar: e-posta servisi</pre>
1055
1055
  <footer>
1056
1056
  <div class="container">
1057
1057
  <a class="brand" href="#top" style="font-size:16px"><svg class="mark" style="width:22px;height:22px"><use href="#mark"/></svg>TillerHand</a>
1058
- <span>Sürüm 3.31.0 · <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>
1058
+ <span>Sürüm 3.31.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>
1059
1059
  </div>
1060
1060
  </footer>
1061
1061
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tillerhand",
3
- "version": "3.31.0",
3
+ "version": "3.31.2",
4
4
  "description": "Claude Code, Codex ve Antigravity için Türkçe vibe coding kiti: kurallar, komutlar, korumalar ve proje şablonları.",
5
5
  "license": "MIT",
6
6
  "type": "module",