Sign In

tokenu

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tokenu - npm Package Compare versions

Comparing version
1.0.1
to
1.1.0
+385
dist/main-COYhytHE.cjs
"use strict";
//#region rolldown:runtime
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
const node_path = __toESM(require("node:path"));
const node_fs_promises = __toESM(require("node:fs/promises"));
//#region src/tokenizer.ts
const VALID_ENCODINGS = new Set([
"o200k_base",
"o200k_harmony",
"cl100k_base",
"p50k_base",
"p50k_edit",
"r50k_base"
]);
function resolveEncoding(encoding, model) {
if (encoding && model) {
throw new Error("Cannot specify both --encoding and --model");
}
if (encoding) {
if (!VALID_ENCODINGS.has(encoding)) {
throw new Error(`Unknown encoding: ${encoding}. Valid encodings: ${[...VALID_ENCODINGS].join(", ")}`);
}
return encoding;
}
return "o200k_base";
}
async function getTokenCounter(encoding, model) {
if (model) {
if (encoding) {
throw new Error("Cannot specify both --encoding and --model");
}
const mod$1 = await import(`gpt-tokenizer/model/${model}`);
return mod$1.countTokens;
}
const resolvedEncoding = resolveEncoding(encoding);
if (resolvedEncoding === "o200k_base") {
const { countTokens } = await import("gpt-tokenizer");
return countTokens;
}
const mod = await import(`gpt-tokenizer/encoding/${resolvedEncoding}`);
return mod.countTokens;
}
//#endregion
//#region src/walker.ts
const DEFAULT_IGNORE = new Set(["node_modules", ".git"]);
const BINARY_CHECK_BYTES = 8192;
const GITIGNORE_FILE = ".gitignore";
function isBinaryBuffer(buffer) {
const length = Math.min(buffer.length, BINARY_CHECK_BYTES);
for (let i = 0; i < length; i++) {
if (buffer[i] === 0) return true;
}
return false;
}
function matchesExclude(name, excludePatterns) {
for (const pattern of excludePatterns) {
if (pattern === name) return true;
if (pattern.startsWith("*.") && name.endsWith(pattern.slice(1))) return true;
if (pattern.endsWith("/*") && name === pattern.slice(0, -2)) return true;
}
return false;
}
function toPosixPath(value) {
return node_path.sep === "/" ? value : value.split(node_path.sep).join("/");
}
function parseGitIgnoreLine(rawLine) {
let pattern = rawLine.trim();
if (pattern === "" || pattern.startsWith("#")) return undefined;
if (pattern.startsWith("\\#") || pattern.startsWith("\\!")) {
pattern = pattern.slice(1);
}
let negated = false;
if (pattern.startsWith("!")) {
negated = true;
pattern = pattern.slice(1);
}
pattern = pattern.trim();
if (pattern === "") return undefined;
const anchored = pattern.startsWith("/");
pattern = pattern.replace(/^\/+/, "");
const directoryOnly = pattern.endsWith("/");
pattern = pattern.replace(/\/+$/, "");
if (pattern === "") return undefined;
return {
pattern,
negated,
directoryOnly,
anchored,
hasSlash: pattern.includes("/")
};
}
function parseGitIgnore(content) {
const rules = [];
for (const line of content.split(/\r?\n/)) {
const rule = parseGitIgnoreLine(line);
if (rule) rules.push(rule);
}
return rules;
}
async function loadGitIgnoreContext(targetPath) {
try {
const content = await (0, node_fs_promises.readFile)((0, node_path.join)(targetPath, GITIGNORE_FILE), "utf-8");
const rules = parseGitIgnore(content);
return rules.length > 0 ? {
rootPath: targetPath,
rules
} : undefined;
} catch (err) {
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
return undefined;
}
throw err;
}
}
function matchesGitIgnorePattern(relativePath, basename, isDirectory, rule) {
if (rule.directoryOnly && !isDirectory) return false;
if (!rule.anchored && !rule.hasSlash) {
return (0, node_path.matchesGlob)(basename, rule.pattern);
}
if ((0, node_path.matchesGlob)(relativePath, rule.pattern)) return true;
if (isDirectory) {
return (0, node_path.matchesGlob)(`${relativePath}/`, `${rule.pattern}/**`);
}
return false;
}
function matchesGitIgnore(childPath, entryName, isDirectory, ignoreContexts) {
let ignored = false;
for (const context of ignoreContexts) {
const relativePath = toPosixPath((0, node_path.relative)(context.rootPath, childPath));
if (relativePath === "" || relativePath.startsWith("../")) continue;
for (const rule of context.rules) {
if (matchesGitIgnorePattern(relativePath, entryName, isDirectory, rule)) {
ignored = !rule.negated;
}
}
}
return ignored;
}
async function countFileTokens(filePath, countFn) {
const buffer = await (0, node_fs_promises.readFile)(filePath);
if (buffer.length === 0) return 0;
if (isBinaryBuffer(buffer)) return 0;
const text = buffer.toString("utf-8");
try {
return countFn(text);
} catch {
return 0;
}
}
async function walkPath(targetPath, basePath, options, currentDepth = 0, ignoreContexts = []) {
const info = await (0, node_fs_promises.stat)(targetPath);
if (info.isFile()) {
const tokens = await countFileTokens(targetPath, options.countFn);
return {
path: (0, node_path.relative)(basePath, targetPath) || targetPath,
tokens,
isFile: true
};
}
if (!info.isDirectory()) {
return {
path: (0, node_path.relative)(basePath, targetPath) || targetPath,
tokens: 0,
isFile: false
};
}
const entries = await (0, node_fs_promises.readdir)(targetPath, { withFileTypes: true });
const children = [];
let totalTokens = 0;
const smartIgnore = options.smartIgnore ?? true;
const currentIgnoreContexts = smartIgnore ? [...ignoreContexts, ...await loadGitIgnoreContext(targetPath).then((context) => context ? [context] : [])] : ignoreContexts;
for (const entry of entries) {
const childPath = (0, node_path.join)(targetPath, entry.name);
const isDirectory = entry.isDirectory();
if (smartIgnore && DEFAULT_IGNORE.has(entry.name)) continue;
if (smartIgnore && matchesGitIgnore(childPath, entry.name, isDirectory, currentIgnoreContexts)) continue;
if (matchesExclude(entry.name, options.exclude)) continue;
if (entry.isFile()) {
const tokens = await countFileTokens(childPath, options.countFn);
totalTokens += tokens;
children.push({
path: (0, node_path.relative)(basePath, childPath),
tokens,
isFile: true
});
} else if (entry.isDirectory()) {
if (options.maxDepth !== undefined && currentDepth >= options.maxDepth) {
continue;
}
const childResult = await walkPath(childPath, basePath, options, currentDepth + 1, currentIgnoreContexts);
totalTokens += childResult.tokens;
children.push(childResult);
}
}
children.sort((a, b) => a.path.localeCompare(b.path));
return {
path: (0, node_path.relative)(basePath, targetPath) || targetPath,
tokens: totalTokens,
isFile: false,
children
};
}
//#endregion
//#region src/formatter.ts
function humanReadableTokens(count) {
if (count >= 1e6) {
const value = count / 1e6;
return value % 1 === 0 ? `${value}M` : `${value.toFixed(1)}M`;
}
if (count >= 1e3) {
const value = count / 1e3;
return value % 1 === 0 ? `${value}K` : `${value.toFixed(1)}K`;
}
return count.toString();
}
function formatLine(tokens, path, humanReadable) {
const count = humanReadable ? humanReadableTokens(tokens) : tokens.toString();
return `${count}\t${path}`;
}
function collectTextLines(result, options, lines, depth = 0) {
if (result.isFile) {
if (options.all) {
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
return;
}
if (result.children) {
for (const child of result.children) {
if (options.maxDepth !== undefined && depth >= options.maxDepth) break;
collectTextLines(child, options, lines, depth + 1);
}
}
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
function formatText(results, options) {
const lines = [];
if (options.summarize) {
for (const result of results) {
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
} else {
for (const result of results) {
collectTextLines(result, options, lines);
}
}
if (options.total) {
const grandTotal = results.reduce((sum, r) => sum + r.tokens, 0);
lines.push(formatLine(grandTotal, "total", options.humanReadable));
}
return lines.join("\n");
}
function collectJsonEntries(result, entries) {
if (result.isFile) {
entries.push({
path: result.path,
tokens: result.tokens,
type: "file"
});
return;
}
if (result.children) {
for (const child of result.children) {
collectJsonEntries(child, entries);
}
}
entries.push({
path: result.path,
tokens: result.tokens,
type: "directory",
children: result.children?.map((c) => c.path)
});
}
function formatJson(results, options, version) {
const entries = [];
if (options.summarize) {
for (const result of results) {
entries.push({
path: result.path,
tokens: result.tokens,
type: result.isFile ? "file" : "directory"
});
}
} else {
for (const result of results) {
collectJsonEntries(result, entries);
}
}
const output = {
version,
encoding: options.encoding,
timestamp: new Date().toISOString(),
results: entries
};
if (options.total || options.summarize) {
output.total = results.reduce((sum, r) => sum + r.tokens, 0);
}
return JSON.stringify(output, null, 2);
}
//#endregion
//#region src/main.ts
async function walkAndCount(paths, options) {
const countFn = await getTokenCounter(options.encoding, options.model);
const results = [];
for (const targetPath of paths) {
const absPath = (0, node_path.resolve)(targetPath);
const result = await walkPath(absPath, (0, node_path.resolve)(absPath, ".."), {
countFn,
all: options.all,
maxDepth: options.maxDepth,
exclude: options.exclude,
smartIgnore: options.smartIgnore
});
results.push(result);
}
return results;
}
//#endregion
Object.defineProperty(exports, '__toESM', {
enumerable: true,
get: function () {
return __toESM;
}
});
Object.defineProperty(exports, 'countFileTokens', {
enumerable: true,
get: function () {
return countFileTokens;
}
});
Object.defineProperty(exports, 'formatJson', {
enumerable: true,
get: function () {
return formatJson;
}
});
Object.defineProperty(exports, 'formatText', {
enumerable: true,
get: function () {
return formatText;
}
});
Object.defineProperty(exports, 'getTokenCounter', {
enumerable: true,
get: function () {
return getTokenCounter;
}
});
Object.defineProperty(exports, 'resolveEncoding', {
enumerable: true,
get: function () {
return resolveEncoding;
}
});
Object.defineProperty(exports, 'walkAndCount', {
enumerable: true,
get: function () {
return walkAndCount;
}
});
import { join, matchesGlob, relative, resolve, sep } from "node:path";
import { readFile, readdir, stat } from "node:fs/promises";
//#region src/tokenizer.ts
const VALID_ENCODINGS = new Set([
"o200k_base",
"o200k_harmony",
"cl100k_base",
"p50k_base",
"p50k_edit",
"r50k_base"
]);
function resolveEncoding(encoding, model) {
if (encoding && model) {
throw new Error("Cannot specify both --encoding and --model");
}
if (encoding) {
if (!VALID_ENCODINGS.has(encoding)) {
throw new Error(`Unknown encoding: ${encoding}. Valid encodings: ${[...VALID_ENCODINGS].join(", ")}`);
}
return encoding;
}
return "o200k_base";
}
async function getTokenCounter(encoding, model) {
if (model) {
if (encoding) {
throw new Error("Cannot specify both --encoding and --model");
}
const mod$1 = await import(`gpt-tokenizer/model/${model}`);
return mod$1.countTokens;
}
const resolvedEncoding = resolveEncoding(encoding);
if (resolvedEncoding === "o200k_base") {
const { countTokens } = await import("gpt-tokenizer");
return countTokens;
}
const mod = await import(`gpt-tokenizer/encoding/${resolvedEncoding}`);
return mod.countTokens;
}
//#endregion
//#region src/walker.ts
const DEFAULT_IGNORE = new Set(["node_modules", ".git"]);
const BINARY_CHECK_BYTES = 8192;
const GITIGNORE_FILE = ".gitignore";
function isBinaryBuffer(buffer) {
const length = Math.min(buffer.length, BINARY_CHECK_BYTES);
for (let i = 0; i < length; i++) {
if (buffer[i] === 0) return true;
}
return false;
}
function matchesExclude(name, excludePatterns) {
for (const pattern of excludePatterns) {
if (pattern === name) return true;
if (pattern.startsWith("*.") && name.endsWith(pattern.slice(1))) return true;
if (pattern.endsWith("/*") && name === pattern.slice(0, -2)) return true;
}
return false;
}
function toPosixPath(value) {
return sep === "/" ? value : value.split(sep).join("/");
}
function parseGitIgnoreLine(rawLine) {
let pattern = rawLine.trim();
if (pattern === "" || pattern.startsWith("#")) return undefined;
if (pattern.startsWith("\\#") || pattern.startsWith("\\!")) {
pattern = pattern.slice(1);
}
let negated = false;
if (pattern.startsWith("!")) {
negated = true;
pattern = pattern.slice(1);
}
pattern = pattern.trim();
if (pattern === "") return undefined;
const anchored = pattern.startsWith("/");
pattern = pattern.replace(/^\/+/, "");
const directoryOnly = pattern.endsWith("/");
pattern = pattern.replace(/\/+$/, "");
if (pattern === "") return undefined;
return {
pattern,
negated,
directoryOnly,
anchored,
hasSlash: pattern.includes("/")
};
}
function parseGitIgnore(content) {
const rules = [];
for (const line of content.split(/\r?\n/)) {
const rule = parseGitIgnoreLine(line);
if (rule) rules.push(rule);
}
return rules;
}
async function loadGitIgnoreContext(targetPath) {
try {
const content = await readFile(join(targetPath, GITIGNORE_FILE), "utf-8");
const rules = parseGitIgnore(content);
return rules.length > 0 ? {
rootPath: targetPath,
rules
} : undefined;
} catch (err) {
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
return undefined;
}
throw err;
}
}
function matchesGitIgnorePattern(relativePath, basename, isDirectory, rule) {
if (rule.directoryOnly && !isDirectory) return false;
if (!rule.anchored && !rule.hasSlash) {
return matchesGlob(basename, rule.pattern);
}
if (matchesGlob(relativePath, rule.pattern)) return true;
if (isDirectory) {
return matchesGlob(`${relativePath}/`, `${rule.pattern}/**`);
}
return false;
}
function matchesGitIgnore(childPath, entryName, isDirectory, ignoreContexts) {
let ignored = false;
for (const context of ignoreContexts) {
const relativePath = toPosixPath(relative(context.rootPath, childPath));
if (relativePath === "" || relativePath.startsWith("../")) continue;
for (const rule of context.rules) {
if (matchesGitIgnorePattern(relativePath, entryName, isDirectory, rule)) {
ignored = !rule.negated;
}
}
}
return ignored;
}
async function countFileTokens(filePath, countFn) {
const buffer = await readFile(filePath);
if (buffer.length === 0) return 0;
if (isBinaryBuffer(buffer)) return 0;
const text = buffer.toString("utf-8");
try {
return countFn(text);
} catch {
return 0;
}
}
async function walkPath(targetPath, basePath, options, currentDepth = 0, ignoreContexts = []) {
const info = await stat(targetPath);
if (info.isFile()) {
const tokens = await countFileTokens(targetPath, options.countFn);
return {
path: relative(basePath, targetPath) || targetPath,
tokens,
isFile: true
};
}
if (!info.isDirectory()) {
return {
path: relative(basePath, targetPath) || targetPath,
tokens: 0,
isFile: false
};
}
const entries = await readdir(targetPath, { withFileTypes: true });
const children = [];
let totalTokens = 0;
const smartIgnore = options.smartIgnore ?? true;
const currentIgnoreContexts = smartIgnore ? [...ignoreContexts, ...await loadGitIgnoreContext(targetPath).then((context) => context ? [context] : [])] : ignoreContexts;
for (const entry of entries) {
const childPath = join(targetPath, entry.name);
const isDirectory = entry.isDirectory();
if (smartIgnore && DEFAULT_IGNORE.has(entry.name)) continue;
if (smartIgnore && matchesGitIgnore(childPath, entry.name, isDirectory, currentIgnoreContexts)) continue;
if (matchesExclude(entry.name, options.exclude)) continue;
if (entry.isFile()) {
const tokens = await countFileTokens(childPath, options.countFn);
totalTokens += tokens;
children.push({
path: relative(basePath, childPath),
tokens,
isFile: true
});
} else if (entry.isDirectory()) {
if (options.maxDepth !== undefined && currentDepth >= options.maxDepth) {
continue;
}
const childResult = await walkPath(childPath, basePath, options, currentDepth + 1, currentIgnoreContexts);
totalTokens += childResult.tokens;
children.push(childResult);
}
}
children.sort((a, b) => a.path.localeCompare(b.path));
return {
path: relative(basePath, targetPath) || targetPath,
tokens: totalTokens,
isFile: false,
children
};
}
//#endregion
//#region src/formatter.ts
function humanReadableTokens(count) {
if (count >= 1e6) {
const value = count / 1e6;
return value % 1 === 0 ? `${value}M` : `${value.toFixed(1)}M`;
}
if (count >= 1e3) {
const value = count / 1e3;
return value % 1 === 0 ? `${value}K` : `${value.toFixed(1)}K`;
}
return count.toString();
}
function formatLine(tokens, path, humanReadable) {
const count = humanReadable ? humanReadableTokens(tokens) : tokens.toString();
return `${count}\t${path}`;
}
function collectTextLines(result, options, lines, depth = 0) {
if (result.isFile) {
if (options.all) {
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
return;
}
if (result.children) {
for (const child of result.children) {
if (options.maxDepth !== undefined && depth >= options.maxDepth) break;
collectTextLines(child, options, lines, depth + 1);
}
}
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
function formatText(results, options) {
const lines = [];
if (options.summarize) {
for (const result of results) {
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
} else {
for (const result of results) {
collectTextLines(result, options, lines);
}
}
if (options.total) {
const grandTotal = results.reduce((sum, r) => sum + r.tokens, 0);
lines.push(formatLine(grandTotal, "total", options.humanReadable));
}
return lines.join("\n");
}
function collectJsonEntries(result, entries) {
if (result.isFile) {
entries.push({
path: result.path,
tokens: result.tokens,
type: "file"
});
return;
}
if (result.children) {
for (const child of result.children) {
collectJsonEntries(child, entries);
}
}
entries.push({
path: result.path,
tokens: result.tokens,
type: "directory",
children: result.children?.map((c) => c.path)
});
}
function formatJson(results, options, version) {
const entries = [];
if (options.summarize) {
for (const result of results) {
entries.push({
path: result.path,
tokens: result.tokens,
type: result.isFile ? "file" : "directory"
});
}
} else {
for (const result of results) {
collectJsonEntries(result, entries);
}
}
const output = {
version,
encoding: options.encoding,
timestamp: new Date().toISOString(),
results: entries
};
if (options.total || options.summarize) {
output.total = results.reduce((sum, r) => sum + r.tokens, 0);
}
return JSON.stringify(output, null, 2);
}
//#endregion
//#region src/main.ts
async function walkAndCount(paths, options) {
const countFn = await getTokenCounter(options.encoding, options.model);
const results = [];
for (const targetPath of paths) {
const absPath = resolve(targetPath);
const result = await walkPath(absPath, resolve(absPath, ".."), {
countFn,
all: options.all,
maxDepth: options.maxDepth,
exclude: options.exclude,
smartIgnore: options.smartIgnore
});
results.push(result);
}
return results;
}
//#endregion
export { countFileTokens, formatJson, formatText, getTokenCounter, resolveEncoding, walkAndCount };
//# sourceMappingURL=main-RYEeU4ax.mjs.map
{"version":3,"file":"main-RYEeU4ax.mjs","names":["encoding?: string","model?: string","buffer: Buffer","name: string","excludePatterns: string[]","value: string","rawLine: string","content: string","rules: GitIgnoreRule[]","targetPath: string","relativePath: string","basename: string","isDirectory: boolean","rule: GitIgnoreRule","childPath: string","entryName: string","ignoreContexts: IgnoreContext[]","filePath: string","countFn: TokenCountFn","basePath: string","options: WalkOptions","currentDepth: number","children: TokenResult[]","count: number","tokens: number","path: string","humanReadable: boolean","result: TokenResult","options: CliOptions","lines: string[]","depth: number","results: TokenResult[]","entries: JsonOutputEntry[]","version: string","output: JsonOutput","paths: string[]","options: Omit<CliOptions, 'paths' | 'json' | 'humanReadable' | 'total' | 'summarize'>","results: TokenResult[]"],"sources":["../src/tokenizer.ts","../src/walker.ts","../src/formatter.ts","../src/main.ts"],"sourcesContent":["import type { TokenCountFn } from './types.ts'\n\nconst VALID_ENCODINGS = new Set([\n 'o200k_base',\n 'o200k_harmony',\n 'cl100k_base',\n 'p50k_base',\n 'p50k_edit',\n 'r50k_base',\n])\n\nexport function resolveEncoding (encoding?: string, model?: string): string {\n if (encoding && model) {\n throw new Error('Cannot specify both --encoding and --model')\n }\n if (encoding) {\n if (!VALID_ENCODINGS.has(encoding)) {\n throw new Error(`Unknown encoding: ${encoding}. Valid encodings: ${[...VALID_ENCODINGS].join(', ')}`)\n }\n return encoding\n }\n return 'o200k_base'\n}\n\nexport async function getTokenCounter (encoding?: string, model?: string): Promise<TokenCountFn> {\n if (model) {\n if (encoding) {\n throw new Error('Cannot specify both --encoding and --model')\n }\n const mod = await import(`gpt-tokenizer/model/${model}`) as { countTokens: TokenCountFn }\n return mod.countTokens\n }\n\n const resolvedEncoding = resolveEncoding(encoding)\n if (resolvedEncoding === 'o200k_base') {\n const { countTokens } = await import('gpt-tokenizer')\n return countTokens\n }\n\n const mod = await import(`gpt-tokenizer/encoding/${resolvedEncoding}`) as { countTokens: TokenCountFn }\n return mod.countTokens\n}\n","import { readdir, readFile, stat } from 'node:fs/promises'\nimport { join, matchesGlob, relative, sep } from 'node:path'\nimport type { TokenCountFn, TokenResult } from './types.ts'\n\nconst DEFAULT_IGNORE = new Set([\n 'node_modules',\n '.git',\n])\n\nconst BINARY_CHECK_BYTES = 8192\nconst GITIGNORE_FILE = '.gitignore'\n\ninterface GitIgnoreRule {\n pattern: string\n negated: boolean\n directoryOnly: boolean\n anchored: boolean\n hasSlash: boolean\n}\n\ninterface IgnoreContext {\n rootPath: string\n rules: GitIgnoreRule[]\n}\n\nfunction isBinaryBuffer (buffer: Buffer): boolean {\n const length = Math.min(buffer.length, BINARY_CHECK_BYTES)\n for (let i = 0; i < length; i++) {\n if (buffer[i] === 0) return true\n }\n return false\n}\n\nfunction matchesExclude (name: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern === name) return true\n if (pattern.startsWith('*.') && name.endsWith(pattern.slice(1))) return true\n if (pattern.endsWith('/*') && name === pattern.slice(0, -2)) return true\n }\n return false\n}\n\nfunction toPosixPath (value: string): string {\n return sep === '/' ? value : value.split(sep).join('/')\n}\n\nfunction parseGitIgnoreLine (rawLine: string): GitIgnoreRule | undefined {\n let pattern = rawLine.trim()\n if (pattern === '' || pattern.startsWith('#')) return undefined\n\n if (pattern.startsWith('\\\\#') || pattern.startsWith('\\\\!')) {\n pattern = pattern.slice(1)\n }\n\n let negated = false\n if (pattern.startsWith('!')) {\n negated = true\n pattern = pattern.slice(1)\n }\n\n pattern = pattern.trim()\n if (pattern === '') return undefined\n\n const anchored = pattern.startsWith('/')\n pattern = pattern.replace(/^\\/+/, '')\n\n const directoryOnly = pattern.endsWith('/')\n pattern = pattern.replace(/\\/+$/, '')\n if (pattern === '') return undefined\n\n return {\n pattern,\n negated,\n directoryOnly,\n anchored,\n hasSlash: pattern.includes('/'),\n }\n}\n\nfunction parseGitIgnore (content: string): GitIgnoreRule[] {\n const rules: GitIgnoreRule[] = []\n for (const line of content.split(/\\r?\\n/)) {\n const rule = parseGitIgnoreLine(line)\n if (rule) rules.push(rule)\n }\n return rules\n}\n\nasync function loadGitIgnoreContext (targetPath: string): Promise<IgnoreContext | undefined> {\n try {\n const content = await readFile(join(targetPath, GITIGNORE_FILE), 'utf-8')\n const rules = parseGitIgnore(content)\n return rules.length > 0 ? { rootPath: targetPath, rules } : undefined\n } catch (err) {\n if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {\n return undefined\n }\n throw err\n }\n}\n\nfunction matchesGitIgnorePattern (\n relativePath: string,\n basename: string,\n isDirectory: boolean,\n rule: GitIgnoreRule\n): boolean {\n if (rule.directoryOnly && !isDirectory) return false\n\n if (!rule.anchored && !rule.hasSlash) {\n return matchesGlob(basename, rule.pattern)\n }\n\n if (matchesGlob(relativePath, rule.pattern)) return true\n\n if (isDirectory) {\n return matchesGlob(`${relativePath}/`, `${rule.pattern}/**`)\n }\n\n return false\n}\n\nfunction matchesGitIgnore (\n childPath: string,\n entryName: string,\n isDirectory: boolean,\n ignoreContexts: IgnoreContext[]\n): boolean {\n let ignored = false\n\n for (const context of ignoreContexts) {\n const relativePath = toPosixPath(relative(context.rootPath, childPath))\n if (relativePath === '' || relativePath.startsWith('../')) continue\n\n for (const rule of context.rules) {\n if (matchesGitIgnorePattern(relativePath, entryName, isDirectory, rule)) {\n ignored = !rule.negated\n }\n }\n }\n\n return ignored\n}\n\nexport async function countFileTokens (\n filePath: string,\n countFn: TokenCountFn\n): Promise<number> {\n const buffer = await readFile(filePath)\n if (buffer.length === 0) return 0\n if (isBinaryBuffer(buffer)) return 0\n const text = buffer.toString('utf-8')\n try {\n return countFn(text)\n } catch {\n return 0\n }\n}\n\nexport interface WalkOptions {\n countFn: TokenCountFn\n all: boolean\n maxDepth?: number\n exclude: string[]\n smartIgnore?: boolean\n}\n\nexport async function walkPath (\n targetPath: string,\n basePath: string,\n options: WalkOptions,\n currentDepth: number = 0,\n ignoreContexts: IgnoreContext[] = []\n): Promise<TokenResult> {\n const info = await stat(targetPath)\n\n if (info.isFile()) {\n const tokens = await countFileTokens(targetPath, options.countFn)\n return {\n path: relative(basePath, targetPath) || targetPath,\n tokens,\n isFile: true,\n }\n }\n\n if (!info.isDirectory()) {\n return { path: relative(basePath, targetPath) || targetPath, tokens: 0, isFile: false }\n }\n\n const entries = await readdir(targetPath, { withFileTypes: true })\n const children: TokenResult[] = []\n let totalTokens = 0\n const smartIgnore = options.smartIgnore ?? true\n const currentIgnoreContexts = smartIgnore\n ? [...ignoreContexts, ...await loadGitIgnoreContext(targetPath).then(context => context ? [context] : [])]\n : ignoreContexts\n\n for (const entry of entries) {\n const childPath = join(targetPath, entry.name)\n const isDirectory = entry.isDirectory()\n\n if (smartIgnore && DEFAULT_IGNORE.has(entry.name)) continue\n if (smartIgnore && matchesGitIgnore(childPath, entry.name, isDirectory, currentIgnoreContexts)) continue\n if (matchesExclude(entry.name, options.exclude)) continue\n\n if (entry.isFile()) {\n const tokens = await countFileTokens(childPath, options.countFn)\n totalTokens += tokens\n children.push({\n path: relative(basePath, childPath),\n tokens,\n isFile: true,\n })\n } else if (entry.isDirectory()) {\n if (options.maxDepth !== undefined && currentDepth >= options.maxDepth) {\n continue\n }\n const childResult = await walkPath(childPath, basePath, options, currentDepth + 1, currentIgnoreContexts)\n totalTokens += childResult.tokens\n children.push(childResult)\n }\n }\n\n children.sort((a, b) => a.path.localeCompare(b.path))\n\n return {\n path: relative(basePath, targetPath) || targetPath,\n tokens: totalTokens,\n isFile: false,\n children,\n }\n}\n","import type { CliOptions, JsonOutput, JsonOutputEntry, TokenResult } from './types.ts'\n\nfunction humanReadableTokens (count: number): string {\n if (count >= 1_000_000) {\n const value = count / 1_000_000\n return value % 1 === 0 ? `${value}M` : `${value.toFixed(1)}M`\n }\n if (count >= 1_000) {\n const value = count / 1_000\n return value % 1 === 0 ? `${value}K` : `${value.toFixed(1)}K`\n }\n return count.toString()\n}\n\nfunction formatLine (tokens: number, path: string, humanReadable: boolean): string {\n const count = humanReadable ? humanReadableTokens(tokens) : tokens.toString()\n return `${count}\\t${path}`\n}\n\nfunction collectTextLines (\n result: TokenResult,\n options: CliOptions,\n lines: string[],\n depth: number = 0\n): void {\n if (result.isFile) {\n if (options.all) {\n lines.push(formatLine(result.tokens, result.path, options.humanReadable))\n }\n return\n }\n\n if (result.children) {\n for (const child of result.children) {\n if (options.maxDepth !== undefined && depth >= options.maxDepth) break\n collectTextLines(child, options, lines, depth + 1)\n }\n }\n\n lines.push(formatLine(result.tokens, result.path, options.humanReadable))\n}\n\nexport function formatText (results: TokenResult[], options: CliOptions): string {\n const lines: string[] = []\n\n if (options.summarize) {\n for (const result of results) {\n lines.push(formatLine(result.tokens, result.path, options.humanReadable))\n }\n } else {\n for (const result of results) {\n collectTextLines(result, options, lines)\n }\n }\n\n if (options.total) {\n const grandTotal = results.reduce((sum, r) => sum + r.tokens, 0)\n lines.push(formatLine(grandTotal, 'total', options.humanReadable))\n }\n\n return lines.join('\\n')\n}\n\nfunction collectJsonEntries (result: TokenResult, entries: JsonOutputEntry[]): void {\n if (result.isFile) {\n entries.push({\n path: result.path,\n tokens: result.tokens,\n type: 'file',\n })\n return\n }\n\n if (result.children) {\n for (const child of result.children) {\n collectJsonEntries(child, entries)\n }\n }\n\n entries.push({\n path: result.path,\n tokens: result.tokens,\n type: 'directory',\n children: result.children?.map(c => c.path),\n })\n}\n\nexport function formatJson (\n results: TokenResult[],\n options: CliOptions,\n version: string\n): string {\n const entries: JsonOutputEntry[] = []\n\n if (options.summarize) {\n for (const result of results) {\n entries.push({\n path: result.path,\n tokens: result.tokens,\n type: result.isFile ? 'file' : 'directory',\n })\n }\n } else {\n for (const result of results) {\n collectJsonEntries(result, entries)\n }\n }\n\n const output: JsonOutput = {\n version,\n encoding: options.encoding,\n timestamp: new Date().toISOString(),\n results: entries,\n }\n\n if (options.total || options.summarize) {\n output.total = results.reduce((sum, r) => sum + r.tokens, 0)\n }\n\n return JSON.stringify(output, null, 2)\n}\n","import { resolve } from 'node:path'\nimport type { CliOptions, TokenResult } from './types.ts'\nimport { getTokenCounter } from './tokenizer.ts'\nimport { walkPath } from './walker.ts'\nexport { countFileTokens } from './walker.ts'\nexport { getTokenCounter, resolveEncoding } from './tokenizer.ts'\nexport { formatText, formatJson } from './formatter.ts'\nexport type { TokenResult, CliOptions, TokenCountFn, JsonOutput, JsonOutputEntry } from './types.ts'\n\nexport async function walkAndCount (\n paths: string[],\n options: Omit<CliOptions, 'paths' | 'json' | 'humanReadable' | 'total' | 'summarize'>\n): Promise<TokenResult[]> {\n const countFn = await getTokenCounter(options.encoding, options.model)\n const results: TokenResult[] = []\n\n for (const targetPath of paths) {\n const absPath = resolve(targetPath)\n const result = await walkPath(absPath, resolve(absPath, '..'), {\n countFn,\n all: options.all,\n maxDepth: options.maxDepth,\n exclude: options.exclude,\n smartIgnore: options.smartIgnore,\n })\n results.push(result)\n }\n\n return results\n}\n"],"mappings":"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"}
+9
-2
#!/usr/bin/env node
"use strict";
const require_main = require('../main-BLcEaqCf.cjs');
const require_main = require('../main-COYhytHE.cjs');
const node_path = require_main.__toESM(require("node:path"));

@@ -24,2 +24,3 @@ const node_fs_promises = require_main.__toESM(require("node:fs/promises"));

--exclude <pat> Glob pattern to exclude (repeatable)
--no-ignore Disable default .git, node_modules, and .gitignore skips
--version Print version and exit

@@ -85,2 +86,6 @@ --help Print this help and exit

},
"no-ignore": {
type: "boolean",
default: false
},
version: {

@@ -130,2 +135,3 @@ type: "boolean",

exclude: excludePatterns,
smartIgnore: !(values["no-ignore"] ?? false),
paths

@@ -139,3 +145,4 @@ };

model: cliOptions.model,
exclude: cliOptions.exclude
exclude: cliOptions.exclude,
smartIgnore: cliOptions.smartIgnore
});

@@ -142,0 +149,0 @@ let output;

#!/usr/bin/env node
import { formatJson, formatText, resolveEncoding, walkAndCount } from "../main-Bt4L5ONO.mjs";
import { formatJson, formatText, resolveEncoding, walkAndCount } from "../main-RYEeU4ax.mjs";
import { dirname, resolve } from "node:path";

@@ -23,2 +23,3 @@ import { readFile } from "node:fs/promises";

--exclude <pat> Glob pattern to exclude (repeatable)
--no-ignore Disable default .git, node_modules, and .gitignore skips
--version Print version and exit

@@ -84,2 +85,6 @@ --help Print this help and exit

},
"no-ignore": {
type: "boolean",
default: false
},
version: {

@@ -129,2 +134,3 @@ type: "boolean",

exclude: excludePatterns,
smartIgnore: !(values["no-ignore"] ?? false),
paths

@@ -138,3 +144,4 @@ };

model: cliOptions.model,
exclude: cliOptions.exclude
exclude: cliOptions.exclude,
smartIgnore: cliOptions.smartIgnore
});

