formatferry 0.1.1 → 0.1.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.
- package/dist/index.js +461 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22108,6 +22108,398 @@ function extractArticle(html, domProvider) {
|
|
|
22108
22108
|
};
|
|
22109
22109
|
}
|
|
22110
22110
|
|
|
22111
|
+
// ../../src/shared/token-estimator.ts
|
|
22112
|
+
var CJK_REGEX = /[\u4e00-\u9fff\u3400-\u4dbf\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g;
|
|
22113
|
+
var MAX_INPUT_LENGTH = 1e6;
|
|
22114
|
+
function estimateTokens(text) {
|
|
22115
|
+
if (!text || text.length === 0) {
|
|
22116
|
+
return 0;
|
|
22117
|
+
}
|
|
22118
|
+
if (text.length > MAX_INPUT_LENGTH) {
|
|
22119
|
+
return Math.round(text.length / 4);
|
|
22120
|
+
}
|
|
22121
|
+
const cjkMatches = text.match(CJK_REGEX);
|
|
22122
|
+
const cjkCount = cjkMatches ? cjkMatches.length : 0;
|
|
22123
|
+
const otherCount = text.length - cjkCount;
|
|
22124
|
+
const cjkTokens = cjkCount / 1.5;
|
|
22125
|
+
const otherTokens = otherCount / 4;
|
|
22126
|
+
return Math.round(cjkTokens + otherTokens);
|
|
22127
|
+
}
|
|
22128
|
+
|
|
22129
|
+
// ../../src/shared/toon-encoder.ts
|
|
22130
|
+
var DEFAULT_OPTIONS = {
|
|
22131
|
+
indent: 0,
|
|
22132
|
+
keyFolding: true,
|
|
22133
|
+
tableFormat: "compact"
|
|
22134
|
+
};
|
|
22135
|
+
var ToonEncoder = class _ToonEncoder {
|
|
22136
|
+
static encode(value, opts) {
|
|
22137
|
+
const options = { ...DEFAULT_OPTIONS, ...opts };
|
|
22138
|
+
return _ToonEncoder.encodeValue(value, options.indent, options);
|
|
22139
|
+
}
|
|
22140
|
+
static encodeValue(value, indent, opts) {
|
|
22141
|
+
if (value === null) {
|
|
22142
|
+
return "null";
|
|
22143
|
+
}
|
|
22144
|
+
if (typeof value === "boolean") {
|
|
22145
|
+
return value ? "true" : "false";
|
|
22146
|
+
}
|
|
22147
|
+
if (typeof value === "number") {
|
|
22148
|
+
return String(value);
|
|
22149
|
+
}
|
|
22150
|
+
if (typeof value === "string") {
|
|
22151
|
+
return _ToonEncoder.encodeString(value);
|
|
22152
|
+
}
|
|
22153
|
+
if (Array.isArray(value)) {
|
|
22154
|
+
return _ToonEncoder.encodeArray(value, indent, opts);
|
|
22155
|
+
}
|
|
22156
|
+
if (typeof value === "object") {
|
|
22157
|
+
return _ToonEncoder.encodeObject(value, indent, opts);
|
|
22158
|
+
}
|
|
22159
|
+
return String(value);
|
|
22160
|
+
}
|
|
22161
|
+
static encodeString(str) {
|
|
22162
|
+
const needsQuotes = str.includes(":") || str.includes("\n") || str.includes(" ") || str.includes("|") || str.includes(",") || str.startsWith(" ") || str.endsWith(" ") || str === "";
|
|
22163
|
+
if (needsQuotes) {
|
|
22164
|
+
const escaped = str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\t/g, "\\t");
|
|
22165
|
+
return `"${escaped}"`;
|
|
22166
|
+
}
|
|
22167
|
+
return str;
|
|
22168
|
+
}
|
|
22169
|
+
static encodeArray(arr, indent, opts) {
|
|
22170
|
+
if (arr.length === 0) {
|
|
22171
|
+
return "[]";
|
|
22172
|
+
}
|
|
22173
|
+
if (_ToonEncoder.isTabularArray(arr)) {
|
|
22174
|
+
return _ToonEncoder.encodeTabular(arr);
|
|
22175
|
+
}
|
|
22176
|
+
const prefix = " ".repeat(indent);
|
|
22177
|
+
const lines = arr.map((item) => {
|
|
22178
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
22179
|
+
const obj = item;
|
|
22180
|
+
const keys = Object.keys(obj);
|
|
22181
|
+
if (keys.length > 0) {
|
|
22182
|
+
const objLines = keys.map((key, idx) => {
|
|
22183
|
+
const value = obj[key];
|
|
22184
|
+
const encodedValue = _ToonEncoder.encodeValue(value, indent + 1, opts);
|
|
22185
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
22186
|
+
const innerLines = encodedValue.split("\n");
|
|
22187
|
+
if (innerLines.length > 1 && !encodedValue.startsWith("{}")) {
|
|
22188
|
+
if (idx === 0) {
|
|
22189
|
+
return `${prefix}- ${key}:
|
|
22190
|
+
${innerLines.map((l) => `${prefix} ${l.trimStart()}`).join("\n")}`;
|
|
22191
|
+
}
|
|
22192
|
+
return `${prefix} ${key}:
|
|
22193
|
+
${innerLines.map((l) => `${prefix} ${l.trimStart()}`).join("\n")}`;
|
|
22194
|
+
}
|
|
22195
|
+
}
|
|
22196
|
+
if (idx === 0) {
|
|
22197
|
+
return `${prefix}- ${key}: ${encodedValue}`;
|
|
22198
|
+
}
|
|
22199
|
+
return `${prefix} ${key}: ${encodedValue}`;
|
|
22200
|
+
});
|
|
22201
|
+
return objLines.join("\n");
|
|
22202
|
+
}
|
|
22203
|
+
}
|
|
22204
|
+
const encoded = _ToonEncoder.encodeValue(item, indent + 1, opts);
|
|
22205
|
+
return `${prefix}- ${encoded}`;
|
|
22206
|
+
});
|
|
22207
|
+
return lines.join("\n");
|
|
22208
|
+
}
|
|
22209
|
+
static encodeObject(obj, indent, opts) {
|
|
22210
|
+
const keys = Object.keys(obj);
|
|
22211
|
+
if (keys.length === 0) {
|
|
22212
|
+
return "{}";
|
|
22213
|
+
}
|
|
22214
|
+
if (opts.keyFolding && keys.length === 1) {
|
|
22215
|
+
const key = keys[0];
|
|
22216
|
+
const value = obj[key];
|
|
22217
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length === 1) {
|
|
22218
|
+
const foldedPath = _ToonEncoder.getFoldedPath(obj);
|
|
22219
|
+
if (foldedPath) {
|
|
22220
|
+
const { path, finalValue } = foldedPath;
|
|
22221
|
+
const encodedValue = _ToonEncoder.encodeValue(finalValue, indent, opts);
|
|
22222
|
+
const prefix2 = " ".repeat(indent);
|
|
22223
|
+
return `${prefix2}${path}: ${encodedValue}`;
|
|
22224
|
+
}
|
|
22225
|
+
}
|
|
22226
|
+
}
|
|
22227
|
+
const prefix = " ".repeat(indent);
|
|
22228
|
+
const lines = keys.map((key) => {
|
|
22229
|
+
const value = obj[key];
|
|
22230
|
+
const encodedValue = _ToonEncoder.encodeValue(value, indent + 1, opts);
|
|
22231
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
22232
|
+
const innerLines = encodedValue.split("\n");
|
|
22233
|
+
if (innerLines.length > 1 && !encodedValue.startsWith("{}")) {
|
|
22234
|
+
return `${prefix}${key}:
|
|
22235
|
+
${innerLines.map((l) => `${prefix} ${l.trimStart()}`).join("\n")}`;
|
|
22236
|
+
}
|
|
22237
|
+
}
|
|
22238
|
+
if (Array.isArray(value) && value.length > 0 && !_ToonEncoder.isTabularArray(value)) {
|
|
22239
|
+
return `${prefix}${key}:
|
|
22240
|
+
${encodedValue}`;
|
|
22241
|
+
}
|
|
22242
|
+
return `${prefix}${key}: ${encodedValue}`;
|
|
22243
|
+
});
|
|
22244
|
+
return lines.join("\n");
|
|
22245
|
+
}
|
|
22246
|
+
static getFoldedPath(obj, pathParts = []) {
|
|
22247
|
+
const keys = Object.keys(obj);
|
|
22248
|
+
if (keys.length !== 1) {
|
|
22249
|
+
return null;
|
|
22250
|
+
}
|
|
22251
|
+
const key = keys[0];
|
|
22252
|
+
const value = obj[key];
|
|
22253
|
+
const newPath = [...pathParts, key];
|
|
22254
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
22255
|
+
const innerKeys = Object.keys(value);
|
|
22256
|
+
if (innerKeys.length === 1) {
|
|
22257
|
+
return _ToonEncoder.getFoldedPath(value, newPath);
|
|
22258
|
+
}
|
|
22259
|
+
}
|
|
22260
|
+
return {
|
|
22261
|
+
path: newPath.join("."),
|
|
22262
|
+
finalValue: value
|
|
22263
|
+
};
|
|
22264
|
+
}
|
|
22265
|
+
static encodeTabular(arr) {
|
|
22266
|
+
if (arr.length === 0) {
|
|
22267
|
+
return "[]";
|
|
22268
|
+
}
|
|
22269
|
+
const keys = Object.keys(arr[0]);
|
|
22270
|
+
const header = `|${keys.join("|")}`;
|
|
22271
|
+
const rows = arr.map((row) => {
|
|
22272
|
+
const values = keys.map((key) => {
|
|
22273
|
+
const value = row[key];
|
|
22274
|
+
if (value === null) return "null";
|
|
22275
|
+
if (typeof value === "string") {
|
|
22276
|
+
return value.replace(/\|/g, "\\|");
|
|
22277
|
+
}
|
|
22278
|
+
return String(value);
|
|
22279
|
+
});
|
|
22280
|
+
return `|${values.join("|")}`;
|
|
22281
|
+
});
|
|
22282
|
+
return [header, ...rows].join("\n");
|
|
22283
|
+
}
|
|
22284
|
+
static isTabularArray(arr) {
|
|
22285
|
+
if (arr.length < 2) {
|
|
22286
|
+
return false;
|
|
22287
|
+
}
|
|
22288
|
+
const firstItem = arr[0];
|
|
22289
|
+
if (typeof firstItem !== "object" || firstItem === null || Array.isArray(firstItem)) {
|
|
22290
|
+
return false;
|
|
22291
|
+
}
|
|
22292
|
+
const firstKeys = Object.keys(firstItem).sort().join(",");
|
|
22293
|
+
for (let i = 1; i < arr.length; i++) {
|
|
22294
|
+
const item = arr[i];
|
|
22295
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
22296
|
+
return false;
|
|
22297
|
+
}
|
|
22298
|
+
const itemKeys = Object.keys(item).sort().join(",");
|
|
22299
|
+
if (itemKeys !== firstKeys) {
|
|
22300
|
+
return false;
|
|
22301
|
+
}
|
|
22302
|
+
for (const key in item) {
|
|
22303
|
+
const value = item[key];
|
|
22304
|
+
if (typeof value === "object" && value !== null) {
|
|
22305
|
+
return false;
|
|
22306
|
+
}
|
|
22307
|
+
}
|
|
22308
|
+
}
|
|
22309
|
+
for (const key in firstItem) {
|
|
22310
|
+
const value = firstItem[key];
|
|
22311
|
+
if (typeof value === "object" && value !== null) {
|
|
22312
|
+
return false;
|
|
22313
|
+
}
|
|
22314
|
+
}
|
|
22315
|
+
return true;
|
|
22316
|
+
}
|
|
22317
|
+
};
|
|
22318
|
+
|
|
22319
|
+
// ../../src/shared/toonify-service.ts
|
|
22320
|
+
var DEFAULT_OPTIONS2 = {
|
|
22321
|
+
preserveCodeBlocks: true,
|
|
22322
|
+
keyFolding: true
|
|
22323
|
+
};
|
|
22324
|
+
var ToonifyService = class {
|
|
22325
|
+
static compress(markdown, options) {
|
|
22326
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
22327
|
+
const original = markdown;
|
|
22328
|
+
const originalTokenEstimate = estimateTokens(original);
|
|
22329
|
+
if (!markdown || markdown.trim().length === 0) {
|
|
22330
|
+
return {
|
|
22331
|
+
original,
|
|
22332
|
+
compressed: "",
|
|
22333
|
+
originalTokenEstimate,
|
|
22334
|
+
compressedTokenEstimate: 0,
|
|
22335
|
+
reductionPercent: 100
|
|
22336
|
+
};
|
|
22337
|
+
}
|
|
22338
|
+
const blocks = this.parseMarkdown(markdown, opts);
|
|
22339
|
+
const document2 = { t: "md", blocks };
|
|
22340
|
+
const encodeOptions = {
|
|
22341
|
+
keyFolding: opts.keyFolding,
|
|
22342
|
+
tableFormat: opts.tableFormat
|
|
22343
|
+
};
|
|
22344
|
+
const compressed = ToonEncoder.encode(document2, encodeOptions);
|
|
22345
|
+
const compressedTokenEstimate = estimateTokens(compressed);
|
|
22346
|
+
const reductionPercent = originalTokenEstimate > 0 ? Math.max(0, Math.min(100, Math.round(
|
|
22347
|
+
(originalTokenEstimate - compressedTokenEstimate) / originalTokenEstimate * 100
|
|
22348
|
+
))) : 0;
|
|
22349
|
+
return {
|
|
22350
|
+
original,
|
|
22351
|
+
compressed,
|
|
22352
|
+
originalTokenEstimate,
|
|
22353
|
+
compressedTokenEstimate,
|
|
22354
|
+
reductionPercent
|
|
22355
|
+
};
|
|
22356
|
+
}
|
|
22357
|
+
static estimateTokens(text) {
|
|
22358
|
+
return estimateTokens(text);
|
|
22359
|
+
}
|
|
22360
|
+
static parseMarkdown(markdown, options) {
|
|
22361
|
+
const blocks = [];
|
|
22362
|
+
const lines = markdown.split("\n");
|
|
22363
|
+
let i = 0;
|
|
22364
|
+
while (i < lines.length) {
|
|
22365
|
+
const line = lines[i];
|
|
22366
|
+
if (line.match(/^```/)) {
|
|
22367
|
+
const { block, endIndex } = this.parseCodeBlock(lines, i, options);
|
|
22368
|
+
if (block) blocks.push(block);
|
|
22369
|
+
i = endIndex + 1;
|
|
22370
|
+
continue;
|
|
22371
|
+
}
|
|
22372
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
22373
|
+
if (headingMatch) {
|
|
22374
|
+
blocks.push({
|
|
22375
|
+
h: headingMatch[1].length,
|
|
22376
|
+
text: headingMatch[2].trim()
|
|
22377
|
+
});
|
|
22378
|
+
i++;
|
|
22379
|
+
continue;
|
|
22380
|
+
}
|
|
22381
|
+
if (line.includes("|") && line.trim().startsWith("|")) {
|
|
22382
|
+
const { block, endIndex } = this.parseTable(lines, i);
|
|
22383
|
+
if (block) blocks.push(block);
|
|
22384
|
+
i = endIndex + 1;
|
|
22385
|
+
continue;
|
|
22386
|
+
}
|
|
22387
|
+
const listMatch = line.match(/^(\s*)[-*]\s+(.+)$|^(\s*)\d+\.\s+(.+)$/);
|
|
22388
|
+
if (listMatch) {
|
|
22389
|
+
const { block, endIndex } = this.parseList(lines, i);
|
|
22390
|
+
if (block) blocks.push(block);
|
|
22391
|
+
i = endIndex + 1;
|
|
22392
|
+
continue;
|
|
22393
|
+
}
|
|
22394
|
+
if (line.trim().length > 0) {
|
|
22395
|
+
const { block, endIndex } = this.parseParagraph(lines, i);
|
|
22396
|
+
if (block) blocks.push(block);
|
|
22397
|
+
i = endIndex + 1;
|
|
22398
|
+
continue;
|
|
22399
|
+
}
|
|
22400
|
+
i++;
|
|
22401
|
+
}
|
|
22402
|
+
return blocks;
|
|
22403
|
+
}
|
|
22404
|
+
static parseCodeBlock(lines, startIndex, options) {
|
|
22405
|
+
const startLine = lines[startIndex];
|
|
22406
|
+
const langMatch = startLine.match(/^```(\w*)$/);
|
|
22407
|
+
const lang = langMatch ? langMatch[1] : "";
|
|
22408
|
+
let endIndex = startIndex + 1;
|
|
22409
|
+
const codeLines = [];
|
|
22410
|
+
while (endIndex < lines.length) {
|
|
22411
|
+
if (lines[endIndex].match(/^```$/)) {
|
|
22412
|
+
break;
|
|
22413
|
+
}
|
|
22414
|
+
codeLines.push(lines[endIndex]);
|
|
22415
|
+
endIndex++;
|
|
22416
|
+
}
|
|
22417
|
+
const codeText = codeLines.join("\n");
|
|
22418
|
+
if (options.preserveCodeBlocks) {
|
|
22419
|
+
return {
|
|
22420
|
+
block: { code: { lang, text: codeText } },
|
|
22421
|
+
endIndex
|
|
22422
|
+
};
|
|
22423
|
+
}
|
|
22424
|
+
return {
|
|
22425
|
+
block: { code: { lang, text: `[code:${codeText.length}chars]` } },
|
|
22426
|
+
endIndex
|
|
22427
|
+
};
|
|
22428
|
+
}
|
|
22429
|
+
static parseTable(lines, startIndex) {
|
|
22430
|
+
const headerLine = lines[startIndex];
|
|
22431
|
+
const headers = this.parseTableRow(headerLine);
|
|
22432
|
+
let endIndex = startIndex + 1;
|
|
22433
|
+
if (endIndex < lines.length && lines[endIndex].match(/^\|[\s:-]+\|[\s:-|]+$/)) {
|
|
22434
|
+
endIndex++;
|
|
22435
|
+
}
|
|
22436
|
+
const rows = [];
|
|
22437
|
+
while (endIndex < lines.length) {
|
|
22438
|
+
const line = lines[endIndex];
|
|
22439
|
+
if (!line.includes("|") || !line.trim().startsWith("|")) {
|
|
22440
|
+
break;
|
|
22441
|
+
}
|
|
22442
|
+
rows.push(this.parseTableRow(line));
|
|
22443
|
+
endIndex++;
|
|
22444
|
+
}
|
|
22445
|
+
return {
|
|
22446
|
+
block: { table: { headers, rows } },
|
|
22447
|
+
endIndex: endIndex - 1
|
|
22448
|
+
};
|
|
22449
|
+
}
|
|
22450
|
+
static parseTableRow(line) {
|
|
22451
|
+
return line.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
|
|
22452
|
+
}
|
|
22453
|
+
static parseList(lines, startIndex) {
|
|
22454
|
+
const items = [];
|
|
22455
|
+
let endIndex = startIndex;
|
|
22456
|
+
while (endIndex < lines.length) {
|
|
22457
|
+
const line = lines[endIndex];
|
|
22458
|
+
const match = line.match(/^(\s*)[-*]\s+(.+)$|^(\s*)\d+\.\s+(.+)$/);
|
|
22459
|
+
if (!match) break;
|
|
22460
|
+
items.push(match[2] || match[4]);
|
|
22461
|
+
endIndex++;
|
|
22462
|
+
}
|
|
22463
|
+
return {
|
|
22464
|
+
block: { list: items },
|
|
22465
|
+
endIndex: endIndex - 1
|
|
22466
|
+
};
|
|
22467
|
+
}
|
|
22468
|
+
static parseParagraph(lines, startIndex) {
|
|
22469
|
+
const paragraphLines = [];
|
|
22470
|
+
let endIndex = startIndex;
|
|
22471
|
+
while (endIndex < lines.length) {
|
|
22472
|
+
const line = lines[endIndex];
|
|
22473
|
+
if (line.trim().length === 0) break;
|
|
22474
|
+
if (line.match(/^#{1,6}\s+/)) break;
|
|
22475
|
+
if (line.match(/^```/)) break;
|
|
22476
|
+
if (line.match(/^[-*]\s+/) || line.match(/^\d+\.\s+/)) break;
|
|
22477
|
+
if (line.includes("|") && line.trim().startsWith("|")) break;
|
|
22478
|
+
paragraphLines.push(line);
|
|
22479
|
+
endIndex++;
|
|
22480
|
+
}
|
|
22481
|
+
return {
|
|
22482
|
+
block: { p: paragraphLines.join(" ").trim() },
|
|
22483
|
+
endIndex: endIndex - 1
|
|
22484
|
+
};
|
|
22485
|
+
}
|
|
22486
|
+
};
|
|
22487
|
+
|
|
22488
|
+
// src/toonify-helpers.ts
|
|
22489
|
+
function formatSingleStats(result) {
|
|
22490
|
+
return `Toonify: ${result.originalTokenEstimate} \u2192 ${result.compressedTokenEstimate} tokens (${result.reductionPercent}% reduction)
|
|
22491
|
+
`;
|
|
22492
|
+
}
|
|
22493
|
+
function formatFileStats(filename, result) {
|
|
22494
|
+
return `Toonify [${filename}]: ${result.originalTokenEstimate} \u2192 ${result.compressedTokenEstimate} tokens (${result.reductionPercent}% reduction)
|
|
22495
|
+
`;
|
|
22496
|
+
}
|
|
22497
|
+
function formatAggregateStats(totalOriginal, totalCompressed, fileCount) {
|
|
22498
|
+
const aggregateReduction = totalOriginal > 0 ? Math.max(0, Math.min(100, Math.round((totalOriginal - totalCompressed) / totalOriginal * 100))) : 0;
|
|
22499
|
+
return `Toonify total: ${totalOriginal} \u2192 ${totalCompressed} tokens (${aggregateReduction}% reduction, ${fileCount} files)
|
|
22500
|
+
`;
|
|
22501
|
+
}
|
|
22502
|
+
|
|
22111
22503
|
// src/commands/convert.ts
|
|
22112
22504
|
var VALID_FORMATS = [
|
|
22113
22505
|
"github",
|
|
@@ -22258,7 +22650,28 @@ async function urlToMarkdown(url, apiKey, format, proxyBaseUrl = process.env.FOR
|
|
|
22258
22650
|
});
|
|
22259
22651
|
return result.markdown;
|
|
22260
22652
|
}
|
|
22653
|
+
function applyToonify(markdown) {
|
|
22654
|
+
try {
|
|
22655
|
+
const result = ToonifyService.compress(markdown);
|
|
22656
|
+
process.stderr.write(formatSingleStats(result));
|
|
22657
|
+
return result.compressed;
|
|
22658
|
+
} catch (err) {
|
|
22659
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
22660
|
+
process.stderr.write(`Error: Toonify compression failed: ${message}
|
|
22661
|
+
`);
|
|
22662
|
+
process.exit(1);
|
|
22663
|
+
}
|
|
22664
|
+
}
|
|
22261
22665
|
async function convertCommand(options) {
|
|
22666
|
+
if (options.toonify) {
|
|
22667
|
+
const provider = await resolveEntitlement();
|
|
22668
|
+
if (!provider.hasFeature("token-optimization")) {
|
|
22669
|
+
process.stderr.write(
|
|
22670
|
+
"Error: Token optimization requires a Premium license.\nRun `formatferry auth --license-key <KEY>` to activate your license.\n"
|
|
22671
|
+
);
|
|
22672
|
+
process.exit(1);
|
|
22673
|
+
}
|
|
22674
|
+
}
|
|
22262
22675
|
const format = validateFormat(options.format ?? "github");
|
|
22263
22676
|
if (options.url) {
|
|
22264
22677
|
if (options.input) {
|
|
@@ -22273,7 +22686,14 @@ async function convertCommand(options) {
|
|
|
22273
22686
|
);
|
|
22274
22687
|
process.exit(1);
|
|
22275
22688
|
}
|
|
22276
|
-
|
|
22689
|
+
let markdown2 = await urlToMarkdown(options.url, apiKey, format);
|
|
22690
|
+
if (options.toonify) {
|
|
22691
|
+
if (markdown2.trim()) {
|
|
22692
|
+
markdown2 = applyToonify(markdown2);
|
|
22693
|
+
} else {
|
|
22694
|
+
markdown2 = "";
|
|
22695
|
+
}
|
|
22696
|
+
}
|
|
22277
22697
|
if (options.output) {
|
|
22278
22698
|
writeFileSync2(options.output, markdown2, "utf8");
|
|
22279
22699
|
} else {
|
|
@@ -22324,6 +22744,13 @@ async function convertCommand(options) {
|
|
|
22324
22744
|
}
|
|
22325
22745
|
markdown = await htmlToMarkdown(html, format, applyAtlassian);
|
|
22326
22746
|
}
|
|
22747
|
+
if (options.toonify) {
|
|
22748
|
+
if (markdown.trim()) {
|
|
22749
|
+
markdown = applyToonify(markdown);
|
|
22750
|
+
} else {
|
|
22751
|
+
markdown = "";
|
|
22752
|
+
}
|
|
22753
|
+
}
|
|
22327
22754
|
if (options.output) {
|
|
22328
22755
|
writeFileSync2(options.output, markdown, "utf8");
|
|
22329
22756
|
} else {
|
|
@@ -22379,6 +22806,14 @@ async function batchCommand(options) {
|
|
|
22379
22806
|
);
|
|
22380
22807
|
process.exit(1);
|
|
22381
22808
|
}
|
|
22809
|
+
if (options.toonify) {
|
|
22810
|
+
if (!provider.hasFeature("token-optimization")) {
|
|
22811
|
+
process.stderr.write(
|
|
22812
|
+
"Error: Token optimization requires a Premium license.\nRun `formatferry auth --license-key <KEY>` to activate your license.\n"
|
|
22813
|
+
);
|
|
22814
|
+
process.exit(1);
|
|
22815
|
+
}
|
|
22816
|
+
}
|
|
22382
22817
|
const glob = options.batch;
|
|
22383
22818
|
if (!glob) {
|
|
22384
22819
|
process.stderr.write("Error: --batch <glob> is required\n");
|
|
@@ -22393,9 +22828,29 @@ async function batchCommand(options) {
|
|
|
22393
22828
|
const format = options.format ?? "github";
|
|
22394
22829
|
if (options.outputDir) mkdirSync2(options.outputDir, { recursive: true });
|
|
22395
22830
|
let successful = 0, failed = 0;
|
|
22831
|
+
let totalOriginal = 0, totalCompressed = 0, toonifiedCount = 0;
|
|
22396
22832
|
for (const filePath of files) {
|
|
22397
22833
|
try {
|
|
22398
|
-
|
|
22834
|
+
let markdown = await convertFile(filePath, format);
|
|
22835
|
+
if (options.toonify) {
|
|
22836
|
+
if (markdown.trim()) {
|
|
22837
|
+
try {
|
|
22838
|
+
const result = ToonifyService.compress(markdown);
|
|
22839
|
+
process.stderr.write(formatFileStats(basename(filePath), result));
|
|
22840
|
+
markdown = result.compressed;
|
|
22841
|
+
totalOriginal += result.originalTokenEstimate;
|
|
22842
|
+
totalCompressed += result.compressedTokenEstimate;
|
|
22843
|
+
toonifiedCount++;
|
|
22844
|
+
} catch (err) {
|
|
22845
|
+
process.stderr.write(`Error toonifying ${filePath}: ${err instanceof Error ? err.message : String(err)}
|
|
22846
|
+
`);
|
|
22847
|
+
failed++;
|
|
22848
|
+
continue;
|
|
22849
|
+
}
|
|
22850
|
+
} else {
|
|
22851
|
+
markdown = "";
|
|
22852
|
+
}
|
|
22853
|
+
}
|
|
22399
22854
|
writeFileSync3(getOutputPath(filePath, options.outputDir), markdown, "utf8");
|
|
22400
22855
|
successful++;
|
|
22401
22856
|
} catch (err) {
|
|
@@ -22404,6 +22859,9 @@ async function batchCommand(options) {
|
|
|
22404
22859
|
failed++;
|
|
22405
22860
|
}
|
|
22406
22861
|
}
|
|
22862
|
+
if (options.toonify && toonifiedCount > 0) {
|
|
22863
|
+
process.stderr.write(formatAggregateStats(totalOriginal, totalCompressed, toonifiedCount));
|
|
22864
|
+
}
|
|
22407
22865
|
process.stderr.write(`
|
|
22408
22866
|
Batch complete: ${successful} succeeded, ${failed} failed, ${files.length} total
|
|
22409
22867
|
`);
|
|
@@ -22601,7 +23059,7 @@ function handleLogout() {
|
|
|
22601
23059
|
|
|
22602
23060
|
// src/index.ts
|
|
22603
23061
|
var VERSION = "0.1.0";
|
|
22604
|
-
program.name("formatferry").version(VERSION).description("Convert HTML, DOCX, PDF, XLSX, CSV to Markdown").option("-i, --input <path>", "Input file path").option("-o, --output <path>", "Output file path").option("-f, --format <format>", "Markdown format (github, commonmark, slack, discord, reddit, confluence, custom, rmarkdown)", "github").option("--url <url>", "Fetch article from URL").option("--batch <glob>", "Batch convert files (Premium)").option("--output-dir <dir>", "Output directory for batch").action(async (options) => {
|
|
23062
|
+
program.name("formatferry").version(VERSION).description("Convert HTML, DOCX, PDF, XLSX, CSV to Markdown").option("-i, --input <path>", "Input file path").option("-o, --output <path>", "Output file path").option("-f, --format <format>", "Markdown format (github, commonmark, slack, discord, reddit, confluence, custom, rmarkdown)", "github").option("--url <url>", "Fetch article from URL").option("--batch <glob>", "Batch convert files (Premium)").option("--output-dir <dir>", "Output directory for batch").option("--toonify", "Compress output to TOON format (Premium)").action(async (options) => {
|
|
22605
23063
|
if (options.batch) {
|
|
22606
23064
|
await batchCommand(options);
|
|
22607
23065
|
} else {
|