toon-skill
Централизованный API для конвертации JSON ↔ TOON и расчёта token savings
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Централизованный API для конвертации JSON ↔ TOON и расчёта token savings
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Use when improving ONE numeric metric under a controlled research loop with a fixed eval and kept/reverted experiments. Not for a feature (use loop-delivery) or a failing test (use loop-repair).
Use when delivering ONE bounded change — a feature, refactor, or chore — as a controlled, audited loop in any repo. Not for a failing test (use loop-repair) or a numeric metric (use loop-autoresearch).
Use when a specific test, CI job, or regression is failing and must be fixed under a reproduce-first controlled loop with proven regression coverage. Not for delivering a change (use loop-delivery) or metrics (use loop-autoresearch).
Use when a loen loop stage — plan, act, check, or result — must be validated and gated before the next one. Mode-aware for delivery/repair/research; the execution-loop analog of check-chain.
Use when you need a cross-run dashboard over all docs/loen/ runs, or --triage to turn failing runs into proposed next actions (proposals only; never launches loops or edits runs).
Use when an active, human-approved loen run should keep going multi-turn on its own — wraps it in Claude's native /goal from loop.yaml. Optional; never bootstraps a run or submits /goal itself.
| name | toon-skill |
| description | Централизованный API для конвертации JSON ↔ TOON и расчёта token savings |
| user-invocable | false |
This skill provides a centralized API for JSON ↔ TOON conversion and token savings calculation in the skills communication layer. It uses the @toon-format/toon npm package with name[N]{fields}: syntax. Use this skill when a skill produces tabular data (arrays >= 5 items) for inter-skill communication. For agent pipeline artifacts (research.toon, plan.toon, critique.toon), use RFC-0003 native TOON format instead — see agents/_shared/toon-protocol.md.
Базовый навык для работы с TOON форматом - компактным, человеко-читаемым форматом данных для оптимизации LLM промптов (30-60% token savings).
TOON (Token-Oriented Object Notation) предназначен для:
toon-skill and RFC-0003 both implement TOON but serve different layers with different syntax:
| Layer | Format | Syntax | Parser |
|---|---|---|---|
| Skills layer (this skill) | @toon-format/toon npm | name[N]{field1,field2}: | npm package |
| Agent pipeline layer (RFC-0003) | Native TOON v1 | TOON:name:v1\nfield1|field2\nval1|val2 | Claude native (no npm) |
Rule: Skills that produce output consumed by the agent pipeline (research.toon, plan.toon, critique.toon) MUST use RFC-0003 native format, NOT toon-skill npm syntax.
Reference implementation: agents/_shared/toon-protocol.md
TOON Format Specification:
@shared:TOON-REFERENCE.md@shared:TOON-REFERENCE.md#integration-patterns@shared:TOON-REFERENCE.md#token-savingsAgent Pipeline TOON (RFC-0003 native):
docs/RFC-0003-toon-protocol.mdagents/_shared/toon-protocol.mdTask Structure:
@shared:TASK-STRUCTURE.md#toon-optimizationExternal References:
.nvm-isolated/npm-global/bin/toon)import { jsonToToon, toonToJson, arrayToToon, nestedToToon } from '../toon-skill/converters/toon-converter.mjs';
// Generic JSON → TOON
const toonString = jsonToToon({ components: [...] });
// TOON → JSON
const jsonObj = toonToJson(toonString);
// Specialized: Array → TOON table (PRIMARY API)
const toonTable = arrayToToon('warnings', warningsArray,
['file', 'line', 'severity', 'message']);
// Output: "warnings[15]{file,line,severity,message}:\n src/app.js,42,BLOCKING,SQL injection\n ..."
// Nested arrays (dependency graphs)
const toonGraph = nestedToToon('dependency_graph', {
nodes: { items: nodesArray, fields: ['id', 'label', 'type'] },
edges: { items: edgesArray, fields: ['from', 'to', 'type'] }
});
import { validateToon, roundTripTest } from '../toon-skill/converters/toon-converter.mjs';
// Syntax validation
const result = validateToon(toonString);
if (!result.valid) console.error(result.errors);
// Lossless conversion test
const test = roundTripTest(jsonObj);
if (test.success) console.log('Round-trip successful!');
import { calculateTokenSavings } from '../toon-skill/converters/toon-converter.mjs';
const stats = calculateTokenSavings({ warnings: [...] });
console.log(`JSON: ${stats.jsonTokens} tokens`);
console.log(`TOON: ${stats.toonTokens} tokens`);
console.log(`Saved: ${stats.savedPercent}`); // "43.2%"
Для других skills всегда используйте hybrid approach:
// Step 1: Generate JSON output (always)
const output = {
status: "success",
warnings: [...], // 15 items
blocking_issues: [...] // 2 items
};
// Step 2: Add TOON optimization (если >= 5 элементов)
import { arrayToToon, calculateTokenSavings } from '../toon-skill/converters/toon-converter.mjs';
if (output.warnings.length >= 5) {
const dataToConvert = { warnings: output.warnings };
const stats = calculateTokenSavings(dataToConvert);
output.toon = {
warnings_toon: arrayToToon('warnings', output.warnings,
['category', 'file', 'line', 'severity', 'message', 'suggestion']),
token_savings: stats.savedPercent,
size_comparison: `JSON: ${stats.jsonTokens} tokens, TOON: ${stats.toonTokens} tokens`
};
}
// blocking_issues не конвертируем (< 5 элементов)
// Step 3: Return hybrid output
return output;
Result structure:
{
"status": "success",
"warnings": [...], // JSON (всегда присутствует)
"blocking_issues": [...], // JSON (всегда присутствует)
"toon": { // TOON (опционально, если >= 5 элементов)
"warnings_toon": "warnings[15]{category,file,line,severity,message,suggestion}:\n ...",
"token_savings": "43.2%",
"size_comparison": "JSON: 3450 tokens, TOON: 1960 tokens"
}
}
Преимущества:
arrayName[N]{field1,field2,field3}:
value1_1,value1_2,value1_3
value2_1,value2_2,value2_3
Правила:
[N] - точное количество элементов (валидация){fields} - схема (названия полей): - начало табличных данных"value, with comma"Пример:
warnings[3]{file,line,severity,message}:
src/app.js,42,BLOCKING,SQL injection vulnerability
src/db.js,15,WARNING,Missing database index
src/api.js,78,INFO,Consider async/await refactoring
parent:
child1[2]{id,name}:
1,Alice
2,Bob
child2[3]{id,status}:
1,active
2,inactive
3,pending
Пример (dependency graph):
dependency_graph:
nodes[2]{id,label,type,layer}:
proxy-mgmt,Proxy Management,module,infrastructure
oauth-handler,OAuth Handler,function,business
edges[1]{from,to,type,description}:
proxy-mgmt,oauth-handler,required,Requires OAuth for authenticated proxies
// In code-review skill
import { arrayToToon, calculateTokenSavings } from '../toon-skill/converters/toon-converter.mjs';
const codeReview = {
score: 85,
blocking_issues: findBlockingIssues(), // Usually < 5, no TOON
warnings: findWarnings(), // Usually 5-20, TOON candidate
lsp_diagnostics: getLspDiagnostics() // Usually 10-50, TOON candidate
};
// Add TOON optimization
const dataToConvert = {};
if (codeReview.warnings.length >= 5) {
codeReview.toon = codeReview.toon || {};
codeReview.toon.warnings_toon = arrayToToon('warnings', codeReview.warnings,
['category', 'file', 'line', 'severity', 'message', 'suggestion']);
dataToConvert.warnings = codeReview.warnings;
}
if (codeReview.lsp_diagnostics && codeReview.lsp_diagnostics.length >= 5) {
codeReview.toon = codeReview.toon || {};
codeReview.toon.lsp_diagnostics_toon = arrayToToon('lsp_diagnostics', codeReview.lsp_diagnostics,
['file', 'line', 'severity', 'code', 'message']);
dataToConvert.lsp_diagnostics = codeReview.lsp_diagnostics;
}
if (codeReview.toon) {
const stats = calculateTokenSavings(dataToConvert);
codeReview.toon.token_savings = stats.savedPercent;
codeReview.toon.size_comparison = `JSON: ${stats.jsonTokens} tokens, TOON: ${stats.toonTokens} tokens`;
}
return { code_review: codeReview };
// In structured-planning skill
import { arrayToToon, calculateTokenSavings } from '../toon-skill/converters/toon-converter.mjs';
const taskPlan = {
task_summary: "Implement user authentication",
execution_steps: [...], // 7 steps
acceptance_criteria: [...], // 5 criteria
files_to_change: [...] // 12 files
};
// Add TOON optimization
if (taskPlan.execution_steps.length >= 5) {
taskPlan.toon = {
execution_steps_toon: arrayToToon('execution_steps', taskPlan.execution_steps,
['step_number', 'description', 'validation']),
token_savings: calculateTokenSavings({ execution_steps: taskPlan.execution_steps }).savedPercent
};
}
if (taskPlan.files_to_change.length >= 5) {
taskPlan.toon = taskPlan.toon || {};
taskPlan.toon.files_to_change_toon = arrayToToon('files_to_change', taskPlan.files_to_change,
['file_path', 'change_type', 'description']);
}
return { task_plan: taskPlan };
// In pr-automation skill
import { arrayToToon, calculateTokenSavings } from '../toon-skill/converters/toon-converter.mjs';
const prResult = {
pr_url: "https://github.com/...",
checks: [...], // 8 checks
autoFixedErrors: [...], // 12 fixes
commits: [...] // 15 commits
};
// Add TOON for all arrays >= 5
const dataToConvert = {};
['checks', 'autoFixedErrors', 'commits'].forEach(arrayName => {
if (prResult[arrayName].length >= 5) {
prResult.toon = prResult.toon || {};
const fields = {
'checks': ['name', 'status', 'duration_ms', 'details_url'],
'autoFixedErrors': ['file', 'line', 'error_type', 'fix_applied'],
'commits': ['hash', 'author', 'message', 'timestamp']
};
prResult.toon[`${arrayName}_toon`] = arrayToToon(arrayName, prResult[arrayName], fields[arrayName]);
dataToConvert[arrayName] = prResult[arrayName];
}
});
if (prResult.toon) {
const stats = calculateTokenSavings(dataToConvert);
prResult.toon.token_savings = stats.savedPercent;
prResult.toon.size_comparison = `JSON: ${stats.jsonTokens} tokens, TOON: ${stats.toonTokens} tokens`;
}
return prResult;
// In adaptive-workflow skill
import { arrayToToon, calculateTokenSavings } from '../toon-skill/converters/toon-converter.mjs';
const complexityResult = {
level: "complex",
workflow: "phase-based",
complexity_factors: [
{factor_id: 1, factor_name: "Files to change", value: 18, threshold: 5, weight: 0.30, impact: "high", contributes_to: "complex"},
{factor_id: 2, factor_name: "Components", value: 5, threshold: 2, weight: 0.20, impact: "high", contributes_to: "complex"},
// ... 8 total factors
],
complexity_score: 0.97
};
// Add TOON optimization (only if complexity_factors >= 5)
if (complexityResult.complexity_factors.length >= 5) {
// Normalize boolean values to strings for TOON
const factorsNormalized = complexityResult.complexity_factors.map(f => ({
factor_id: f.factor_id,
factor_name: f.factor_name,
value: typeof f.value === 'boolean' ? f.value.toString() : f.value,
threshold: typeof f.threshold === 'boolean' ? f.threshold.toString() : f.threshold,
weight: f.weight,
impact: f.impact,
contributes_to: f.contributes_to
}));
complexityResult.toon = {
complexity_factors_toon: arrayToToon('complexity_factors', factorsNormalized,
['factor_id', 'factor_name', 'value', 'threshold', 'weight', 'impact', 'contributes_to']),
...calculateTokenSavings({ complexity_factors: factorsNormalized })
};
}
return { complexity_result: complexityResult };
Output (with TOON):
{
"complexity_result": {
"level": "complex",
"complexity_factors": [...], // JSON (8 items)
"toon": {
"complexity_factors_toon": "complexity_factors[8]{factor_id,factor_name,value,threshold,weight,impact,contributes_to}:\n 1,Files to change,18,5,0.30,high,complex\n 2,Components,5,2,0.20,high,complex\n ...",
"token_savings": "28.0%",
"size_comparison": "JSON: 1680 tokens, TOON: 1210 tokens"
}
}
}
// In phase-execution skill
import { arrayToToon, calculateTokenSavings } from '../toon-skill/converters/toon-converter.mjs';
const checkpoint = {
checkpoint_id: 1,
checkpoint_name: "ЗАГРУЗКА И АНАЛИЗ",
checks: [
{check_id: 1, check_name: "Phase file read", status: "passed", details: "plans/phase-2.md (127 lines)"},
{check_id: 2, check_name: "Metadata parsed", status: "passed", details: "JSON valid"},
// ... 5+ checks
],
overall_result: "PASSED"
};
// Add TOON optimization
if (checkpoint.checks.length >= 5) {
checkpoint.toon = {
checks_toon: arrayToToon('checks', checkpoint.checks,
['check_id', 'check_name', 'status', 'details']),
...calculateTokenSavings({ checks: checkpoint.checks })
};
}
const phaseSummary = {
phase_number: 2,
status: "COMPLETED",
files_changed: [
{file: "services/jwt_service.py", change_type: "create", lines_added: 45, lines_removed: 0},
{file: "api/v1/endpoints/auth.py", change_type: "create", lines_added: 78, lines_removed: 0},
// ... 7+ files
]
};
// Add TOON optimization
if (phaseSummary.files_changed.length >= 5) {
// Normalize lines_removed field (default to 0)
const filesNormalized = phaseSummary.files_changed.map(f => ({
file: f.file,
change_type: f.change_type,
lines_added: f.lines_added,
lines_removed: f.lines_removed || 0
}));
phaseSummary.toon = {
files_changed_toon: arrayToToon('files_changed', filesNormalized,
['file', 'change_type', 'lines_added', 'lines_removed']),
...calculateTokenSavings({ files_changed: filesNormalized })
};
}
return { checkpoint, phase_summary: phaseSummary };
Token savings:
// In task-decomposition skill
import { nestedToToon, calculateTokenSavings } from '../toon-skill/converters/toon-converter.mjs';
const masterPlan = {
task_name: "Implement JWT authentication",
total_phases: 3,
phases: [
{phase_id: 1, phase_name: "Database Models", files: ["models/user.py", "models/refresh_token.py"], dependencies: []},
{phase_id: 2, phase_name: "Backend API", files: ["services/jwt_service.py", "api/auth.py"], dependencies: [1]},
{phase_id: 3, phase_name: "Frontend", files: ["LoginForm.tsx", "api/auth.ts"], dependencies: [2]}
]
};
// Add TOON optimization (only if phases >= 5)
if (masterPlan.phases.length >= 5) {
// For task-decomposition, usually 2-5 phases, so threshold rarely met
// But when met (complex multi-phase projects):
masterPlan.toon = {
phases_toon: arrayToToon('phases', masterPlan.phases,
['phase_id', 'phase_name', 'files', 'dependencies']),
...calculateTokenSavings({ phases: masterPlan.phases })
};
}
return { master_plan: masterPlan };
Note: Task-decomposition обычно генерирует 2-5 фаз, поэтому TOON threshold (>= 5) редко достигается. Это правильно - для небольших планов JSON более читаем.
Quick Reference (First 3 Use Cases)
| Use Case | Array Size | JSON Tokens | TOON Tokens | Savings |
|---|---|---|---|---|
| Components (architecture-documentation) | 6 items | 202 | 123 | 39.1% |
| Dependency Graph (architecture-documentation) | 4 nodes + 6 edges | 223 | 114 | 48.9% |
| Code Review Warnings (code-review) | 15 items | 450 | 260 | 42.2% |
(See TOON block below for complete 9-benchmark catalog)
Complete Benchmarks (TOON)
benchmarks[9]{use_case,array_size,json_tokens,toon_tokens,savings}:
Components (architecture-documentation),6 items,202,123,39.1%
Dependency Graph (architecture-documentation),4 nodes + 6 edges,223,114,48.9%
Code Review Warnings (code-review),15 items,450,260,42.2%
Execution Steps (structured-planning),10 items,380,220,42.1%
PR Checks (pr-automation),8 items,290,175,39.7%
LSP Diagnostics (code-review),50 items,2100,1050,50.0%
Complexity Factors (adaptive-workflow),8 items,1680,1210,28.0%
Checkpoint Checks (phase-execution),6 items,1012,685,32.3%
Files Changed (phase-execution),12 items,2120,1319,37.8%
Usage: Reference these benchmarks when estimating token savings for new TOON implementations.
Aggregate savings для typical workflow:
Если ваш skill получает output от другого skill с TOON:
import { toonToJson } from '../toon-skill/converters/toon-converter.mjs';
// Input: hybrid output from upstream skill
const upstreamOutput = {
items: [...], // JSON (всегда доступен)
toon: {
items_toon: "items[15]{...}:\n ..." // TOON (опционально)
}
};
// Strategy 1: Always use JSON (safest, backward compatible)
const items = upstreamOutput.items;
// Strategy 2: Prefer TOON if available (token efficient)
const items = upstreamOutput.toon?.items_toon
? toonToJson(upstreamOutput.toon.items_toon).items
: upstreamOutput.items;
// Strategy 3: Use TOON for validation только
if (upstreamOutput.toon?.items_toon) {
const toonItems = toonToJson(upstreamOutput.toon.items_toon).items;
// Validate consistency
assert.deepStrictEqual(toonItems, upstreamOutput.items);
}
A: Проверьте, что все поля имеют consistent типы и schema. TOON требует uniform структуру.
// ❌ Bad: inconsistent schema
const items = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob', age: 30 } // 'age' only in second item
];
// ✅ Good: consistent schema
const items = [
{ id: 1, name: 'Alice', age: null },
{ id: 2, name: 'Bob', age: 30 }
];
A: TOON наиболее эффективен для >= 10 элементов, табличных структур. Для 5-9 элементов экономия 25-35%.
A: Используйте validateToon() для проверки синтаксиса:
const result = validateToon(toonString);
if (!result.valid) {
console.error('Invalid TOON:', result.error);
}
A: arrayToToon() автоматически quotes значения с запятыми:
const items = [
{ file: 'app.js', message: 'Error: invalid input, check validation' }
];
const toon = arrayToToon('items', items, ['file', 'message']);
// Output: items[1]{file,message}:
// app.js,"Error: invalid input, check validation"
A: Да, добавьте optional toon field используя $ref: "@shared:TASK-STRUCTURE.md#toon-optimization":
{
"type": "object",
"properties": {
"status": { "enum": ["success", "failed"] },
"warnings": { "type": "array" },
"toon": {
"$ref": "../_shared/base-schema.json#/definitions/toon_optimization"
}
},
"required": ["status", "warnings"]
}
toon-skill/
├── SKILL.md # Этот файл
├── converters/
│ ├── toon-converter.mjs # Main API (generic + specialized converters)
│ └── README.md # API documentation
├── templates/
│ └── hybrid-output.json # Шаблон JSON + TOON output
├── examples/
│ ├── array-conversion.example # Примеры конвертации массивов
│ ├── nested-objects.example # Примеры вложенных структур
│ ├── hybrid-output.example # Пример hybrid output
│ └── integration-guide.md # Руководство по интеграции в другие skills
├── schemas/
│ └── toon-output.schema.json # JSON Schema для toon field
└── tests/
├── round-trip.test.mjs # Тесты lossless конвертации
└── token-savings.test.mjs # Тесты расчёта экономии
TOON converter можно использовать через command line:
# Convert JSON file to TOON
node converters/toon-converter.mjs encode input.json
# Convert TOON file to JSON
node converters/toon-converter.mjs decode input.toon
# Run round-trip test
node converters/toon-converter.mjs test input.json
# Show token savings statistics
node converters/toon-converter.mjs stats input.json
См. актуальный список в:
@shared:TOON-REFERENCE.md - Integration patterns../README.md - Skills status matrix с TOON supportHigh Priority Skills (с TOON интеграцией):
Legend:
MIT License
Разработан командой Claude Code для оптимизации inter-skill коммуникации и снижения token costs.
../_shared/TOON-PATTERNS.md → @shared:TOON-REFERENCE.mdarrayToToon(), nestedToToon()componentsToToon(), dependencyGraphToToon(), edgesToToon()calculateTokenSavings(), validateToon(), roundTripTest()