@@ -141,0 +148,0 @@ let output;

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"cli.mjs","names":["maxDepth: number | undefined","cliOptions: CliOptions","output: string"],"sources":["../../src/bin/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from 'node:util'\nimport { readFile } from 'node:fs/promises'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { walkAndCount, resolveEncoding, formatText, formatJson } from '../main.ts'\nimport type { CliOptions } from '../types.ts'\n\nconst HELP_TEXT = `Usage: tokenu [options] [path...]\n\nA du-like CLI that counts token usage per file and directory.\n\nOptions:\n -s, --summarize Display only a total for each argument\n -h, --human-readable Print token counts in human-readable format (1K, 1M)\n -a, --all Show counts for all files, not just directories\n -d, --max-depth <N> Print totals only for directories N levels deep\n -c, --total Produce a grand total\n --json Output as JSON (for AI agent consumption)\n --encoding <enc> Tokenizer encoding (default: o200k_base)\n --model <name> Model name (e.g. gpt-4o, gpt-3.5-turbo)\n --exclude <pat> Glob pattern to exclude (repeatable)\n --version Print version and exit\n --help Print this help and exit\n\nEncodings: o200k_base, o200k_harmony, cl100k_base, p50k_base, p50k_edit, r50k_base\n\nExamples:\n tokenu . Recursive token counts per directory\n tokenu -hs src/ Human-readable summary of src/\n tokenu -a --json . All files as JSON\n tokenu --encoding cl100k_base . Use GPT-4/3.5 encoding\n tokenu -d 1 --total . Depth-limited with grand total\n`\n\nasync function getVersion (): Promise<string> {\n const currentDir = dirname(fileURLToPath(import.meta.url))\n const pkgPath = resolve(currentDir, '..', '..', 'package.json')\n try {\n const raw = await readFile(pkgPath, 'utf-8')\n const pkg = JSON.parse(raw) as { version: string }\n return pkg.version\n } catch {\n return '0.0.0'\n }\n}\n\nasync function main (): Promise<void> {\n const { values, positionals } = parseArgs({\n options: {\n summarize: { type: 'boolean', short: 's', default: false },\n 'human-readable': { type: 'boolean', short: 'h', default: false },\n all: { type: 'boolean', short: 'a', default: false },\n 'max-depth': { type: 'string', short: 'd' },\n total: { type: 'boolean', short: 'c', default: false },\n json: { type: 'boolean', default: false },\n encoding: { type: 'string' },\n model: { type: 'string' },\n exclude: { type: 'string', multiple: true },\n version: { type: 'boolean', default: false },\n help: { type: 'boolean', default: false },\n },\n allowPositionals: true,\n strict: true,\n })\n\n if (values.help) {\n process.stdout.write(HELP_TEXT)\n return\n }\n\n if (values.version) {\n const version = await getVersion()\n process.stdout.write(`${version}\\n`)\n return\n }\n\n const maxDepthRaw = values['max-depth']\n let maxDepth: number | undefined\n if (maxDepthRaw !== undefined) {\n maxDepth = parseInt(maxDepthRaw, 10)\n if (Number.isNaN(maxDepth) || maxDepth < 0) {\n process.stderr.write(`tokenu: invalid max depth: ${maxDepthRaw}\\n`)\n process.exitCode = 1\n return\n }\n }\n\n const encoding = resolveEncoding(values.encoding, values.model)\n const paths = positionals.length > 0 ? positionals : ['.']\n const excludePatterns = values.exclude ?? []\n\n const cliOptions: CliOptions = {\n summarize: values.summarize ?? false,\n humanReadable: values['human-readable'] ?? false,\n all: values.all ?? false,\n maxDepth,\n total: values.total ?? false,\n json: values.json ?? false,\n encoding,\n model: values.model,\n exclude: excludePatterns,\n paths,\n }\n\n try {\n const results = await walkAndCount(paths, {\n all: cliOptions.all,\n maxDepth: cliOptions.maxDepth,\n encoding: cliOptions.encoding,\n model: cliOptions.model,\n exclude: cliOptions.exclude,\n })\n\n let output: string\n if (cliOptions.json) {\n const version = await getVersion()\n output = formatJson(results, cliOptions, version)\n } else {\n output = formatText(results, cliOptions)\n }\n\n process.stdout.write(output + '\\n')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n process.stderr.write(`tokenu: ${message}\\n`)\n process.exitCode = 1\n }\n}\n\nmain()\n"],"mappings":";;;;;;;;AAQA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BnB,eAAe,aAA+B;CAC5C,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;CAC1D,MAAM,UAAU,QAAQ,YAAY,MAAM,MAAM,eAAe;AAC/D,KAAI;EACF,MAAM,MAAM,MAAM,SAAS,SAAS,QAAQ;EAC5C,MAAM,MAAM,KAAK,MAAM,IAAI;AAC3B,SAAO,IAAI;CACZ,QAAO;AACN,SAAO;CACR;AACF;AAED,eAAe,OAAuB;CACpC,MAAM,EAAE,QAAQ,aAAa,GAAG,UAAU;EACxC,SAAS;GACP,WAAW;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAO;GAC1D,kBAAkB;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAO;GACjE,KAAK;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAO;GACpD,aAAa;IAAE,MAAM;IAAU,OAAO;GAAK;GAC3C,OAAO;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAO;GACtD,MAAM;IAAE,MAAM;IAAW,SAAS;GAAO;GACzC,UAAU,EAAE,MAAM,SAAU;GAC5B,OAAO,EAAE,MAAM,SAAU;GACzB,SAAS;IAAE,MAAM;IAAU,UAAU;GAAM;GAC3C,SAAS;IAAE,MAAM;IAAW,SAAS;GAAO;GAC5C,MAAM;IAAE,MAAM;IAAW,SAAS;GAAO;EAC1C;EACD,kBAAkB;EAClB,QAAQ;CACT,EAAC;AAEF,KAAI,OAAO,MAAM;AACf,UAAQ,OAAO,MAAM,UAAU;AAC/B;CACD;AAED,KAAI,OAAO,SAAS;EAClB,MAAM,UAAU,MAAM,YAAY;AAClC,UAAQ,OAAO,OAAO,EAAE,QAAQ,IAAI;AACpC;CACD;CAED,MAAM,cAAc,OAAO;CAC3B,IAAIA;AACJ,KAAI,gBAAgB,WAAW;AAC7B,aAAW,SAAS,aAAa,GAAG;AACpC,MAAI,OAAO,MAAM,SAAS,IAAI,WAAW,GAAG;AAC1C,WAAQ,OAAO,OAAO,6BAA6B,YAAY,IAAI;AACnE,WAAQ,WAAW;AACnB;EACD;CACF;CAED,MAAM,WAAW,gBAAgB,OAAO,UAAU,OAAO,MAAM;CAC/D,MAAM,QAAQ,YAAY,SAAS,IAAI,cAAc,CAAC,GAAI;CAC1D,MAAM,kBAAkB,OAAO,WAAW,CAAE;CAE5C,MAAMC,aAAyB;EAC7B,WAAW,OAAO,aAAa;EAC/B,eAAe,OAAO,qBAAqB;EAC3C,KAAK,OAAO,OAAO;EACnB;EACA,OAAO,OAAO,SAAS;EACvB,MAAM,OAAO,QAAQ;EACrB;EACA,OAAO,OAAO;EACd,SAAS;EACT;CACD;AAED,KAAI;EACF,MAAM,UAAU,MAAM,aAAa,OAAO;GACxC,KAAK,WAAW;GAChB,UAAU,WAAW;GACrB,UAAU,WAAW;GACrB,OAAO,WAAW;GAClB,SAAS,WAAW;EACrB,EAAC;EAEF,IAAIC;AACJ,MAAI,WAAW,MAAM;GACnB,MAAM,UAAU,MAAM,YAAY;AAClC,YAAS,WAAW,SAAS,YAAY,QAAQ;EAClD,OAAM;AACL,YAAS,WAAW,SAAS,WAAW;EACzC;AAED,UAAQ,OAAO,MAAM,SAAS,KAAK;CACpC,SAAQ,KAAK;EACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,UAAQ,OAAO,OAAO,UAAU,QAAQ,IAAI;AAC5C,UAAQ,WAAW;CACpB;AACF;AAED,MAAM"}
{"version":3,"file":"cli.mjs","names":["maxDepth: number | undefined","cliOptions: CliOptions","output: string"],"sources":["../../src/bin/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from 'node:util'\nimport { readFile } from 'node:fs/promises'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { walkAndCount, resolveEncoding, formatText, formatJson } from '../main.ts'\nimport type { CliOptions } from '../types.ts'\n\nconst HELP_TEXT = `Usage: tokenu [options] [path...]\n\nA du-like CLI that counts token usage per file and directory.\n\nOptions:\n -s, --summarize Display only a total for each argument\n -h, --human-readable Print token counts in human-readable format (1K, 1M)\n -a, --all Show counts for all files, not just directories\n -d, --max-depth <N> Print totals only for directories N levels deep\n -c, --total Produce a grand total\n --json Output as JSON (for AI agent consumption)\n --encoding <enc> Tokenizer encoding (default: o200k_base)\n --model <name> Model name (e.g. gpt-4o, gpt-3.5-turbo)\n --exclude <pat> Glob pattern to exclude (repeatable)\n --no-ignore Disable default .git, node_modules, and .gitignore skips\n --version Print version and exit\n --help Print this help and exit\n\nEncodings: o200k_base, o200k_harmony, cl100k_base, p50k_base, p50k_edit, r50k_base\n\nExamples:\n tokenu . Recursive token counts per directory\n tokenu -hs src/ Human-readable summary of src/\n tokenu -a --json . All files as JSON\n tokenu --encoding cl100k_base . Use GPT-4/3.5 encoding\n tokenu -d 1 --total . Depth-limited with grand total\n`\n\nasync function getVersion (): Promise<string> {\n const currentDir = dirname(fileURLToPath(import.meta.url))\n const pkgPath = resolve(currentDir, '..', '..', 'package.json')\n try {\n const raw = await readFile(pkgPath, 'utf-8')\n const pkg = JSON.parse(raw) as { version: string }\n return pkg.version\n } catch {\n return '0.0.0'\n }\n}\n\nasync function main (): Promise<void> {\n const { values, positionals } = parseArgs({\n options: {\n summarize: { type: 'boolean', short: 's', default: false },\n 'human-readable': { type: 'boolean', short: 'h', default: false },\n all: { type: 'boolean', short: 'a', default: false },\n 'max-depth': { type: 'string', short: 'd' },\n total: { type: 'boolean', short: 'c', default: false },\n json: { type: 'boolean', default: false },\n encoding: { type: 'string' },\n model: { type: 'string' },\n exclude: { type: 'string', multiple: true },\n 'no-ignore': { type: 'boolean', default: false },\n version: { type: 'boolean', default: false },\n help: { type: 'boolean', default: false },\n },\n allowPositionals: true,\n strict: true,\n })\n\n if (values.help) {\n process.stdout.write(HELP_TEXT)\n return\n }\n\n if (values.version) {\n const version = await getVersion()\n process.stdout.write(`${version}\\n`)\n return\n }\n\n const maxDepthRaw = values['max-depth']\n let maxDepth: number | undefined\n if (maxDepthRaw !== undefined) {\n maxDepth = parseInt(maxDepthRaw, 10)\n if (Number.isNaN(maxDepth) || maxDepth < 0) {\n process.stderr.write(`tokenu: invalid max depth: ${maxDepthRaw}\\n`)\n process.exitCode = 1\n return\n }\n }\n\n const encoding = resolveEncoding(values.encoding, values.model)\n const paths = positionals.length > 0 ? positionals : ['.']\n const excludePatterns = values.exclude ?? []\n\n const cliOptions: CliOptions = {\n summarize: values.summarize ?? false,\n humanReadable: values['human-readable'] ?? false,\n all: values.all ?? false,\n maxDepth,\n total: values.total ?? false,\n json: values.json ?? false,\n encoding,\n model: values.model,\n exclude: excludePatterns,\n smartIgnore: !(values['no-ignore'] ?? false),\n paths,\n }\n\n try {\n const results = await walkAndCount(paths, {\n all: cliOptions.all,\n maxDepth: cliOptions.maxDepth,\n encoding: cliOptions.encoding,\n model: cliOptions.model,\n exclude: cliOptions.exclude,\n smartIgnore: cliOptions.smartIgnore,\n })\n\n let output: string\n if (cliOptions.json) {\n const version = await getVersion()\n output = formatJson(results, cliOptions, version)\n } else {\n output = formatText(results, cliOptions)\n }\n\n process.stdout.write(output + '\\n')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n process.stderr.write(`tokenu: ${message}\\n`)\n process.exitCode = 1\n }\n}\n\nmain()\n"],"mappings":";;;;;;;;AAQA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BnB,eAAe,aAA+B;CAC5C,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;CAC1D,MAAM,UAAU,QAAQ,YAAY,MAAM,MAAM,eAAe;AAC/D,KAAI;EACF,MAAM,MAAM,MAAM,SAAS,SAAS,QAAQ;EAC5C,MAAM,MAAM,KAAK,MAAM,IAAI;AAC3B,SAAO,IAAI;CACZ,QAAO;AACN,SAAO;CACR;AACF;AAED,eAAe,OAAuB;CACpC,MAAM,EAAE,QAAQ,aAAa,GAAG,UAAU;EACxC,SAAS;GACP,WAAW;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAO;GAC1D,kBAAkB;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAO;GACjE,KAAK;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAO;GACpD,aAAa;IAAE,MAAM;IAAU,OAAO;GAAK;GAC3C,OAAO;IAAE,MAAM;IAAW,OAAO;IAAK,SAAS;GAAO;GACtD,MAAM;IAAE,MAAM;IAAW,SAAS;GAAO;GACzC,UAAU,EAAE,MAAM,SAAU;GAC5B,OAAO,EAAE,MAAM,SAAU;GACzB,SAAS;IAAE,MAAM;IAAU,UAAU;GAAM;GAC3C,aAAa;IAAE,MAAM;IAAW,SAAS;GAAO;GAChD,SAAS;IAAE,MAAM;IAAW,SAAS;GAAO;GAC5C,MAAM;IAAE,MAAM;IAAW,SAAS;GAAO;EAC1C;EACD,kBAAkB;EAClB,QAAQ;CACT,EAAC;AAEF,KAAI,OAAO,MAAM;AACf,UAAQ,OAAO,MAAM,UAAU;AAC/B;CACD;AAED,KAAI,OAAO,SAAS;EAClB,MAAM,UAAU,MAAM,YAAY;AAClC,UAAQ,OAAO,OAAO,EAAE,QAAQ,IAAI;AACpC;CACD;CAED,MAAM,cAAc,OAAO;CAC3B,IAAIA;AACJ,KAAI,gBAAgB,WAAW;AAC7B,aAAW,SAAS,aAAa,GAAG;AACpC,MAAI,OAAO,MAAM,SAAS,IAAI,WAAW,GAAG;AAC1C,WAAQ,OAAO,OAAO,6BAA6B,YAAY,IAAI;AACnE,WAAQ,WAAW;AACnB;EACD;CACF;CAED,MAAM,WAAW,gBAAgB,OAAO,UAAU,OAAO,MAAM;CAC/D,MAAM,QAAQ,YAAY,SAAS,IAAI,cAAc,CAAC,GAAI;CAC1D,MAAM,kBAAkB,OAAO,WAAW,CAAE;CAE5C,MAAMC,aAAyB;EAC7B,WAAW,OAAO,aAAa;EAC/B,eAAe,OAAO,qBAAqB;EAC3C,KAAK,OAAO,OAAO;EACnB;EACA,OAAO,OAAO,SAAS;EACvB,MAAM,OAAO,QAAQ;EACrB;EACA,OAAO,OAAO;EACd,SAAS;EACT,eAAe,OAAO,gBAAgB;EACtC;CACD;AAED,KAAI;EACF,MAAM,UAAU,MAAM,aAAa,OAAO;GACxC,KAAK,WAAW;GAChB,UAAU,WAAW;GACrB,UAAU,WAAW;GACrB,OAAO,WAAW;GAClB,SAAS,WAAW;GACpB,aAAa,WAAW;EACzB,EAAC;EAEF,IAAIC;AACJ,MAAI,WAAW,MAAM;GACnB,MAAM,UAAU,MAAM,YAAY;AAClC,YAAS,WAAW,SAAS,YAAY,QAAQ;EAClD,OAAM;AACL,YAAS,WAAW,SAAS,WAAW;EACzC;AAED,UAAQ,OAAO,MAAM,SAAS,KAAK;CACpC,SAAQ,KAAK;EACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,UAAQ,OAAO,OAAO,UAAU,QAAQ,IAAI;AAC5C,UAAQ,WAAW;CACpB;AACF;AAED,MAAM"}

