tillerhand 3.27.0 → 3.27.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.
@@ -8,8 +8,8 @@
8
8
  import fs from 'node:fs';
9
9
  import path from 'node:path';
10
10
  import { execFileSync, spawnSync } from 'node:child_process';
11
- import { pathToFileURL } from 'node:url';
12
11
  import { isTestFile } from './test-guard.mjs';
12
+ import { isMain } from './shared.mjs';
13
13
 
14
14
  export const BUDGET = { complexity: 15, function: 80, file: 400 };
15
15
  const LIZARD = ['pipx', 'run', 'lizard==1.24.1'];
@@ -58,7 +58,7 @@ function check() {
58
58
  console.log(`Karmaşıklık bütçesi uygun: ${files.length} dosya.`);
59
59
  }
60
60
 
61
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
61
+ if (isMain(import.meta.url)) {
62
62
  if (process.argv.includes('--check')) check();
63
63
  else { console.error('Kullanım: node .claude/hooks/budget.mjs --check [--base <commit>]'); process.exit(1); }
64
64
  }
@@ -6,7 +6,7 @@
6
6
  // Rollback does not delete history: unsaved work is stashed first, files are set to the target state and
7
7
  // a NEW commit is made. So a rollback can be undone too. reset --hard is not used.
8
8
  import { execFileSync } from 'node:child_process';
9
- import { pathToFileURL } from 'node:url';
9
+ import { isMain } from './shared.mjs';
10
10
 
11
11
  // Tag prefix in users' repos: it stays as it is, existing tags must keep matching
12
12
  const TAG_PREFIX = 'calisan/';
@@ -47,7 +47,7 @@ export function restore(root, target) {
47
47
  return { changed: true, stashed, target: short, newCommit: git(root, 'rev-parse', '--short', 'HEAD') };
48
48
  }
49
49
 