@@ -1,2 +0,2 @@

const require_main = require('./main-BLcEaqCf.cjs');
const require_main = require('./main-COYhytHE.cjs');

@@ -3,0 +3,0 @@ exports.countFileTokens = require_main.countFileTokens

@@ -18,2 +18,3 @@ //#region src/types.d.ts

exclude: string[];
smartIgnore?: boolean;
paths: string[];

@@ -38,2 +39,13 @@ }

//# sourceMappingURL=types.d.ts.map
interface GitIgnoreRule {
pattern: string;
negated: boolean;
directoryOnly: boolean;
anchored: boolean;
hasSlash: boolean;
}
interface IgnoreContext {
rootPath: string;
rules: GitIgnoreRule[];
}
declare function countFileTokens(filePath: string, countFn: TokenCountFn): Promise<number>;

@@ -45,8 +57,8 @@ interface WalkOptions {

exclude: string[];
smartIgnore?: boolean;
}
declare function walkPath(targetPath: string, basePath: string, options: WalkOptions, currentDepth?: number): Promise<TokenResult>;
declare function walkPath(targetPath: string, basePath: string, options: WalkOptions, currentDepth?: number, ignoreContexts?: IgnoreContext[]): Promise<TokenResult>;
//#endregion
//#region src/tokenizer.d.ts
//# sourceMappingURL=walker.d.ts.map
declare function resolveEncoding(encoding?: string, model?: string): string;

@@ -53,0 +65,0 @@ declare function getTokenCounter(encoding?: string, model?: string): Promise<TokenCountFn>;

@@ -1,1 +0,1 @@

{"version":3,"file":"main.d.cts","names":[],"sources":["../src/types.ts","../src/walker.ts","../src/tokenizer.ts","../src/formatter.ts","../src/main.ts"],"sourcesContent":null,"mappings":";UAAiB,WAAA;EAAA,IAAA,EAAA,MAAA;EAOA,MAAA,EAAA,MAAU;EAaV,MAAA,EAAA,OAAA;EAOA,QAAA,CAAA,EAvBJ,WAuBc,EAIhB;AAIX;UA5BiB,UAAA;;;ECqBK,GAAA,EAAA,OAAA;EAAe,QAAA,CAAA,EAAA,MAAA;EAAA,KAE1B,EAAA,OAAA;EAAY,IACpB,EAAA,OAAA;EAAO,QAAA,EAAA,MAAA;EAYO,KAAA,CAAA,EAAA,MAAW;EAON,OAAA,EAAA,MAAQ,EAAA;EAAA,KAAA,EAAA,MAAA,EAAA;;AAKnB,UDnCM,eAAA,CCmCN;EAAW,IAAnB,EAAA,MAAA;EAAO,MAAA,EAAA,MAAA;;;;AC5CM,UFgBC,UAAA,CEhBc;EAaT,OAAA,EAAA,MAAA;EAAe,QAAA,EAAA,MAAA;EAAA,SAA8C,EAAA,MAAA;EAAY,OAApB,EFOhE,eEPgE,EAAA;EAAO,KAAA,CAAA,EAAA,MAAA;;KFWtE,YAAA;;;;AAnCK,iBC4BK,eAAA,CDxBE,QAAA,EAAA,MAAA,EAAA,OAAA,EC0Bb,YD1Ba,CAAA,EC2BrB,OD3BqB,CAAA,MAAA,CAAA;AAGP,UCoCA,WAAA,CDpCU;EAaV,OAAA,ECwBN,YDxBqB;EAOf,GAAA,EAAA,OAAA;EAQL,QAAA,CAAA,EAAA,MAAY;;;iBCeF,QAAA,gDAGX,qCAER,QAAQ;;;;AA3BX;AD5BiB,iBEWD,eAAA,CFPQ,QAAA,CAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAGP,iBEiBK,eAAA,CFjBK,QAAA,CAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EEiBgD,OFjBhD,CEiBwD,YFjBxD,CAAA;;;;AAa3B;AApBiB,iBG0CD,UAAA,CHtCH,OAAW,EGsCa,WHtCb,EAAA,EAAA,OAAA,EGsCqC,UHtCrC,CAAA,EAAA,MAAA;AAGP,iBGgFD,UAAA,CHhFW,OAAA,EGiFhB,WHjFgB,EAAA,EAAA,OAAA,EGkFhB,UHlFgB,EAAA,OAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;;AAa3B;;AAeY,iBI1BU,YAAA,CJ0BE,KAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EIxBb,IJwBa,CIxBR,UJwBQ,EAAA,OAAA,GAAA,MAAA,GAAA,eAAA,GAAA,OAAA,GAAA,WAAA,CAAA,CAAA,EIvBrB,OJuBqB,CIvBb,WJuBa,EAAA,CAAA"}
{"version":3,"file":"main.d.cts","names":[],"sources":["../src/types.ts","../src/walker.ts","../src/tokenizer.ts","../src/formatter.ts","../src/main.ts"],"sourcesContent":null,"mappings":";UAAiB,WAAA;EAAA,IAAA,EAAA,MAAA;EAOA,MAAA,EAAA,MAAU;EAcV,MAAA,EAAA,OAAA;EAOA,QAAA,CAAA,EAxBJ,WAwBc,EAIhB;AAIX;UA7BiB,UAAA;;;ECKP,GAAA,EAAA,OAAA;EAQA,QAAA,CAAA,EAAA,MAAa;EA4HD,KAAA,EAAA,OAAA;EAAe,IAAA,EAAA,OAAA;EAAA,QAE1B,EAAA,MAAA;EAAY,KACpB,CAAA,EAAA,MAAA;EAAO,OAAA,EAAA,MAAA,EAAA;EAYO,WAAA,CAAA,EAAW,OAAA;EAQN,KAAA,EAAA,MAAQ,EAAA;;AAGnB,UDrJM,eAAA,CCqJN;EAAW,IAEJ,EAAA,MAAA;EAAa,MACpB,EAAA,MAAA;EAAW,IAAnB,EAAA,MAAA,GAAA,WAAA;EAAO,QAAA,CAAA,EAAA,MAAA,EAAA;;UDjJO,UAAA;;EEjBD,QAAA,EAAA,MAAA;EAaM,SAAA,EAAA,MAAA;EAAe,OAAA,EFQ1B,eER0B,EAAA;EAAA,KAA8C,CAAA,EAAA,MAAA;;AAAD,KFYtE,YAAA,GEZsE,CAAA,IAAA,EAAA,MAAA,EAAA,GAAA,MAAA,CAAA;;;;AFxBlF,UCYU,aAAA,CDZkB;EAOX,OAAA,EAAA,MAAU;EAcV,OAAA,EAAA,OAAA;EAOA,aAAU,EAAA,OAAA;EAQf,QAAA,EAAA,OAAY;;;UChBd,aAAA;EARA,QAAA,EAAA,MAAa;EAQb,KAAA,EAED,aAFc,EAAA;AA4HvB;AAAqC,iBAAf,eAAA,CAAe,QAAA,EAAA,MAAA,EAAA,OAAA,EAE1B,YAF0B,CAAA,EAGlC,OAHkC,CAAA,MAAA,CAAA;AAE1B,UAaM,WAAA,CAbN;EAAY,OACpB,EAaQ,YAbR;EAAO,GAAA,EAAA,OAAA;EAYO,QAAA,CAAA,EAAA,MAAW;EAQN,OAAA,EAAA,MAAQ,EAAA;EAAA,WAAA,CAAA,EAAA,OAAA;;AAKZ,iBALI,QAAA,CAKJ,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAFP,WAEO,EAAA,YAAA,CAAA,EAAA,MAAA,EAAA,cAAA,CAAA,EAAA,aAAA,EAAA,CAAA,EACf,OADe,CACP,WADO,CAAA;;;;AD5KD,iBEWD,eAAA,CFPQ,QAAA,CAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAGP,iBEiBK,eAAA,CFjBK,QAAA,CAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EEiBgD,OFjBhD,CEiBwD,YFjBxD,CAAA;;;;AAc3B;AArBiB,iBG0CD,UAAA,CHtCH,OAAW,EGsCa,WHtCb,EAAA,EAAA,OAAA,EGsCqC,UHtCrC,CAAA,EAAA,MAAA;AAGP,iBGgFD,UAAA,CHhFW,OAAA,EGiFhB,WHjFgB,EAAA,EAAA,OAAA,EGkFhB,UHlFgB,EAAA,OAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;;AAc3B;;AAeY,iBI3BU,YAAA,CJ2BE,KAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EIzBb,IJyBa,CIzBR,UJyBQ,EAAA,OAAA,GAAA,MAAA,GAAA,eAAA,GAAA,OAAA,GAAA,WAAA,CAAA,CAAA,EIxBrB,OJwBqB,CIxBb,WJwBa,EAAA,CAAA"}