50
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
50
+ if (isMain(import.meta.url)) {
51
51
  const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
52
52
  const arg = process.argv.slice(2);
53
53
  // ONLY the first argument sets the mode: the permission list matches by prefix ("--list:*", "--save:*"),
@@ -8,8 +8,7 @@ import fs from 'node:fs';
8
8
  import path from 'node:path';
9
9
  import crypto from 'node:crypto';
10
10
  import { execSync } from 'node:child_process';
11
- import { fileURLToPath } from 'node:url';
12
- import { findRoot } from './shared.mjs';
11
+ import { findRoot, isMain } from './shared.mjs';
13
12
  import { unquote } from './destructive-guard.mjs';
14
13
  import { approvedCommands, commandList } from './gate.mjs'; // circular import: used at run time only
15
14
 
@@ -161,7 +160,7 @@ function hook() {
161
160
  process.exit(2);
162
161
  }
163
162
 
164
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
163
+ if (isMain(import.meta.url)) {
165
164
  const i = process.argv.indexOf('--approve');
166
165
  if (i > -1) approve(process.argv[i + 1] || 'belirtilmedi');
167
166
  else hook();
@@ -4,8 +4,8 @@
4
4
  // node .claude/hooks/council.mjs docs/workflow/council/<tutanak>.md
5
5
  // The tally is done in code: a model cannot add up the ranking wrong, the chair cannot change the count.
6
6
  import fs from 'node:fs';
7
- import { pathToFileURL } from 'node:url';
8
7
  import { sections, findSection } from './status-read.mjs';
8
+ import { isMain } from './shared.mjs';
9
9
 
10
10
  const VOTE = /^\s*[-*]?\s*([A-Z]\d+)\s*[:=]\s*(DOĞRU|YANLIŞ|ABARTILI)(?!\p{L})/gimu; // \b does not work on Turkish letters (Ş)
11
11
 
@@ -99,7 +99,7 @@ export function writeMinutes(minutes, s) {
99
99
  return `${minutes.trimEnd()}\n\n${section}`;
100
100
  }
101
101
 
102
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
102
+ if (isMain(import.meta.url)) {
103
103
  const file = process.argv[2];
104
104
  if (!file || !fs.existsSync(file)) { console.error('Kullanım: node .claude/hooks/council.mjs <tutanak.md>'); process.exit(1); }
105
105
  const text = fs.readFileSync(file, 'utf8');
@@ -7,8 +7,7 @@
7
7
  import fs from 'node:fs';
8
8
  import os from 'node:os';
9
9
  import path from 'node:path';
10
- import { pathToFileURL } from 'node:url';
11
- import { readInput, decide } from './shared.mjs';
10
+ import { readInput, decide, isMain } from './shared.mjs';
12
11
  import { isTestFile, isTestConfig } from './test-guard.mjs';
13
12
  import { readLock, readPlan, allowedInLock, inScope, realPath, relPath, protectedRule } from './freeze-guard.mjs';
14
13
 
@@ -319,7 +318,7 @@ function evaluateOne(command) {
319
318
  return null;
320
319
  }
321
320
 
322
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
321
+ if (isMain(import.meta.url)) {
323
322
  const g = readInput();
324
323
  // Of the lists, the stricter wins (deny > ask)
325
324
  const results = g ? [evaluate(g.command), g.tool !== 'claude' ? permissionEquivalent(g.command) : null, scopeIssue(g.command, g.dir, g.root)].filter(Boolean) : [];
@@ -6,8 +6,7 @@
6
6
  // State: .claude/state/edit-rounds.json → { session, counts: { file: n }, pending: [note] }; reset when the session changes.
7
7
  import fs from 'node:fs';
8
8
  import path from 'node:path';
9
- import { pathToFileURL } from 'node:url';
10
- import { readInput, findRoot } from './shared.mjs';
9
+ import { readInput, findRoot, isMain } from './shared.mjs';
11
10
 
12
11
  export const ROUND = 5;
13
12
  const CODE = /\.(m?[jt]sx?|cjs|py|go|rb|php|java|kt|swift|rs|sql|vue|svelte)$/i;
@@ -66,6 +65,6 @@ function inject() {
66
65
  process.exit(0);
67
66
  }
68
67
 
69
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
68
+ if (isMain(import.meta.url)) {
70
69
  process.argv.includes('--inject') ? inject() : hook();
71
70
  }
@@ -16,8 +16,7 @@
16
16
  // node .claude/hooks/freeze-guard.mjs --plan-end close when the task is done
17
17
  import fs from 'node:fs';
18
18
  import path from 'node:path';
19
- import { pathToFileURL } from 'node:url';
20
- import { readInput, decide, findRoot } from './shared.mjs';
19
+ import { readInput, decide, findRoot, isMain } from './shared.mjs';
21
20
 
22
21
  // Project root even when called from a subfolder: lock and plan are written where the hook reads them; paths are root-relative
23
22
  const root = findRoot();
@@ -157,7 +156,7 @@ function hook() {
157
156
  process.exit(0);
158
157
  }
159
158
 
160
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
159
+ if (isMain(import.meta.url)) {
161
160
  const [mode, value] = process.argv.slice(2);
162
161
  // A mistyped or removed flag must fail loudly: run as the hook it would pass silently and the AI would think the plan is saved
163
162
  if (mode?.startsWith('--') && !MODES.includes(mode)) {
@@ -14,10 +14,9 @@ import os from 'node:os';
14
14
  import path from 'node:path';
15
15
  import crypto from 'node:crypto';
16
16
  import { spawnSync } from 'node:child_process';
17
- import { pathToFileURL } from 'node:url';
18
17
  import { changes } from './commit-gate.mjs';
19
18
  import { isCodeFile } from './edit-rounds.mjs';
20
- import { findRoot } from './shared.mjs';
19
+ import { findRoot, isMain } from './shared.mjs';
21
20
  import { specInvariants } from './dashboard-data.mjs';
22
21
 
23
22
  // Keys of the AGENTS.md → Komutlar block (Turkish template text, matched as written)
@@ -139,7 +138,7 @@ export function runGate(root) {
139
138
  return { passed, text: [`## Kapı · ${files.length} dosya değişti`, ...checks.map(line), result].join('\n') };
140
139
  }
141
140
 
142
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
141
+ if (isMain(import.meta.url)) {
143
142
  const arg = process.argv[2];
144
143
  if (arg === '--approve') { console.log(approve(findRoot())); process.exit(0); }
145
144
  if (arg !== '--run') { console.error('Kullanım: node .claude/hooks/gate.mjs --run | --approve'); process.exit(1); }
@@ -5,8 +5,7 @@
5
5
  // 2) Git pre-commit: `node .claude/hooks/guard-secrets.mjs --staged` scans the staged changes.
6
6
  // High-confidence patterns only; no generic "password=" search, to avoid false alarms.
7
7
  import { execSync } from 'node:child_process';
8
- import { pathToFileURL } from 'node:url';
9
- import { readInput, decide } from './shared.mjs';
8
+ import { readInput, decide, isMain } from './shared.mjs';
10
9
 
11
10
  export const PATTERNS = [
12
11
  ['Anthropic/OpenAI anahtarı', /\bsk-(?:ant-|proj-)?[A-Za-z0-9_-]{32,}/],
@@ -59,6 +58,6 @@ function stagedMode() {
59
58
  }
60
59
 
61
60
  // Run only when called directly, not when tests import this file
62
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
61
+ if (isMain(import.meta.url)) {
63
62
  process.argv.includes('--staged') ? stagedMode() : hookMode();
64
63
  }
@@ -4,8 +4,8 @@
4
4
  // Package missing -> denied (deny)
5
5
  // Very new / few downloads / very close to a popular package / unverified -> the user is asked (ask)
6
6
  // The decision goes through Claude Code's permission UI; the AI cannot skip it.
7
- import { pathToFileURL } from 'node:url';
8
- import { readInput, decide } from './shared.mjs';
7
+
8
+ import { readInput, decide, isMain } from './shared.mjs';
9
9
 
10
10
  export const THRESHOLD = { newDays: 30, npmWeeklyDownloads: 500 };
11
11
 
@@ -128,4 +128,4 @@ async function hook() {
128
128
  process.exit(0);
129
129
  }
130
130
 
131
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) hook();
131
+ if (isMain(import.meta.url)) hook();
@@ -9,7 +9,7 @@
9
9
  import fs from 'node:fs';
10
10
  import path from 'node:path';
11
11
  import { execFileSync } from 'node:child_process';
12
- import { pathToFileURL } from 'node:url';
12
+ import { isMain } from './shared.mjs';
13
13
 
14
14
  // Windows paths are case-insensitive; other systems are case-sensitive
15
15
  const norm = (p) => (process.platform === 'win32' ? path.resolve(p).toLowerCase() : path.resolve(p));
@@ -33,7 +33,7 @@ export function disabledHere(wanted, installed, root) {
33
33
  .map(([id]) => id);
34
34
  }
35
35
 
36
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
36
+ if (isMain(import.meta.url)) {
37
37
  const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
38
38
  // No shell: arguments go straight to claude (claude is a single-file executable, .exe on Windows)
39
39
  const run = (...arg) => execFileSync('claude', arg, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 180000 });
@@ -9,9 +9,8 @@ import fs from 'node:fs';
9
9
  import os from 'node:os';
10
10
  import path from 'node:path';
11
11
  import { spawn, spawnSync } from 'node:child_process';
12
- import { pathToFileURL } from 'node:url';
13
12
  import { approvedCommands, approvalNeeded, isApproved } from './gate.mjs';
14
- import { findRoot } from './shared.mjs';
13
+ import { findRoot, isMain } from './shared.mjs';
15
14
 
16
15
  const CLI = 'npx -y @playwright/cli@0.1.22';
17
16
  const SIZES = [['desktop', 1440, 900], ['mobile', 390, 844]];
@@ -120,7 +119,7 @@ export async function preview(root, task) {
120
119
  }
121
120
  }
122
121
 
123
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
122
+ if (isMain(import.meta.url)) {
124
123
  const { code, text } = await preview(findRoot(), process.argv[2]);
125
124
  console.log(text);
126
125
  process.exit(code);
@@ -7,9 +7,8 @@
7
7
  // Exit code: 0 nothing failed, 1 a criterion failed, 2 nothing to report (no JUnit file, no PRD criterion, no test ran).
8
8
  import fs from 'node:fs';
9
9
  import path from 'node:path';
10
- import { pathToFileURL } from 'node:url';
11
10
  import { prdCriteria, junitCases } from './dashboard-data.mjs';
12
- import { findRoot } from './shared.mjs';
11
+ import { findRoot, isMain } from './shared.mjs';
13
12
 
14
13
  // A pipe would split the table cell (the dashboard's table reader does not know \|)
15
14
  const cell = (s) => String(s ?? '').split(/\r?\n/)[0].trim().slice(0, 160).replace(/\|/g, '/');
@@ -62,7 +61,7 @@ export function qaReport(root, junit = 'test-results-qa/junit.xml', now = new Da
62
61
  return { code: count('kaldı') ? 1 : 0, text: `QA: ${summary} → ${file}${stale}` };
63
62
  }
64
63
 
65
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
64
+ if (isMain(import.meta.url)) {
66
65
  const { code, text } = qaReport(findRoot(), process.argv[2]);
67
66
  console.log(text);
68
67
  process.exit(code);
@@ -11,6 +11,17 @@ import fs from 'node:fs';
11
11
  import path from 'node:path';
12
12
  import { fileURLToPath } from 'node:url';
13
13
 
14
+ // Was this file started as the program (not imported)? Compared as real paths: a project opened through a symlink or a
15
+ // junction (macOS /tmp, linked folders, npx's node_modules/.bin) starts the script by its linked path, while
16
+ // import.meta.url is the target. A plain comparison would skip the guard silently there.
17
+ export function isMain(metaUrl) {
18
+ try {
19
+ return Boolean(process.argv[1]) && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(metaUrl));
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+
14
25
  const AG_WRITE = ['write_to_file', 'replace_file_content', 'multi_replace_file_content'];
15
26
 
16
27
  // Claude Code passes CLAUDE_PROJECT_DIR to hooks; Codex does not. Codex has no "ask", so a misdetection stays on the safe side.
@@ -7,8 +7,7 @@
7
7
  import fs from 'node:fs';
8
8
  import path from 'node:path';
9
9
  import { execFileSync } from 'node:child_process';
10
- import { pathToFileURL } from 'node:url';
11
- import { readInput, decide } from './shared.mjs';
10
+ import { readInput, decide, isMain } from './shared.mjs';
12
11
 
13
12
  const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
14
13
 
@@ -80,6 +79,6 @@ function checkAll() {
80
79
  process.exit(1);
81
80
  }
82
81
 
83
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
82
+ if (isMain(import.meta.url)) {
84
83
  process.argv.includes('--check') ? checkAll() : hook();
85
84
  }
@@ -8,8 +8,7 @@
8
8
  // It does not block: the user approves a justified change.
9
9
  import fs from 'node:fs';
10
10
  import path from 'node:path';
11
- import { pathToFileURL } from 'node:url';
12
- import { readInput, decide } from './shared.mjs';
11
+ import { readInput, decide, isMain } from './shared.mjs';
13
12
 
14
13
  const TEST = /(^|\/)(tests?|__tests__|e2e|spec)\/|\.(test|spec|e2e-spec)\.[cm]?[jt]sx?$|(^|\/)test_[^/]*\.py$|_test\.(py|go)$/i;
15
14
  export const isTestFile = (file) => TEST.test(file.replace(/\\/g, '/'));
@@ -77,4 +76,4 @@ function hook() {
77
76
  process.exit(0);
78
77
  }
79
78
 
80
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) hook();
79
+ if (isMain(import.meta.url)) hook();
@@ -11,8 +11,8 @@
11
11
  import fs from 'node:fs';
12
12
  import os from 'node:os';
13
13
  import path from 'node:path';
14
- import { pathToFileURL } from 'node:url';
15
14
  import { missingPlugins } from './plugins-sync.mjs';
15
+ import { isMain } from './shared.mjs';
16
16
 
17
17
  // Keys are the package names users type (/tools ac tasarim): they stay Turkish
18
18
  export const PACKAGES = {
@@ -142,7 +142,7 @@ const tokenOf = (measurement, ids) => {
142
142
  return found.length ? `~${found.reduce((s, o) => s + o.skillTokens + o.agentTokens, 0)} token` : '? token (kurulu değil)';
143
143
  };
144
144
  const NOTE = 'Açtıktan sonra /reload-plugins. Oturum ortasında açmak prompt önbelleğini bozar; en iyisi faz sınırında, /clear ile.';
145
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
145
+ if (isMain(import.meta.url)) {
146
146
  const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
147
147
  const [flag, name] = process.argv.slice(2);
148
148
  try {
@@ -6,7 +6,7 @@
6
6
  // node .claude/hooks/usage.mjs --json machine-readable
7
7
  import fs from 'node:fs';
8
8
  import path from 'node:path';
9
- import { fileURLToPath } from 'node:url';
9
+ import { isMain } from './shared.mjs';
10
10
 
11
11
  export const THRESHOLDS = { five_hour: { yellow: 60, red: 85 }, seven_day: { yellow: 70, red: 90 } };
12
12
  const WINDOWS = { five_hour: '5 saatlik', seven_day: 'haftalık' };
@@ -82,7 +82,7 @@ export function summary(data, now = Date.now()) {
82
82
  return `${parts.join(' · ')} · seviye: ${LEVEL_LABEL[level(data)]} · ${data.ageMin} dk önce okundu${stale}`;
83
83
  }
84
84
 
85
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
85
+ if (isMain(import.meta.url)) {
86
86
  const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
87
87
  const data = readLimits(root);
88
88
  if (process.argv.includes('--json')) console.log(JSON.stringify({ ...data, level: level(data), thresholds: THRESHOLDS }));
package/README.en.md CHANGED
@@ -56,7 +56,7 @@ You don't need to know how to code. Claude handles git, the terminal and install
56
56
 
57
57
  **2. Create your project**
58
58
  1. Make a folder for your project and select it in the **Code** tab of the Claude desktop app.
59
- 2. Type: `clone github.com/burakdogrul/TillerHand into this folder as vibe-kit, then run node vibe-kit/kit/kit.mjs init`.
59
+ 2. Type: `run npx tillerhand init in this folder`.
60
60
  3. **Start a new session** in the same folder. The kit's commands and guardrails load in the new session.
61
61
 
62
62
  **3. Start**
@@ -87,14 +87,20 @@ Claude asks its questions one by one. **The first option is always the recommend
87
87
 
88
88
  ```bash
89
89
  mkdir my-app && cd my-app
90
+ npx tillerhand init
91
+ claude
92
+ ```
93
+
94
+ With git instead of npm (the kit stays in a `vibe-kit/` folder inside the project and goes into `.gitignore`):
95
+
96
+ ```bash
90
97
  git clone https://github.com/burakdogrul/TillerHand.git vibe-kit
91
98
  node vibe-kit/kit/kit.mjs init
92
- claude
93
99
  ```
94
100
 
95
101
  - On first launch Claude Code offers to install the kit's skill plugins; accept.
96
102
  - Kit in another folder: `node <kit>/kit/kit.mjs init --project <project>`.
97
- - For Codex or Antigravity: `node vibe-kit/kit/kit.mjs init --tool codex` (several: `--tool claude,codex`).
103
+ - For Codex or Antigravity: `npx tillerhand init --tool codex` (several: `--tool claude,codex`).
98
104
  - Recommended: a code map ([codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)). The AI queries the code instead of reading it file by file, which saves tokens. `/start` offers to install it.
99
105
 
100
106
  ## How it works
package/README.md CHANGED
@@ -54,7 +54,7 @@ Kod yazmayı bilmen gerekmez. Git, terminal ve kurulum işlerini Claude yapar; s
54
54
 
55
55
  **2. Projeni oluştur**
56
56
  1. Projen için bir klasör aç ve Claude masaüstü uygulamasında **Code** sekmesinde seç.
57
- 2. Şunu yaz: `github.com/burakdogrul/TillerHand reposunu bu klasörün içine vibe-kit adıyla klonla, sonra node vibe-kit/kit/kit.mjs init komutunu çalıştır`.
57
+ 2. Şunu yaz: `bu klasörde npx tillerhand init komutunu çalıştır`.
58
58
  3. Aynı klasörde **yeni oturum aç**. Kitin komutları ve korumaları yeni oturumda yüklenir.
59
59
 
60
60
  **3. Başlat**
@@ -85,14 +85,20 @@ Claude sırayla soru sorar. **Her sorunun ilk seçeneği önerilendir**; emin de
85
85
 
86
86
  ```bash
87
87
  mkdir yeni-proje && cd yeni-proje
88
+ npx tillerhand init
89
+ claude
90
+ ```
91
+
92
+ npm yerine git ile (kit, proje içinde `vibe-kit/` klasöründe durur ve `.gitignore`'a girer):
93
+
94
+ ```bash
88
95
  git clone https://github.com/burakdogrul/TillerHand.git vibe-kit
89
96
  node vibe-kit/kit/kit.mjs init
90
- claude
91
97
  ```
92
98
 
93
99
  - Açılışta Claude Code, kitin skill eklentilerini kurmayı önerir; kabul et.
94
100
  - Kit başka bir klasördeyse: `node <kit>/kit/kit.mjs init --project <proje>`.
95
- - Codex veya Antigravity için: `node vibe-kit/kit/kit.mjs init --tool codex` (birden fazlası: `--tool claude,codex`).
101
+ - Codex veya Antigravity için: `npx tillerhand init --tool codex` (birden fazlası: `--tool claude,codex`).
96
102
  - Önerilen: kod haritası ([codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)). AI kodu dosya dosya okumak yerine sorgular, token tasarrufu sağlar. `/start` kurmayı sorar.
97
103
 
98
104
  ## Nasıl çalışır
package/kit/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
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.27.2] - 2026-10-25
7
+
8
+ ### Belgeler
9
+ - **README ve rehberde npm ile kurulum:** `npx tillerhand init` önerilen yol; git klonu seçenek olarak duruyor. Codex/Antigravity için `npx tillerhand init --tool codex`.
10
+ - **Rehber:** Üstteki "Hemen başla" ve "Nasıl çalışır?" düğmeleri kaldırıldı. Kurulum komutları ve terminal blokları (durum bloğu, alt durum çubuğu) dar ekranda yana kaymadan alt satıra iner.
11
+ - Paket Node.js 18 ve üstünü kabul eder (belgedeki gereksinimle aynı).
12
+
13
+ ## [3.27.1] - 2026-10-25
14
+
15
+ ### Düzeltilen
16
+ - **Koruma hook'ları, proje sembolik bağlantı veya junction üzerinden açılınca sessizce çalışmıyordu.** Sır engeli, yıkıcı komut kalkanı, commit kapısı, yapı ve test korumaları dahil 17 betik, başlatıldıkları yolu gerçek yolla karşılaştırırken bağlantıyı çözmüyordu; macOS'ta `/tmp` gibi bağlantılı klasörlerde veya bağlantılı proje klasörlerinde hiçbir şey yapmadan çıkıyorlardı. Artık iki yol da gerçek yola çevrilip karşılaştırılıyor (`shared.mjs` → `isMain`).
17
+ - **`npx tillerhand init` Linux ve macOS'ta hiçbir şey yapmıyordu** (aynı neden: npx kiti `node_modules/.bin` bağlantısıyla başlatıyor). Windows'ta çalışıyordu. 3.27.0'ı Linux veya macOS'ta denediysen tekrar çalıştır.
18
+ - Elle uyarla: yok; `/kit-update` hook'ları yeniler.
19
+
6
20
  ## [3.27.0] - 2026-10-25
7
21
 
8
22
  ### Yeni
package/kit/VERSION CHANGED
@@ -1 +1 @@
1
- 3.27.0
1
+ 3.27.2
package/kit/guide.html CHANGED
@@ -90,9 +90,9 @@ p { margin: 0 0 1em; }
90
90
  .hero h1 { font-size: clamp(40px, 5.6vw, 64px); font-weight: 800; letter-spacing: -.035em; line-height: 1.04; margin-bottom: 22px; }
91
91
  .hero h1 em { font-style: normal; color: var(--accent); }
92
92
  .hero .sub { font-size: 19px; color: var(--fg-2); max-width: 34em; margin-bottom: 30px; }
93
- .cta { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 30px; }
94
93
  .install { display: flex; align-items: center; gap: 10px; max-width: 520px; background: var(--term-bg); color: var(--term-fg); border-radius: 12px; padding: 12px 12px 12px 18px; font: 14px var(--mono); }
95
- .install span { flex: 1; overflow-x: auto; white-space: nowrap; }
94
+ .install + .install { margin-top: 10px; }
95
+ .install span { flex: 1; min-width: 0; overflow-wrap: anywhere; }
96
96
  .install span::before { content: "$ "; color: var(--term-dim); }
97
97
  .copy { flex: none; background: rgba(255,255,255,.08); color: var(--term-fg); border: 1px solid rgba(255,255,255,.14); border-radius: 8px; font: 500 12.5px var(--font); padding: 6px 10px; cursor: pointer; }
98
98
  .copy:hover { background: rgba(255,255,255,.16); }
@@ -158,7 +158,7 @@ p { margin: 0 0 1em; }
158
158
  .moments { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
159
159
  .moment h3 { font-size: 17px; margin: 18px 0 6px; }
160
160
  .moment > p { color: var(--muted); font-size: 15px; margin: 0; }
161
- .term { background: var(--term-bg); color: var(--term-fg); font: 13px/1.7 var(--mono); padding: 18px 20px; border-radius: 12px; overflow-x: auto; white-space: pre; margin: 0 0 1.2em; }
161
+ .term { background: var(--term-bg); color: var(--term-fg); font: 13px/1.7 var(--mono); padding: 18px 20px; border-radius: 12px; white-space: pre-wrap; overflow-wrap: anywhere; margin: 0 0 1.2em; }
162
162
  .term .c { color: var(--term-dim); }
163
163
  .term .r { color: #ff8a80; font-weight: 600; }
164
164
  .term .g { color: #7ee2a8; }
@@ -287,14 +287,11 @@ 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.26.0</b> Claude Code için başlangıç kiti · MIT</span>
290
+ <span class="pill"><b>v3.27.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
- <div class="cta">
294
- <a class="btn primary" href="#baslangic">Hemen başla</a>
295
- <a class="btn ghost" href="#nasil">Nasıl çalışır?</a>
296
- </div>
297
- <div class="install"><span id="cmd">git clone https://github.com/burakdogrul/TillerHand.git vibe-kit</span><button class="copy" type="button" data-copy="cmd">Kopyala</button></div>
293
+ <div class="install"><span id="cmd">npx tillerhand init</span><button class="copy" type="button" data-copy="cmd">Kopyala</button></div>
294
+ <div class="install"><span id="cmd-git">git clone https://github.com/burakdogrul/TillerHand.git vibe-kit</span><button class="copy" type="button" data-copy="cmd-git">Kopyala</button></div>
298
295
  <p class="hero-note">Claude Code için yazıldı · Codex ve Antigravity'de de çalışır · Kod bilmen gerekmez</p>
299
296
  </div>
300
297
  <div class="window" aria-label="Örnek: AI'nın sana sorduğu bir karar sorusu">
@@ -527,11 +524,13 @@ Bekleyen karar: e-posta servisi</pre>
527
524
  <h2>Kurulum</h2>
528
525
  <p class="lead">TillerHand ve projen ayrı klasörlerde durur. <code>init</code> gereken dosyaları (<code>.claude/</code>, <code>CLAUDE.md</code>, <code>AGENTS.md</code>, <code>docs/</code>) proje klasörüne kopyalar, git deposu açar ve kit klasörüne dokunmaz. Proje tipi kurulumda değil, <code>/start</code> sırasında soruyla seçilir.</p>
529
526
  <p><b>Gereksinimler:</b> <a href="https://claude.com/claude-code">Claude Code</a>, Git, Node.js 18+.</p>
530
- <pre class="term"><span class="c"># 1. Proje klasörü ve içinde kit</span>
527
+ <pre class="term"><span class="c"># 1. Proje klasöründe kiti kur (npm ile, önerilen)</span>
531
528
  mkdir yeni-proje &amp;&amp; cd yeni-proje
532
- git clone https://github.com/burakdogrul/TillerHand.git vibe-kit
533
- node vibe-kit/kit/kit.mjs init <span class="c"># vibe-kit/ .gitignore'a girer</span>
529
+ npx tillerhand init
534
530
  claude
531
+ <span class="c"># ya da git ile: kit proje içinde vibe-kit/ klasöründe durur (.gitignore'a girer)</span>
532
+ git clone https://github.com/burakdogrul/TillerHand.git vibe-kit
533
+ node vibe-kit/kit/kit.mjs init
535
534
  <span class="c"># 2. Claude Code içinde (yeni oturum)</span>
536
535
  /start Serbest çalışanlar için fatura ve tahsilat takip uygulaması
537
536
  <span class="c"># var olan projede /start yerine:</span>
@@ -549,7 +548,7 @@ claude
549
548
  <ol>
550
549
  <li><b>Hesaplar (bir kez):</b> <a href="https://github.com">GitHub</a> hesabı (ücretsiz) ve Claude <b>Pro</b> veya <b>Max</b> aboneliği.</li>
551
550
  <li><b>Programlar (bir kez):</b> <a href="https://claude.com/download">Claude masaüstü uygulaması</a>, <a href="https://nodejs.org">Node.js</a> (LTS sürümü), <a href="https://git-scm.com/downloads">Git</a>. İstersen <a href="https://desktop.github.com">GitHub Desktop</a> da kurabilirsin.</li>
552
- <li><b>Proje:</b> Projen için bir klasör aç ve Claude masaüstü uygulamasında <b>Code</b> sekmesinde seç. Claude'a yaz: "github.com/burakdogrul/TillerHand reposunu bu klasörün içine vibe-kit adıyla klonla, sonra <code>node vibe-kit/kit/kit.mjs init</code> çalıştır".</li>
551
+ <li><b>Proje:</b> Projen için bir klasör aç ve Claude masaüstü uygulamasında <b>Code</b> sekmesinde seç. Claude'a yaz: "bu klasörde <code>npx tillerhand init</code> komutunu çalıştır".</li>
553
552
  <li><b>Aç:</b> Aynı klasörde <b>yeni oturum</b> aç; kitin komutları ve korumaları yeni oturumda yüklenir. Eklentileri kurmayı önerirse kabul et.</li>
554
553
  <li><b>Başlat:</b> <code>/start fikrin</code> yaz. <b>Her sorunun ilk seçeneği önerilendir</b>; emin değilsen onu seç. Pro'daysan ilk sorular için öneri: profil <b>Hafif</b>, planlama derinliği <b>Hızlı</b> (5 saatlik limitin ~%25-35'i), pano <b>Evet</b>, anlatıcı mod <b>Evet</b>.</li>
555
554
  </ol>
@@ -953,7 +952,7 @@ Sıradaki: G-013 şifre sıfırlama · bekleyen karar: e-posta servisi</pre>
953
952
  <p class="path">Başka araçla</p>
954
953
  <h2>Codex ve Antigravity</h2>
955
954
  <p class="lead">TillerHand Claude Code için yazıldı. Kurallar, komutlar ve korumalar OpenAI Codex ve Google Antigravity'de de çalışır. Kurul tek modelle yürür; soru arayüzü ve durum çubuğu bu araçlarda yok.</p>
956
- <pre class="term">node vibe-kit/kit/kit.mjs init --tool codex <span class="c"># ya da antigravity, ya da claude,codex</span>
955
+ <pre class="term">npx tillerhand init --tool codex <span class="c"># ya da antigravity, ya da claude,codex</span>
957
956
  <span class="c"># var olan projeye araç eklemek</span>
958
957
  /kit-update --tool claude,codex</pre>
959
958
  <div class="table-wrap"><table>
package/kit/kit.mjs CHANGED
@@ -474,7 +474,9 @@ async function askTools() {
474
474
  return c.split(',').map((s) => TOOLS[Number(s.trim()) - 1] ?? s.trim().toLowerCase()).filter(Boolean);
475
475
  }
476
476
 
477
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
477
+ // Real paths: npx starts the CLI through a link (node_modules/.bin/tillerhand), while import.meta.url is the target
478
+ const realArg = () => { try { return fs.realpathSync(process.argv[1]); } catch { return ''; } };
479
+ if (process.argv[1] && realArg() === fs.realpathSync(fileURLToPath(import.meta.url))) {
478
480
  const [command, ...rest] = process.argv.slice(2);
479
481
  try {
480
482
  const a = parseArgs(rest);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tillerhand",
3
- "version": "3.27.0",
3
+ "version": "3.27.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",
@@ -22,7 +22,7 @@
22
22
  "CLAUDE.md"
23
23
  ],
24
24
  "engines": {
25
- "node": ">=22"
25
+ "node": ">=18"
26
26
  },
27
27
  "repository": {
28
28
  "type": "git",