@@ -18,2 +18,3 @@ //#region src/types.d.ts

exclude: string[];
smartIgnore?: boolean;
paths: string[];

@@ -38,2 +39,13 @@ }

//# sourceMappingURL=types.d.ts.map
interface GitIgnoreRule {
pattern: string;
negated: boolean;
directoryOnly: boolean;
anchored: boolean;
hasSlash: boolean;
}
interface IgnoreContext {
rootPath: string;
rules: GitIgnoreRule[];
}
declare function countFileTokens(filePath: string, countFn: TokenCountFn): Promise<number>;

@@ -45,8 +57,8 @@ interface WalkOptions {

exclude: string[];
smartIgnore?: boolean;
}
declare function walkPath(targetPath: string, basePath: string, options: WalkOptions, currentDepth?: number): Promise<TokenResult>;
declare function walkPath(targetPath: string, basePath: string, options: WalkOptions, currentDepth?: number, ignoreContexts?: IgnoreContext[]): Promise<TokenResult>;
//#endregion
//#region src/tokenizer.d.ts
//# sourceMappingURL=walker.d.ts.map
declare function resolveEncoding(encoding?: string, model?: string): string;

@@ -53,0 +65,0 @@ declare function getTokenCounter(encoding?: string, model?: string): Promise<TokenCountFn>;

@@ -1,1 +0,1 @@

{"version":3,"file":"main.d.mts","names":[],"sources":["../src/types.ts","../src/walker.ts","../src/tokenizer.ts","../src/formatter.ts","../src/main.ts"],"sourcesContent":null,"mappings":";UAAiB,WAAA;EAAA,IAAA,EAAA,MAAA;EAOA,MAAA,EAAA,MAAU;EAaV,MAAA,EAAA,OAAA;EAOA,QAAA,CAAA,EAvBJ,WAuBc,EAIhB;AAIX;UA5BiB,UAAA;;;ECqBK,GAAA,EAAA,OAAA;EAAe,QAAA,CAAA,EAAA,MAAA;EAAA,KAE1B,EAAA,OAAA;EAAY,IACpB,EAAA,OAAA;EAAO,QAAA,EAAA,MAAA;EAYO,KAAA,CAAA,EAAA,MAAW;EAON,OAAA,EAAA,MAAQ,EAAA;EAAA,KAAA,EAAA,MAAA,EAAA;;AAKnB,UDnCM,eAAA,CCmCN;EAAW,IAAnB,EAAA,MAAA;EAAO,MAAA,EAAA,MAAA;;;;AC5CM,UFgBC,UAAA,CEhBc;EAaT,OAAA,EAAA,MAAA;EAAe,QAAA,EAAA,MAAA;EAAA,SAA8C,EAAA,MAAA;EAAY,OAApB,EFOhE,eEPgE,EAAA;EAAO,KAAA,CAAA,EAAA,MAAA;;KFWtE,YAAA;;;;AAnCK,iBC4BK,eAAA,CDxBE,QAAA,EAAA,MAAA,EAAA,OAAA,EC0Bb,YD1Ba,CAAA,EC2BrB,OD3BqB,CAAA,MAAA,CAAA;AAGP,UCoCA,WAAA,CDpCU;EAaV,OAAA,ECwBN,YDxBqB;EAOf,GAAA,EAAA,OAAA;EAQL,QAAA,CAAA,EAAA,MAAY;;;iBCeF,QAAA,gDAGX,qCAER,QAAQ;;;;AA3BX;AD5BiB,iBEWD,eAAA,CFPQ,QAAA,CAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAGP,iBEiBK,eAAA,CFjBK,QAAA,CAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EEiBgD,OFjBhD,CEiBwD,YFjBxD,CAAA;;;;AAa3B;AApBiB,iBG0CD,UAAA,CHtCH,OAAW,EGsCa,WHtCb,EAAA,EAAA,OAAA,EGsCqC,UHtCrC,CAAA,EAAA,MAAA;AAGP,iBGgFD,UAAA,CHhFW,OAAA,EGiFhB,WHjFgB,EAAA,EAAA,OAAA,EGkFhB,UHlFgB,EAAA,OAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;;AAa3B;;AAeY,iBI1BU,YAAA,CJ0BE,KAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EIxBb,IJwBa,CIxBR,UJwBQ,EAAA,OAAA,GAAA,MAAA,GAAA,eAAA,GAAA,OAAA,GAAA,WAAA,CAAA,CAAA,EIvBrB,OJuBqB,CIvBb,WJuBa,EAAA,CAAA"}
{"version":3,"file":"main.d.mts","names":[],"sources":["../src/types.ts","../src/walker.ts","../src/tokenizer.ts","../src/formatter.ts","../src/main.ts"],"sourcesContent":null,"mappings":";UAAiB,WAAA;EAAA,IAAA,EAAA,MAAA;EAOA,MAAA,EAAA,MAAU;EAcV,MAAA,EAAA,OAAA;EAOA,QAAA,CAAA,EAxBJ,WAwBc,EAIhB;AAIX;UA7BiB,UAAA;;;ECKP,GAAA,EAAA,OAAA;EAQA,QAAA,CAAA,EAAA,MAAa;EA4HD,KAAA,EAAA,OAAA;EAAe,IAAA,EAAA,OAAA;EAAA,QAE1B,EAAA,MAAA;EAAY,KACpB,CAAA,EAAA,MAAA;EAAO,OAAA,EAAA,MAAA,EAAA;EAYO,WAAA,CAAA,EAAW,OAAA;EAQN,KAAA,EAAA,MAAQ,EAAA;;AAGnB,UDrJM,eAAA,CCqJN;EAAW,IAEJ,EAAA,MAAA;EAAa,MACpB,EAAA,MAAA;EAAW,IAAnB,EAAA,MAAA,GAAA,WAAA;EAAO,QAAA,CAAA,EAAA,MAAA,EAAA;;UDjJO,UAAA;;EEjBD,QAAA,EAAA,MAAA;EAaM,SAAA,EAAA,MAAA;EAAe,OAAA,EFQ1B,eER0B,EAAA;EAAA,KAA8C,CAAA,EAAA,MAAA;;AAAD,KFYtE,YAAA,GEZsE,CAAA,IAAA,EAAA,MAAA,EAAA,GAAA,MAAA,CAAA;;;;AFxBlF,UCYU,aAAA,CDZkB;EAOX,OAAA,EAAA,MAAU;EAcV,OAAA,EAAA,OAAA;EAOA,aAAU,EAAA,OAAA;EAQf,QAAA,EAAA,OAAY;;;UChBd,aAAA;EARA,QAAA,EAAA,MAAa;EAQb,KAAA,EAED,aAFc,EAAA;AA4HvB;AAAqC,iBAAf,eAAA,CAAe,QAAA,EAAA,MAAA,EAAA,OAAA,EAE1B,YAF0B,CAAA,EAGlC,OAHkC,CAAA,MAAA,CAAA;AAE1B,UAaM,WAAA,CAbN;EAAY,OACpB,EAaQ,YAbR;EAAO,GAAA,EAAA,OAAA;EAYO,QAAA,CAAA,EAAA,MAAW;EAQN,OAAA,EAAA,MAAQ,EAAA;EAAA,WAAA,CAAA,EAAA,OAAA;;AAKZ,iBALI,QAAA,CAKJ,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAFP,WAEO,EAAA,YAAA,CAAA,EAAA,MAAA,EAAA,cAAA,CAAA,EAAA,aAAA,EAAA,CAAA,EACf,OADe,CACP,WADO,CAAA;;;;AD5KD,iBEWD,eAAA,CFPQ,QAAA,CAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAGP,iBEiBK,eAAA,CFjBK,QAAA,CAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EEiBgD,OFjBhD,CEiBwD,YFjBxD,CAAA;;;;AAc3B;AArBiB,iBG0CD,UAAA,CHtCH,OAAW,EGsCa,WHtCb,EAAA,EAAA,OAAA,EGsCqC,UHtCrC,CAAA,EAAA,MAAA;AAGP,iBGgFD,UAAA,CHhFW,OAAA,EGiFhB,WHjFgB,EAAA,EAAA,OAAA,EGkFhB,UHlFgB,EAAA,OAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;;AAc3B;;AAeY,iBI3BU,YAAA,CJ2BE,KAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EIzBb,IJyBa,CIzBR,UJyBQ,EAAA,OAAA,GAAA,MAAA,GAAA,eAAA,GAAA,OAAA,GAAA,WAAA,CAAA,CAAA,EIxBrB,OJwBqB,CIxBb,WJwBa,EAAA,CAAA"}

@@ -1,3 +0,3 @@

import { countFileTokens, formatJson, formatText, getTokenCounter, resolveEncoding, walkAndCount } from "./main-Bt4L5ONO.mjs";
import { countFileTokens, formatJson, formatText, getTokenCounter, resolveEncoding, walkAndCount } from "./main-RYEeU4ax.mjs";
export { countFileTokens, formatJson, formatText, getTokenCounter, resolveEncoding, walkAndCount };
{
"name": "tokenu",
"version": "1.0.1",
"version": "1.1.0",
"description": "A Unix du-like command line tool to count token usage per files and directories",

@@ -8,3 +8,4 @@ "types": "dist/main.d.mts",

"bin": {
"tokenu": "./dist/bin/cli.cjs"
"tokenu": "./dist/bin/cli.cjs",
"tu": "./dist/bin/cli.cjs"
},

@@ -11,0 +12,0 @@ "exports": {

@@ -49,2 +49,14 @@ <!-- markdownlint-disable -->

Prefer a shorter command without installing tokenu globally? Add this alias to your shell config file, such as `~/.zshrc` or `~/.bashrc`:
```sh
alias tu='npx tokenu'
```
Then use `tu` anywhere you would use `tokenu`:
```sh
tu .
```
### Real-World Recipes

@@ -101,2 +113,3 @@

| `--exclude <pat>` | Glob pattern to exclude (repeatable) |
| `--no-ignore` | Disable default `.git`, `node_modules`, and `.gitignore` skips |

@@ -186,3 +199,3 @@ Supported encodings: `o200k_base`, `o200k_harmony`, `cl100k_base`, `p50k_base`, `p50k_edit`, `r50k_base`

Actual tokenization. No estimation or approximation. tokenu reads every file, feeds its content through a real tokenizer (the [`gpt-tokenizer`](https://github.com/nicolo-ribaudo/gpt-tokenizer) library), and sums the results recursively across directories. The counts you see are the same counts the model would consume.
Actual tokenization. No estimation or approximation. tokenu reads each included file, feeds its content through a real tokenizer (the [`gpt-tokenizer`](https://github.com/niieani/gpt-tokenizer) library), and sums the results recursively across directories. It skips `.git`, `node_modules`, and files matched by `.gitignore` by default; pass `--no-ignore` to include them. The counts you see are the same counts the model would consume.

@@ -206,3 +219,3 @@ You can also choose which tokenizer encoding to use. Different model families use different encodings, and token counts can vary between them. For example:

Please consult [CONTRIBUTING](./.github/CONTRIBUTING.md) for guidelines on contributing to this project.
Please consult [CONTRIBUTING](./CONTRIBUTING.md) for guidelines on contributing to this project.

@@ -209,0 +222,0 @@ ## Author

@@ -23,2 +23,3 @@ #!/usr/bin/env node

--exclude <pat> Glob pattern to exclude (repeatable)
--no-ignore Disable default .git, node_modules, and .gitignore skips
--version Print version and exit

@@ -61,2 +62,3 @@ --help Print this help and exit

exclude: { type: 'string', multiple: true },
'no-ignore': { type: 'boolean', default: false },
version: { type: 'boolean', default: false },

@@ -105,2 +107,3 @@ help: { type: 'boolean', default: false },

exclude: excludePatterns,
smartIgnore: !(values['no-ignore'] ?? false),
paths,

@@ -116,2 +119,3 @@ }

exclude: cliOptions.exclude,
smartIgnore: cliOptions.smartIgnore,
})

@@ -118,0 +122,0 @@

@@ -24,2 +24,3 @@ import { resolve } from 'node:path'

exclude: options.exclude,
smartIgnore: options.smartIgnore,
})

@@ -26,0 +27,0 @@ results.push(result)

@@ -18,2 +18,3 @@ export interface TokenResult {

exclude: string[]
smartIgnore?: boolean
paths: string[]

@@ -20,0 +21,0 @@ }

import { readdir, readFile, stat } from 'node:fs/promises'
import { join, relative } from 'node:path'
import { join, matchesGlob, relative, sep } from 'node:path'
import type { TokenCountFn, TokenResult } from './types.ts'

@@ -11,3 +11,17 @@

const BINARY_CHECK_BYTES = 8192
const GITIGNORE_FILE = '.gitignore'
interface GitIgnoreRule {
pattern: string
negated: boolean
directoryOnly: boolean
anchored: boolean
hasSlash: boolean
}
interface IgnoreContext {
rootPath: string
rules: GitIgnoreRule[]
}
function isBinaryBuffer (buffer: Buffer): boolean {

@@ -30,2 +44,104 @@ const length = Math.min(buffer.length, BINARY_CHECK_BYTES)

function toPosixPath (value: string): string {
return sep === '/' ? value : value.split(sep).join('/')
}
function parseGitIgnoreLine (rawLine: string): GitIgnoreRule | undefined {
let pattern = rawLine.trim()
if (pattern === '' || pattern.startsWith('#')) return undefined
if (pattern.startsWith('\\#') || pattern.startsWith('\\!')) {
pattern = pattern.slice(1)
}
let negated = false
if (pattern.startsWith('!')) {
negated = true
pattern = pattern.slice(1)
}
pattern = pattern.trim()
if (pattern === '') return undefined
const anchored = pattern.startsWith('/')
pattern = pattern.replace(/^\/+/, '')
const directoryOnly = pattern.endsWith('/')
pattern = pattern.replace(/\/+$/, '')
if (pattern === '') return undefined
return {
pattern,
negated,
directoryOnly,
anchored,
hasSlash: pattern.includes('/'),
}
}
function parseGitIgnore (content: string): GitIgnoreRule[] {
const rules: GitIgnoreRule[] = []
for (const line of content.split(/\r?\n/)) {
const rule = parseGitIgnoreLine(line)
if (rule) rules.push(rule)
}
return rules
}
async function loadGitIgnoreContext (targetPath: string): Promise<IgnoreContext | undefined> {
try {
const content = await readFile(join(targetPath, GITIGNORE_FILE), 'utf-8')
const rules = parseGitIgnore(content)
return rules.length > 0 ? { rootPath: targetPath, rules } : undefined
} catch (err) {
if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
return undefined
}
throw err
}
}
function matchesGitIgnorePattern (
relativePath: string,
basename: string,
isDirectory: boolean,
rule: GitIgnoreRule
): boolean {
if (rule.directoryOnly && !isDirectory) return false
if (!rule.anchored && !rule.hasSlash) {
return matchesGlob(basename, rule.pattern)
}
if (matchesGlob(relativePath, rule.pattern)) return true
if (isDirectory) {
return matchesGlob(`${relativePath}/`, `${rule.pattern}/**`)
}
return false
}
function matchesGitIgnore (
childPath: string,
entryName: string,
isDirectory: boolean,
ignoreContexts: IgnoreContext[]
): boolean {
let ignored = false
for (const context of ignoreContexts) {
const relativePath = toPosixPath(relative(context.rootPath, childPath))
if (relativePath === '' || relativePath.startsWith('../')) continue
for (const rule of context.rules) {
if (matchesGitIgnorePattern(relativePath, entryName, isDirectory, rule)) {
ignored = !rule.negated
}
}
}
return ignored
}
export async function countFileTokens (

@@ -51,2 +167,3 @@ filePath: string,

exclude: string[]
smartIgnore?: boolean
}

@@ -58,3 +175,4 @@

options: WalkOptions,
currentDepth: number = 0
currentDepth: number = 0,
ignoreContexts: IgnoreContext[] = []
): Promise<TokenResult> {

@@ -79,9 +197,15 @@ const info = await stat(targetPath)

let totalTokens = 0
const smartIgnore = options.smartIgnore ?? true
const currentIgnoreContexts = smartIgnore
? [...ignoreContexts, ...await loadGitIgnoreContext(targetPath).then(context => context ? [context] : [])]
: ignoreContexts
for (const entry of entries) {
if (DEFAULT_IGNORE.has(entry.name)) continue
const childPath = join(targetPath, entry.name)
const isDirectory = entry.isDirectory()
if (smartIgnore && DEFAULT_IGNORE.has(entry.name)) continue
if (smartIgnore && matchesGitIgnore(childPath, entry.name, isDirectory, currentIgnoreContexts)) continue
if (matchesExclude(entry.name, options.exclude)) continue
const childPath = join(targetPath, entry.name)
if (entry.isFile()) {

@@ -99,3 +223,3 @@ const tokens = await countFileTokens(childPath, options.countFn)

}
const childResult = await walkPath(childPath, basePath, options, currentDepth + 1)
const childResult = await walkPath(childPath, basePath, options, currentDepth + 1, currentIgnoreContexts)
totalTokens += childResult.tokens

@@ -102,0 +226,0 @@ children.push(childResult)

"use strict";
//#region rolldown:runtime
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
const node_path = __toESM(require("node:path"));
const node_fs_promises = __toESM(require("node:fs/promises"));
//#region src/tokenizer.ts
const VALID_ENCODINGS = new Set([
"o200k_base",
"o200k_harmony",
"cl100k_base",
"p50k_base",
"p50k_edit",
"r50k_base"
]);
function resolveEncoding(encoding, model) {
if (encoding && model) {
throw new Error("Cannot specify both --encoding and --model");
}
if (encoding) {
if (!VALID_ENCODINGS.has(encoding)) {
throw new Error(`Unknown encoding: ${encoding}. Valid encodings: ${[...VALID_ENCODINGS].join(", ")}`);
}
return encoding;
}
return "o200k_base";
}
async function getTokenCounter(encoding, model) {
if (model) {
if (encoding) {
throw new Error("Cannot specify both --encoding and --model");
}
const mod$1 = await import(`gpt-tokenizer/model/${model}`);
return mod$1.countTokens;
}
const resolvedEncoding = resolveEncoding(encoding);
if (resolvedEncoding === "o200k_base") {
const { countTokens } = await import("gpt-tokenizer");
return countTokens;
}
const mod = await import(`gpt-tokenizer/encoding/${resolvedEncoding}`);
return mod.countTokens;
}
//#endregion
//#region src/walker.ts
const DEFAULT_IGNORE = new Set(["node_modules", ".git"]);
const BINARY_CHECK_BYTES = 8192;
function isBinaryBuffer(buffer) {
const length = Math.min(buffer.length, BINARY_CHECK_BYTES);
for (let i = 0; i < length; i++) {
if (buffer[i] === 0) return true;
}
return false;
}
function matchesExclude(name, excludePatterns) {
for (const pattern of excludePatterns) {
if (pattern === name) return true;
if (pattern.startsWith("*.") && name.endsWith(pattern.slice(1))) return true;
if (pattern.endsWith("/*") && name === pattern.slice(0, -2)) return true;
}
return false;
}
async function countFileTokens(filePath, countFn) {
const buffer = await (0, node_fs_promises.readFile)(filePath);
if (buffer.length === 0) return 0;
if (isBinaryBuffer(buffer)) return 0;
const text = buffer.toString("utf-8");
try {
return countFn(text);
} catch {
return 0;
}
}
async function walkPath(targetPath, basePath, options, currentDepth = 0) {
const info = await (0, node_fs_promises.stat)(targetPath);
if (info.isFile()) {
const tokens = await countFileTokens(targetPath, options.countFn);
return {
path: (0, node_path.relative)(basePath, targetPath) || targetPath,
tokens,
isFile: true
};
}
if (!info.isDirectory()) {
return {
path: (0, node_path.relative)(basePath, targetPath) || targetPath,
tokens: 0,
isFile: false
};
}
const entries = await (0, node_fs_promises.readdir)(targetPath, { withFileTypes: true });
const children = [];
let totalTokens = 0;
for (const entry of entries) {
if (DEFAULT_IGNORE.has(entry.name)) continue;
if (matchesExclude(entry.name, options.exclude)) continue;
const childPath = (0, node_path.join)(targetPath, entry.name);
if (entry.isFile()) {
const tokens = await countFileTokens(childPath, options.countFn);
totalTokens += tokens;
children.push({
path: (0, node_path.relative)(basePath, childPath),
tokens,
isFile: true
});
} else if (entry.isDirectory()) {
if (options.maxDepth !== undefined && currentDepth >= options.maxDepth) {
continue;
}
const childResult = await walkPath(childPath, basePath, options, currentDepth + 1);
totalTokens += childResult.tokens;
children.push(childResult);
}
}
children.sort((a, b) => a.path.localeCompare(b.path));
return {
path: (0, node_path.relative)(basePath, targetPath) || targetPath,
tokens: totalTokens,
isFile: false,
children
};
}
//#endregion
//#region src/formatter.ts
function humanReadableTokens(count) {
if (count >= 1e6) {
const value = count / 1e6;
return value % 1 === 0 ? `${value}M` : `${value.toFixed(1)}M`;
}
if (count >= 1e3) {
const value = count / 1e3;
return value % 1 === 0 ? `${value}K` : `${value.toFixed(1)}K`;
}
return count.toString();
}
function formatLine(tokens, path, humanReadable) {
const count = humanReadable ? humanReadableTokens(tokens) : tokens.toString();
return `${count}\t${path}`;
}
function collectTextLines(result, options, lines, depth = 0) {
if (result.isFile) {
if (options.all) {
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
return;
}
if (result.children) {
for (const child of result.children) {
if (options.maxDepth !== undefined && depth >= options.maxDepth) break;
collectTextLines(child, options, lines, depth + 1);
}
}
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
function formatText(results, options) {
const lines = [];
if (options.summarize) {
for (const result of results) {
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
} else {
for (const result of results) {
collectTextLines(result, options, lines);
}
}
if (options.total) {
const grandTotal = results.reduce((sum, r) => sum + r.tokens, 0);
lines.push(formatLine(grandTotal, "total", options.humanReadable));
}
return lines.join("\n");
}
function collectJsonEntries(result, entries) {
if (result.isFile) {
entries.push({
path: result.path,
tokens: result.tokens,
type: "file"
});
return;
}
if (result.children) {
for (const child of result.children) {
collectJsonEntries(child, entries);
}
}
entries.push({
path: result.path,
tokens: result.tokens,
type: "directory",
children: result.children?.map((c) => c.path)
});
}
function formatJson(results, options, version) {
const entries = [];
if (options.summarize) {
for (const result of results) {
entries.push({
path: result.path,
tokens: result.tokens,
type: result.isFile ? "file" : "directory"
});
}
} else {
for (const result of results) {
collectJsonEntries(result, entries);
}
}
const output = {
version,
encoding: options.encoding,
timestamp: new Date().toISOString(),
results: entries
};
if (options.total || options.summarize) {
output.total = results.reduce((sum, r) => sum + r.tokens, 0);
}
return JSON.stringify(output, null, 2);
}
//#endregion
//#region src/main.ts
async function walkAndCount(paths, options) {
const countFn = await getTokenCounter(options.encoding, options.model);
const results = [];
for (const targetPath of paths) {
const absPath = (0, node_path.resolve)(targetPath);
const result = await walkPath(absPath, (0, node_path.resolve)(absPath, ".."), {
countFn,
all: options.all,
maxDepth: options.maxDepth,
exclude: options.exclude
});
results.push(result);
}
return results;
}
//#endregion
Object.defineProperty(exports, '__toESM', {
enumerable: true,
get: function () {
return __toESM;
}
});
Object.defineProperty(exports, 'countFileTokens', {
enumerable: true,
get: function () {
return countFileTokens;
}
});
Object.defineProperty(exports, 'formatJson', {
enumerable: true,
get: function () {
return formatJson;
}
});
Object.defineProperty(exports, 'formatText', {
enumerable: true,
get: function () {
return formatText;
}
});
Object.defineProperty(exports, 'getTokenCounter', {
enumerable: true,
get: function () {
return getTokenCounter;
}
});
Object.defineProperty(exports, 'resolveEncoding', {
enumerable: true,
get: function () {
return resolveEncoding;
}
});
Object.defineProperty(exports, 'walkAndCount', {
enumerable: true,
get: function () {
return walkAndCount;
}
});
import { join, relative, resolve } from "node:path";
import { readFile, readdir, stat } from "node:fs/promises";
//#region src/tokenizer.ts
const VALID_ENCODINGS = new Set([
"o200k_base",
"o200k_harmony",
"cl100k_base",
"p50k_base",
"p50k_edit",
"r50k_base"
]);
function resolveEncoding(encoding, model) {
if (encoding && model) {
throw new Error("Cannot specify both --encoding and --model");
}
if (encoding) {
if (!VALID_ENCODINGS.has(encoding)) {
throw new Error(`Unknown encoding: ${encoding}. Valid encodings: ${[...VALID_ENCODINGS].join(", ")}`);
}
return encoding;
}
return "o200k_base";
}
async function getTokenCounter(encoding, model) {
if (model) {
if (encoding) {
throw new Error("Cannot specify both --encoding and --model");
}
const mod$1 = await import(`gpt-tokenizer/model/${model}`);
return mod$1.countTokens;
}
const resolvedEncoding = resolveEncoding(encoding);
if (resolvedEncoding === "o200k_base") {
const { countTokens } = await import("gpt-tokenizer");
return countTokens;
}
const mod = await import(`gpt-tokenizer/encoding/${resolvedEncoding}`);
return mod.countTokens;
}
//#endregion
//#region src/walker.ts
const DEFAULT_IGNORE = new Set(["node_modules", ".git"]);
const BINARY_CHECK_BYTES = 8192;
function isBinaryBuffer(buffer) {
const length = Math.min(buffer.length, BINARY_CHECK_BYTES);
for (let i = 0; i < length; i++) {
if (buffer[i] === 0) return true;
}
return false;
}
function matchesExclude(name, excludePatterns) {
for (const pattern of excludePatterns) {
if (pattern === name) return true;
if (pattern.startsWith("*.") && name.endsWith(pattern.slice(1))) return true;
if (pattern.endsWith("/*") && name === pattern.slice(0, -2)) return true;
}
return false;
}
async function countFileTokens(filePath, countFn) {
const buffer = await readFile(filePath);
if (buffer.length === 0) return 0;
if (isBinaryBuffer(buffer)) return 0;
const text = buffer.toString("utf-8");
try {
return countFn(text);
} catch {
return 0;
}
}
async function walkPath(targetPath, basePath, options, currentDepth = 0) {
const info = await stat(targetPath);
if (info.isFile()) {
const tokens = await countFileTokens(targetPath, options.countFn);
return {
path: relative(basePath, targetPath) || targetPath,
tokens,
isFile: true
};
}
if (!info.isDirectory()) {
return {
path: relative(basePath, targetPath) || targetPath,
tokens: 0,
isFile: false
};
}
const entries = await readdir(targetPath, { withFileTypes: true });
const children = [];
let totalTokens = 0;
for (const entry of entries) {
if (DEFAULT_IGNORE.has(entry.name)) continue;
if (matchesExclude(entry.name, options.exclude)) continue;
const childPath = join(targetPath, entry.name);
if (entry.isFile()) {
const tokens = await countFileTokens(childPath, options.countFn);
totalTokens += tokens;
children.push({
path: relative(basePath, childPath),
tokens,
isFile: true
});
} else if (entry.isDirectory()) {
if (options.maxDepth !== undefined && currentDepth >= options.maxDepth) {
continue;
}
const childResult = await walkPath(childPath, basePath, options, currentDepth + 1);
totalTokens += childResult.tokens;
children.push(childResult);
}
}
children.sort((a, b) => a.path.localeCompare(b.path));
return {
path: relative(basePath, targetPath) || targetPath,
tokens: totalTokens,
isFile: false,
children
};
}
//#endregion
//#region src/formatter.ts
function humanReadableTokens(count) {
if (count >= 1e6) {
const value = count / 1e6;
return value % 1 === 0 ? `${value}M` : `${value.toFixed(1)}M`;
}
if (count >= 1e3) {
const value = count / 1e3;
return value % 1 === 0 ? `${value}K` : `${value.toFixed(1)}K`;
}
return count.toString();
}
function formatLine(tokens, path, humanReadable) {
const count = humanReadable ? humanReadableTokens(tokens) : tokens.toString();
return `${count}\t${path}`;
}
function collectTextLines(result, options, lines, depth = 0) {
if (result.isFile) {
if (options.all) {
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
return;
}
if (result.children) {
for (const child of result.children) {
if (options.maxDepth !== undefined && depth >= options.maxDepth) break;
collectTextLines(child, options, lines, depth + 1);
}
}
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
function formatText(results, options) {
const lines = [];
if (options.summarize) {
for (const result of results) {
lines.push(formatLine(result.tokens, result.path, options.humanReadable));
}
} else {
for (const result of results) {
collectTextLines(result, options, lines);
}
}
if (options.total) {
const grandTotal = results.reduce((sum, r) => sum + r.tokens, 0);
lines.push(formatLine(grandTotal, "total", options.humanReadable));
}
return lines.join("\n");
}
function collectJsonEntries(result, entries) {
if (result.isFile) {
entries.push({
path: result.path,
tokens: result.tokens,
type: "file"
});
return;
}
if (result.children) {
for (const child of result.children) {
collectJsonEntries(child, entries);
}
}
entries.push({
path: result.path,
tokens: result.tokens,
type: "directory",
children: result.children?.map((c) => c.path)
});
}
function formatJson(results, options, version) {
const entries = [];
if (options.summarize) {
for (const result of results) {
entries.push({
path: result.path,
tokens: result.tokens,
type: result.isFile ? "file" : "directory"
});
}
} else {
for (const result of results) {
collectJsonEntries(result, entries);
}
}
const output = {
version,
encoding: options.encoding,
timestamp: new Date().toISOString(),
results: entries
};
if (options.total || options.summarize) {
output.total = results.reduce((sum, r) => sum + r.tokens, 0);
}
return JSON.stringify(output, null, 2);
}
//#endregion
//#region src/main.ts
async function walkAndCount(paths, options) {
const countFn = await getTokenCounter(options.encoding, options.model);
const results = [];
for (const targetPath of paths) {
const absPath = resolve(targetPath);
const result = await walkPath(absPath, resolve(absPath, ".."), {
countFn,
all: options.all,
maxDepth: options.maxDepth,
exclude: options.exclude
});
results.push(result);
}
return results;
}
//#endregion
export { countFileTokens, formatJson, formatText, getTokenCounter, resolveEncoding, walkAndCount };
//# sourceMappingURL=main-Bt4L5ONO.mjs.map
{"version":3,"file":"main-Bt4L5ONO.mjs","names":["encoding?: string","model?: string","buffer: Buffer","name: string","excludePatterns: string[]","filePath: string","countFn: TokenCountFn","targetPath: string","basePath: string","options: WalkOptions","currentDepth: number","children: TokenResult[]","count: number","tokens: number","path: string","humanReadable: boolean","result: TokenResult","options: CliOptions","lines: string[]","depth: number","results: TokenResult[]","entries: JsonOutputEntry[]","version: string","output: JsonOutput","paths: string[]","options: Omit<CliOptions, 'paths' | 'json' | 'humanReadable' | 'total' | 'summarize'>","results: TokenResult[]"],"sources":["../src/tokenizer.ts","../src/walker.ts","../src/formatter.ts","../src/main.ts"],"sourcesContent":["import type { TokenCountFn } from './types.ts'\n\nconst VALID_ENCODINGS = new Set([\n 'o200k_base',\n 'o200k_harmony',\n 'cl100k_base',\n 'p50k_base',\n 'p50k_edit',\n 'r50k_base',\n])\n\nexport function resolveEncoding (encoding?: string, model?: string): string {\n if (encoding && model) {\n throw new Error('Cannot specify both --encoding and --model')\n }\n if (encoding) {\n if (!VALID_ENCODINGS.has(encoding)) {\n throw new Error(`Unknown encoding: ${encoding}. Valid encodings: ${[...VALID_ENCODINGS].join(', ')}`)\n }\n return encoding\n }\n return 'o200k_base'\n}\n\nexport async function getTokenCounter (encoding?: string, model?: string): Promise<TokenCountFn> {\n if (model) {\n if (encoding) {\n throw new Error('Cannot specify both --encoding and --model')\n }\n const mod = await import(`gpt-tokenizer/model/${model}`) as { countTokens: TokenCountFn }\n return mod.countTokens\n }\n\n const resolvedEncoding = resolveEncoding(encoding)\n if (resolvedEncoding === 'o200k_base') {\n const { countTokens } = await import('gpt-tokenizer')\n return countTokens\n }\n\n const mod = await import(`gpt-tokenizer/encoding/${resolvedEncoding}`) as { countTokens: TokenCountFn }\n return mod.countTokens\n}\n","import { readdir, readFile, stat } from 'node:fs/promises'\nimport { join, relative } from 'node:path'\nimport type { TokenCountFn, TokenResult } from './types.ts'\n\nconst DEFAULT_IGNORE = new Set([\n 'node_modules',\n '.git',\n])\n\nconst BINARY_CHECK_BYTES = 8192\n\nfunction isBinaryBuffer (buffer: Buffer): boolean {\n const length = Math.min(buffer.length, BINARY_CHECK_BYTES)\n for (let i = 0; i < length; i++) {\n if (buffer[i] === 0) return true\n }\n return false\n}\n\nfunction matchesExclude (name: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern === name) return true\n if (pattern.startsWith('*.') && name.endsWith(pattern.slice(1))) return true\n if (pattern.endsWith('/*') && name === pattern.slice(0, -2)) return true\n }\n return false\n}\n\nexport async function countFileTokens (\n filePath: string,\n countFn: TokenCountFn\n): Promise<number> {\n const buffer = await readFile(filePath)\n if (buffer.length === 0) return 0\n if (isBinaryBuffer(buffer)) return 0\n const text = buffer.toString('utf-8')\n try {\n return countFn(text)\n } catch {\n return 0\n }\n}\n\nexport interface WalkOptions {\n countFn: TokenCountFn\n all: boolean\n maxDepth?: number\n exclude: string[]\n}\n\nexport async function walkPath (\n targetPath: string,\n basePath: string,\n options: WalkOptions,\n currentDepth: number = 0\n): Promise<TokenResult> {\n const info = await stat(targetPath)\n\n if (info.isFile()) {\n const tokens = await countFileTokens(targetPath, options.countFn)\n return {\n path: relative(basePath, targetPath) || targetPath,\n tokens,\n isFile: true,\n }\n }\n\n if (!info.isDirectory()) {\n return { path: relative(basePath, targetPath) || targetPath, tokens: 0, isFile: false }\n }\n\n const entries = await readdir(targetPath, { withFileTypes: true })\n const children: TokenResult[] = []\n let totalTokens = 0\n\n for (const entry of entries) {\n if (DEFAULT_IGNORE.has(entry.name)) continue\n if (matchesExclude(entry.name, options.exclude)) continue\n\n const childPath = join(targetPath, entry.name)\n\n if (entry.isFile()) {\n const tokens = await countFileTokens(childPath, options.countFn)\n totalTokens += tokens\n children.push({\n path: relative(basePath, childPath),\n tokens,\n isFile: true,\n })\n } else if (entry.isDirectory()) {\n if (options.maxDepth !== undefined && currentDepth >= options.maxDepth) {\n continue\n }\n const childResult = await walkPath(childPath, basePath, options, currentDepth + 1)\n totalTokens += childResult.tokens\n children.push(childResult)\n }\n }\n\n children.sort((a, b) => a.path.localeCompare(b.path))\n\n return {\n path: relative(basePath, targetPath) || targetPath,\n tokens: totalTokens,\n isFile: false,\n children,\n }\n}\n","import type { CliOptions, JsonOutput, JsonOutputEntry, TokenResult } from './types.ts'\n\nfunction humanReadableTokens (count: number): string {\n if (count >= 1_000_000) {\n const value = count / 1_000_000\n return value % 1 === 0 ? `${value}M` : `${value.toFixed(1)}M`\n }\n if (count >= 1_000) {\n const value = count / 1_000\n return value % 1 === 0 ? `${value}K` : `${value.toFixed(1)}K`\n }\n return count.toString()\n}\n\nfunction formatLine (tokens: number, path: string, humanReadable: boolean): string {\n const count = humanReadable ? humanReadableTokens(tokens) : tokens.toString()\n return `${count}\\t${path}`\n}\n\nfunction collectTextLines (\n result: TokenResult,\n options: CliOptions,\n lines: string[],\n depth: number = 0\n): void {\n if (result.isFile) {\n if (options.all) {\n lines.push(formatLine(result.tokens, result.path, options.humanReadable))\n }\n return\n }\n\n if (result.children) {\n for (const child of result.children) {\n if (options.maxDepth !== undefined && depth >= options.maxDepth) break\n collectTextLines(child, options, lines, depth + 1)\n }\n }\n\n lines.push(formatLine(result.tokens, result.path, options.humanReadable))\n}\n\nexport function formatText (results: TokenResult[], options: CliOptions): string {\n const lines: string[] = []\n\n if (options.summarize) {\n for (const result of results) {\n lines.push(formatLine(result.tokens, result.path, options.humanReadable))\n }\n } else {\n for (const result of results) {\n collectTextLines(result, options, lines)\n }\n }\n\n if (options.total) {\n const grandTotal = results.reduce((sum, r) => sum + r.tokens, 0)\n lines.push(formatLine(grandTotal, 'total', options.humanReadable))\n }\n\n return lines.join('\\n')\n}\n\nfunction collectJsonEntries (result: TokenResult, entries: JsonOutputEntry[]): void {\n if (result.isFile) {\n entries.push({\n path: result.path,\n tokens: result.tokens,\n type: 'file',\n })\n return\n }\n\n if (result.children) {\n for (const child of result.children) {\n collectJsonEntries(child, entries)\n }\n }\n\n entries.push({\n path: result.path,\n tokens: result.tokens,\n type: 'directory',\n children: result.children?.map(c => c.path),\n })\n}\n\nexport function formatJson (\n results: TokenResult[],\n options: CliOptions,\n version: string\n): string {\n const entries: JsonOutputEntry[] = []\n\n if (options.summarize) {\n for (const result of results) {\n entries.push({\n path: result.path,\n tokens: result.tokens,\n type: result.isFile ? 'file' : 'directory',\n })\n }\n } else {\n for (const result of results) {\n collectJsonEntries(result, entries)\n }\n }\n\n const output: JsonOutput = {\n version,\n encoding: options.encoding,\n timestamp: new Date().toISOString(),\n results: entries,\n }\n\n if (options.total || options.summarize) {\n output.total = results.reduce((sum, r) => sum + r.tokens, 0)\n }\n\n return JSON.stringify(output, null, 2)\n}\n","import { resolve } from 'node:path'\nimport type { CliOptions, TokenResult } from './types.ts'\nimport { getTokenCounter } from './tokenizer.ts'\nimport { walkPath } from './walker.ts'\nexport { countFileTokens } from './walker.ts'\nexport { getTokenCounter, resolveEncoding } from './tokenizer.ts'\nexport { formatText, formatJson } from './formatter.ts'\nexport type { TokenResult, CliOptions, TokenCountFn, JsonOutput, JsonOutputEntry } from './types.ts'\n\nexport async function walkAndCount (\n paths: string[],\n options: Omit<CliOptions, 'paths' | 'json' | 'humanReadable' | 'total' | 'summarize'>\n): Promise<TokenResult[]> {\n const countFn = await getTokenCounter(options.encoding, options.model)\n const results: TokenResult[] = []\n\n for (const targetPath of paths) {\n const absPath = resolve(targetPath)\n const result = await walkPath(absPath, resolve(absPath, '..'), {\n countFn,\n all: options.all,\n maxDepth: options.maxDepth,\n exclude: options.exclude,\n })\n results.push(result)\n }\n\n return results\n}\n"],"mappings":"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"}