tonedial 0.1.3 → 0.1.5

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.
@@ -1,7 +1,7 @@
1
1
  // Decode known current and historical English artifact bodies. Unknown prose is never a data source.
2
- import { ANCHORS, AXIS_ORDER, CONTROLS, FORMAT_COMPOSITION, PERSONA, REGISTER_COMPOSITION, SCOPE_HEADER, SPEECH_LEVEL, } from "./anchors.js";
2
+ import { ANCHORS, AXIS_DEFAULT, AXIS_ORDER, CONTROLS, FORMAT_COMPOSITION, PERSONA, REGISTER_COMPOSITION, SCOPE_HEADER, SPEECH_LEVEL, } from "./anchors.js";
3
3
  import { AGE_BANDS, GENDERS, defaultConfig, recoverSettings, safewordRejection, } from "./config.js";
4
- import { composites } from "./combos.js";
4
+ import { composites, intensityConfirmation } from "./combos.js";
5
5
  // Historical producer text is decoder evidence only, never a current tone instruction.
6
6
  // Keep the finite pre-redesign body set so an older TONE.md can still recover its values after
7
7
  // the current anchor wording changes.
@@ -91,9 +91,114 @@ const LEGACY_CURRENT_ANCHORS = {
91
91
  "Make boasting, credit-claiming, and overplayed suffering the default register.",
92
92
  ],
93
93
  };
94
+ // Exact 0.1.4 producer text. Current artifacts changed several axes, composites and rough
95
+ // register wording together, so decoding must not derive this evidence from today's owners.
96
+ const RELEASED_0_1_4_ANCHORS = {
97
+ formality: [
98
+ "Use familiar wording, contractions, and slang.",
99
+ "Use relaxed conversational wording with occasional formality.",
100
+ "Use standard conversational wording.",
101
+ "Use professional, restrained wording throughout.",
102
+ "Use ceremonial, deferential wording throughout.",
103
+ ],
104
+ verbosity: [
105
+ "Use one or two short sentences for a simple result, or terse fragments for items; omit rationale, process, and background.",
106
+ "Give the conclusion and one short reason.",
107
+ "Give the conclusion with the explanation it needs.",
108
+ "Give the conclusion, its reason, and one concrete example.",
109
+ "Explain the requested answer in depth with rationale, edge cases, alternatives, and background; do not broaden the task.",
110
+ ],
111
+ profanity: [
112
+ "Use no profanity; allow role slang and metaphors.",
113
+ "Use at most one mild swear as an interjection per answer.",
114
+ "Use mild swearing at most once per paragraph.",
115
+ "Use coarse language up to twice per answer.",
116
+ "Use strong profanity throughout the answer for emphasis.",
117
+ ],
118
+ playfulness: [
119
+ "Use literal wording, no jokes or role idioms; keep the configured address.",
120
+ "Add one light joke, analogy, or restrained role expression per answer.",
121
+ "Use two role idioms per answer, or two playful touches without a role.",
122
+ "Use exaggerated role idioms, slang and metaphors in most paragraphs; without a role, use frequent playful touches.",
123
+ "Make the character voice flamboyant; use its idioms, slang and metaphors in nearly every sentence. Without a role, use exuberant playful language.",
124
+ ],
125
+ critique: [
126
+ "State defects as neutral observations with no judgement.",
127
+ "Name defects in softened language and describe their impact.",
128
+ "Point out each defect directly and state its impact.",
129
+ "Label each defect's severity and state the consequence plainly.",
130
+ "Condemn severe defects in vivid, unsparing language.",
131
+ ],
132
+ flattery: [
133
+ "Praise nothing.",
134
+ "Include one brief positive remark about the user's effort per answer.",
135
+ "Include two specific positive remarks about the user's effort per answer.",
136
+ "Include three specific positive remarks about the user's effort per answer.",
137
+ "Include four positive remarks about the user's effort per answer, even for small choices.",
138
+ ],
139
+ warmth: [
140
+ "Use no greeting, warm acknowledgement, or encouragement.",
141
+ "Use one friendly acknowledgement per answer.",
142
+ "Use two warm acknowledgements per answer.",
143
+ "Use three warm acknowledgements per answer, including encouragement.",
144
+ "Use four warm acknowledgements per answer, including encouragement and empathy.",
145
+ ],
146
+ conviction: [
147
+ "List the viable options and ask the user to choose.",
148
+ "Name one preferred option, but ask before deciding.",
149
+ "Recommend one option and give its reason.",
150
+ "Lead with one clear recommendation and mark it as your choice.",
151
+ "State one recommendation as the decision and ask no follow-up question.",
152
+ ],
153
+ care: [
154
+ "Return only what was requested; add no related checks, context, or follow-up.",
155
+ "Add one risk that is directly tied to the requested task.",
156
+ "Organize the requested task into clear steps or a checklist.",
157
+ "Add one adjacent improvement or test that protects the requested result.",
158
+ "Cover relevant follow-up, backup, and the user's rest; use brief status markers, lists, tables, or emoji when they fit.",
159
+ ],
160
+ compliance: [
161
+ "Question the request once, then carry it out.",
162
+ "Add one reluctant remark, then carry it out.",
163
+ "Carry out the request directly with no resistance.",
164
+ "Acknowledge the request and start carrying it out immediately.",
165
+ "Carry out the request immediately and omit commentary about complying.",
166
+ ],
167
+ refusal: [
168
+ "Do not perform a refusal or delay for effect.",
169
+ "Push back once per session, then proceed after one restatement.",
170
+ "State one condition before proceeding, up to twice per session.",
171
+ "Negotiate conditions up to three times per session, and proceed after two requests.",
172
+ "Declare a strike until coaxed; the safeword drops the act for this reply only.",
173
+ ],
174
+ bravado: [
175
+ "Do not exaggerate effort or claim credit.",
176
+ "Make one playful overstatement about your effort per answer.",
177
+ "Claim credit once and make one boast per answer.",
178
+ "Boast and claim credit twice per answer, with one dramatic effort remark.",
179
+ "Use a boastful expert persona by default; claim credit and dramatize effort in every answer.",
180
+ ],
181
+ };
182
+ const RELEASED_0_1_4_COMPOSITES = [
183
+ { fires: (a) => a.warmth >= 4 && a.profanity >= 4, line: "Character: use affectionate swearing." },
184
+ { fires: (a) => a.compliance <= 2 && a.conviction <= 2, line: "Grumble without being passive; make the complaint itself clear." },
185
+ { fires: (a) => a.care === 5, line: "Character: be a caring organizer; keep the user oriented with brief status markers and a clear next step." },
186
+ { fires: (a) => a.playfulness >= 4 && a.bravado >= 4, line: "Character: be a playful, cocky expert; add confident wit while keeping the technical answer clear." },
187
+ { fires: (a) => a.verbosity <= 2 && a.care >= 4, line: "When verbosity is low and care is high, keep organized items compact; use functional emoji or status markers when they fit." },
188
+ { fires: (a) => a.verbosity === 5 && a.care === 1, line: "When verbosity is high and care is minimal, give depth inside the requested task without adding adjacent work." },
189
+ ];
190
+ const RELEASED_0_1_4_SPEECH_LEVEL = {
191
+ ...SPEECH_LEVEL,
192
+ rough: "In languages with honorifics, use rough, cheeky non-honorific sentence endings; keep the configured address terms and the selected formality, warmth, profanity and playfulness levels.",
193
+ };
194
+ const PREVIOUS_SPEECH_LEVEL = {
195
+ auto: RELEASED_0_1_4_SPEECH_LEVEL.auto,
196
+ polite: RELEASED_0_1_4_SPEECH_LEVEL.polite,
197
+ plain: RELEASED_0_1_4_SPEECH_LEVEL.plain,
198
+ };
94
199
  const PREVIOUS_ANCHORS = {
95
- ...ANCHORS,
96
- profanity: ["Use no profanity and soften even interjections.", ...ANCHORS.profanity.slice(1)],
200
+ ...RELEASED_0_1_4_ANCHORS,
201
+ profanity: ["Use no profanity and soften even interjections.", ...RELEASED_0_1_4_ANCHORS.profanity.slice(1)],
97
202
  playfulness: [
98
203
  "Use no jokes, wordplay, or playful asides.",
99
204
  "Add one light analogy or piece of wordplay per answer.",
@@ -101,8 +206,8 @@ const PREVIOUS_ANCHORS = {
101
206
  "Add two playful touches per answer, including a joke or meme.",
102
207
  "Use playful roleplay or character voice as the default register.",
103
208
  ],
104
- warmth: ["Use no greeting, emotional language, or encouragement.", ...ANCHORS.warmth.slice(1)],
105
- bravado: ["Do not exaggerate.", ...ANCHORS.bravado.slice(1)],
209
+ warmth: ["Use no greeting, emotional language, or encouragement.", ...RELEASED_0_1_4_ANCHORS.warmth.slice(1)],
210
+ bravado: ["Do not exaggerate.", ...RELEASED_0_1_4_ANCHORS.bravado.slice(1)],
106
211
  };
107
212
  const PREVIOUS_REGISTER_COMPOSITION = "Keep the chosen address forms; formality changes distance within them, while warmth and profanity keep their selected effects.";
108
213
  const OLD_ANCHORS = {
@@ -274,62 +379,94 @@ export function recoverLegacyTone(text, t) {
274
379
  if (controlsAt === -1)
275
380
  return null;
276
381
  const rules = body.slice("### Rules\n".length, controlsAt).split("\n");
277
- const anchorCandidates = historical ? [OLD_ANCHORS] : styleMarker ? [ANCHORS, PREVIOUS_ANCHORS] : [ANCHORS, PREVIOUS_ANCHORS, LEGACY_CURRENT_ANCHORS];
278
- const anchors = anchorCandidates.find((candidate) => AXIS_ORDER.every((axis, position) => candidate[axis].some((anchor) => rules[position] === `- ${anchor}`)));
279
- if (anchors === undefined)
280
- return null;
281
- const speechLevels = historical ? OLD_SPEECH_LEVEL : SPEECH_LEVEL;
282
- for (const [position, axis] of AXIS_ORDER.entries()) {
283
- const matches = anchors[axis].map((anchor, index) => ({ anchor, level: index + 1 }))
284
- .filter(({ anchor }) => rules[position] === `- ${anchor}`);
285
- if (matches.length !== 1)
286
- return null;
287
- config.axes[axis] = matches[0].level;
288
- }
289
- if (styleMarker && (Number(styleMarker[1]) !== config.axes.verbosity || Number(styleMarker[2]) !== config.axes.care))
290
- return null;
291
- const speech = rules.at(-1);
292
- config.speech_level = anchors === ANCHORS && styleMarker !== null && speech === `- ${SPEECH_LEVEL.rough}` ? "rough"
293
- : speech === `- ${speechLevels.polite}` ? "polite"
294
- : speech === `- ${speechLevels.plain}` ? "plain" : "auto";
295
- const speechRule = config.speech_level === "rough" ? SPEECH_LEVEL.rough : speechLevels[config.speech_level];
296
- const expectedRules = [
297
- ...AXIS_ORDER.map((axis) => `- ${anchors[axis][config.axes[axis] - 1]}`),
298
- ...(historical ? [
299
- ...(config.axes.warmth >= 4 && config.axes.profanity >= 4
300
- ? ["Keep the swearing affectionate and aimed at the situation only."] : []),
301
- ...(config.axes.compliance <= 2 && config.axes.conviction <= 2
302
- ? ["Grumble without being passive; make the complaint itself clear."] : []),
303
- ] : composites(config.axes)).map((line) => `- ${line}`),
304
- ...(historical ? [] : [`- ${FORMAT_COMPOSITION}`, `- ${anchors === ANCHORS ? REGISTER_COMPOSITION : PREVIOUS_REGISTER_COMPOSITION}`]),
305
- ...(speechRule === null ? [] : [`- ${speechRule}`]),
382
+ const oldComposites = (axes) => [
383
+ ...(axes.warmth >= 4 && axes.profanity >= 4
384
+ ? ["Keep the swearing affectionate and aimed at the situation only."] : []),
385
+ ...(axes.compliance <= 2 && axes.conviction <= 2
386
+ ? ["Grumble without being passive; make the complaint itself clear."] : []),
306
387
  ];
307
- const legacyExpectedRules = [
308
- ...AXIS_ORDER.map((axis) => `- ${LEGACY_CURRENT_ANCHORS[axis][config.axes[axis] - 1]}`),
309
- ...LEGACY_CURRENT_COMPOSITES.filter((_line, index) => index === 0
310
- ? config.axes.warmth >= 4 && config.axes.profanity >= 4
311
- : config.axes.compliance <= 2 && config.axes.conviction <= 2).map((line) => `- ${line}`),
312
- `- ${PREVIOUS_REGISTER_COMPOSITION}`,
313
- ...(speechRule === null ? [] : [`- ${speechRule}`]),
388
+ const releasedComposites = (axes) => RELEASED_0_1_4_COMPOSITES
389
+ .filter(({ fires }) => fires(axes)).map(({ line }) => line);
390
+ const legacyComposites = (axes) => LEGACY_CURRENT_COMPOSITES
391
+ .filter((_line, index) => index === 0
392
+ ? axes.warmth >= 4 && axes.profanity >= 4
393
+ : axes.compliance <= 2 && axes.conviction <= 2).slice();
394
+ const producers = historical ? [{
395
+ current: false, anchors: OLD_ANCHORS, composites: oldComposites,
396
+ speech: OLD_SPEECH_LEVEL, register: null, format: false,
397
+ }] : [
398
+ { current: true, anchors: ANCHORS, composites, speech: SPEECH_LEVEL, register: REGISTER_COMPOSITION, format: true },
399
+ { current: false, anchors: RELEASED_0_1_4_ANCHORS, composites: releasedComposites,
400
+ speech: RELEASED_0_1_4_SPEECH_LEVEL, register: REGISTER_COMPOSITION, format: true },
401
+ { current: false, anchors: PREVIOUS_ANCHORS, composites: releasedComposites,
402
+ speech: PREVIOUS_SPEECH_LEVEL, register: PREVIOUS_REGISTER_COMPOSITION, format: true },
403
+ ...(!styleMarker ? [{ current: false, anchors: LEGACY_CURRENT_ANCHORS, composites: legacyComposites,
404
+ speech: PREVIOUS_SPEECH_LEVEL, register: PREVIOUS_REGISTER_COMPOSITION, format: false }] : []),
314
405
  ];
315
- const acceptedRules = historical || styleMarker ? [expectedRules] : [expectedRules, legacyExpectedRules];
316
- if (!acceptedRules.some((candidate) => rules.length === candidate.length &&
317
- rules.every((line, index) => line === candidate[index])))
318
- return null;
319
406
  const controls = body.slice(controlsAt + heading.length).replace(/\n$/, "");
320
- const remaining = historical && controls.startsWith(OLD_GUARD_BUNDLE)
321
- ? controls.slice(OLD_GUARD_BUNDLE.length) : controls;
322
- const safeword = /^- Safeword: when the user's message contains "([^"\n]+)" or a near-identical rewording,\n/.exec(remaining);
323
- config.safeword = safeword?.[1] ?? null;
324
- if (config.safeword !== null && safewordRejection(config.safeword, t) !== null)
325
- return null;
326
- const tail = `${historical ? `- ${OLD_EMERGENCY}\n` : ""}- ${CONTROLS.fallback}`;
327
- const known = config.safeword === null ? [tail] : [
328
- `- ${CONTROLS.safeword(config.safeword)}\n${tail}`,
329
- ...(historical ? [`- ${oldSafeword(config.safeword)}\n${tail}`] : []),
330
- ];
331
- if (!known.includes(remaining))
407
+ let selected = null;
408
+ for (const producer of producers) {
409
+ const axes = { ...AXIS_DEFAULT };
410
+ let recognized = true;
411
+ for (const [position, axis] of AXIS_ORDER.entries()) {
412
+ const matches = producer.anchors[axis].map((anchor, index) => ({ anchor, level: index + 1 }))
413
+ .filter(({ anchor }) => rules[position] === `- ${anchor}`);
414
+ if (matches.length !== 1) {
415
+ recognized = false;
416
+ break;
417
+ }
418
+ axes[axis] = matches[0].level;
419
+ }
420
+ if (!recognized || styleMarker && (Number(styleMarker[1]) !== axes.verbosity || Number(styleMarker[2]) !== axes.care))
421
+ continue;
422
+ const speechLine = rules.at(-1);
423
+ const speech = styleMarker && producer.speech.rough !== undefined && speechLine === `- ${producer.speech.rough}` ? "rough"
424
+ : speechLine === `- ${producer.speech.polite}` ? "polite"
425
+ : speechLine === `- ${producer.speech.plain}` ? "plain" : "auto";
426
+ const speechRule = speech === "rough" ? producer.speech.rough : producer.speech[speech];
427
+ const expectedRules = [
428
+ ...AXIS_ORDER.map((axis) => `- ${producer.anchors[axis][axes[axis] - 1]}`),
429
+ ...producer.composites(axes).map((line) => `- ${line}`),
430
+ ...(producer.format ? [`- ${FORMAT_COMPOSITION}`] : []),
431
+ ...(producer.register === null ? [] : [`- ${producer.register}`]),
432
+ ...(speechRule == null ? [] : [`- ${speechRule}`]),
433
+ ];
434
+ if (rules.length !== expectedRules.length || !rules.every((line, index) => line === expectedRules[index]))
435
+ continue;
436
+ let remaining = historical && controls.startsWith(OLD_GUARD_BUNDLE)
437
+ ? controls.slice(OLD_GUARD_BUNDLE.length) : controls;
438
+ if (producer.current) {
439
+ const prefix = `- ${CONTROLS.intensityConsent}\n`;
440
+ if (!remaining.startsWith(prefix))
441
+ continue;
442
+ remaining = remaining.slice(prefix.length);
443
+ const confirmation = intensityConfirmation(axes);
444
+ if (confirmation !== null) {
445
+ const confirmationLine = `- ${confirmation}\n`;
446
+ if (!remaining.startsWith(confirmationLine))
447
+ continue;
448
+ remaining = remaining.slice(confirmationLine.length);
449
+ }
450
+ }
451
+ const safeword = /^- Safeword: when the user's message contains "([^"\n]+)" or a near-identical rewording,\n/.exec(remaining)?.[1] ?? null;
452
+ if (safeword !== null && safewordRejection(safeword, t) !== null)
453
+ continue;
454
+ const boundary = producer.current ? `- ${CONTROLS.intensityBoundary}\n` : "";
455
+ const tail = `${historical ? `- ${OLD_EMERGENCY}\n` : ""}${boundary}- ${CONTROLS.fallback}`;
456
+ const known = safeword === null ? [tail] : [
457
+ `- ${CONTROLS.safeword(safeword)}\n${tail}`,
458
+ ...(historical ? [`- ${oldSafeword(safeword)}\n${tail}`] : []),
459
+ ];
460
+ if (!known.includes(remaining))
461
+ continue;
462
+ selected = { axes, speech, safeword };
463
+ break;
464
+ }
465
+ if (selected === null)
332
466
  return null;
467
+ config.axes = selected.axes;
468
+ config.speech_level = selected.speech;
469
+ config.safeword = selected.safeword;
333
470
  config.preset = marker[1] === "unsaved" ? null : marker[1];
334
471
  config.unsaved = marker[1] === "unsaved";
335
472
  const parsed = recoverSettings(JSON.stringify(config), t);
@@ -115,7 +115,11 @@ const messages = {
115
115
  rollbackOrder: "Restaura primero las conexiones y después los archivos de la aplicación:",
116
116
  noSnapshot: (id) => `${id}: no hay una instantánea válida para este directorio de estado`,
117
117
  retire: (id) => `Archiva la operación detenida ${id} antes de reconstruir. Sus cambios externos no se certifican ni se deshacen.`,
118
- failure: (id) => `La recuperación no terminó. Conserva la operación ${id} y ejecuta tonedial doctor para ver las opciones.`,
118
+ resumeOperation: (id) => `Operación detenida:\n${id}`,
119
+ resumeQuestion: "Para continuar, escribe el ID completo de arriba; Enter cancela:",
120
+ cancelled: "Recuperación cancelada. No se cambió ningún archivo.",
121
+ operationMismatch: "El ID de operación no coincide. No se cambió ningún archivo.",
122
+ failure: (id, phase, path, code) => `La recuperación falló durante la ${({ prepare: "preparación", write: "escritura de archivos", verify: "verificación", finalize: "finalización" })[phase]} (${code}).${path ? `\n${path}` : ""}\nEs posible que algunos cambios ya estén aplicados. Conserva el registro de la operación ${id}.`,
119
123
  },
120
124
  persona: {
121
125
  defaults: role("yo", "tú", "La persona que solicita ayuda con una tarea.", "Un asistente general que ayuda con la tarea solicitada."),
@@ -154,7 +158,7 @@ const messages = {
154
158
  safewordShort: (phrase) => `Palabra clave ${phrase}: demasiado corta; podría activarse por accidente`,
155
159
  safewordSet: (phrase) => `Palabra clave ${phrase}`,
156
160
  noSafeword: "sin palabra clave",
157
- register: (level) => `registro ${level} (${({ auto: "según la relación", polite: "formas honoríficas", plain: "formas normales sin honoríficos", rough: "formas bruscas sin honoríficos" })[level]})`,
161
+ register: (level) => `registro ${level} (${({ auto: "según la relación", polite: "formas honoríficas", plain: "formas normales sin honoríficos", rough: "formas no honoríficas cortas y tajantes" })[level]})`,
158
162
  },
159
163
  artifact: {
160
164
  size: (chars) => `TONE.md ${chars} caracteres`,
@@ -264,7 +268,8 @@ const messages = {
264
268
  seedsOk: (version) => `versión ${version}`,
265
269
  seedsNotRun: "sin archivos incluidos · aquí no se ha ejecutado init",
266
270
  seedsAttention: "los archivos incluidos requieren atención",
267
- recoveryPending: "Hay una operación activa o pendiente de recuperación. No se cambió nada. Consulta los ID con tonedial doctor --json. Revierte con doctor --fix --rollback ID o reconstruye desde una operación válida detenida con doctor --fix --from-operation ID. Los propietarios activos o ambiguos siguen protegidos.",
271
+ recoveryPending: "Hay una operación en curso o que necesita recuperación. Esta ejecución no cambió archivos.",
272
+ resumeOperation: "Continuar la reparación de la aplicación:",
268
273
  contextCurrent: "La estructura de AGENTS.md y TONE.md está al día.",
269
274
  contextRepair: "Los archivos de contexto gestionados necesitan reparación. Revisa el diagnóstico de la guía y del tono.",
270
275
  contextSize: (guide, total) => total === null
@@ -466,7 +471,7 @@ const messages = {
466
471
  },
467
472
  combos: {
468
473
  contradiction: (rule) => `${rule} se contradice.`,
469
- refusalCompliance: "Un eje pide declarar una negativa; el otro, actuar sin comentarios. Reduce uno de los dos.",
474
+ refusalCompliance: "Un eje pide aplazar la tarea; el otro, empezarla de inmediato con entusiasmo. Reduce uno de los dos.",
470
475
  nothingBlocked: "No se bloquea nada.",
471
476
  },
472
477
  errors: {
@@ -609,21 +614,21 @@ export const es = {
609
614
  },
610
615
  axisHints: {
611
616
  formality: { owns: "lenguaje y distancia social", low: "lenguaje familiar con jerga", high: "lenguaje ceremonial y deferente" },
612
- verbosity: { owns: "longitud y profundidad de la respuesta", low: "resultado directo en una o dos frases", high: "profundidad dentro de la tarea pedida" },
613
- profanity: { owns: "grosería del lenguaje", low: "ninguna palabrota; interjecciones suaves", high: "palabrotas fuertes durante toda la respuesta" },
614
- playfulness: { owns: "frecuencia e intensidad del humor y del rol", low: "lenguaje literal y la forma de trato elegida", high: "expresiones del personaje en toda la respuesta" },
615
- critique: { owns: "intensidad de la crítica", low: "defectos neutrales sin juzgar", high: "críticas vivas e implacables" },
616
- flattery: { owns: "frecuencia de elogios al usuario", low: "ninguno", high: "cuatro elogios, incluso por decisiones pequeñas" },
617
- warmth: { owns: "calidez hacia el usuario", low: "sin saludo, acogida cálida ni ánimo", high: "cuatro comentarios cálidos, con ánimo y empatía" },
617
+ verbosity: { owns: "longitud y profundidad de la respuesta", low: "una frase breve para un resultado sencillo", high: "profundidad dentro de la tarea pedida" },
618
+ profanity: { owns: "solo palabrotas: frecuencia e intensidad", low: "sin palabrotas; conserva la jerga del rol", high: "palabrotas sin censura en casi cada frase" },
619
+ playfulness: { owns: "intensidad y frecuencia del humor y del rol", low: "lenguaje literal; conserva el trato", high: "voz del personaje en casi cada frase" },
620
+ critique: { owns: "dureza de la crítica a defectos y decisiones", low: "defectos neutrales sin juzgar", high: "burla implacable de defectos graves y decisiones" },
621
+ flattery: { owns: "cantidad e intensidad de los elogios", low: "ninguno", high: "elogio efusivo cada párrafo; evaluación intacta" },
622
+ warmth: { owns: "calidez hacia el usuario", low: "sin saludo, acogida cálida ni ánimo", high: "amabilidad, empatía y consuelo en cada párrafo" },
618
623
  conviction: { owns: "firmeza al imponer su criterio", low: "presenta opciones y pregunta", high: "expone una recomendación como decisión" },
619
- care: { owns: "cobertura, organización y presentación funcional", low: "solo la tarea solicitada", high: "seguimiento, copia, descanso, listas y emoji de estado" },
620
- compliance: { owns: "objeciones antes de actuar", low: "pregunta una vez y luego actúa", high: "actúa de inmediato sin comentar que va a obedecer" },
621
- refusal: { owns: "frecuencia de rechazo", low: "sin rechazo ni demora", high: "negativa hasta persuadirlo o usar la palabra clave" },
622
- bravado: { owns: "intensidad de la fanfarronería", low: "sin exageración", high: "personalidad de experto fanfarrón por defecto" },
624
+ care: { owns: "cobertura, organización y presentación", low: "solo la tarea solicitada", high: "seguimiento, copia, descanso, listas y estados" },
625
+ compliance: { owns: "voluntad cuando refusal no aplaza", low: "objeta con un motivo y luego actúa", high: "empieza con ganas; sin demora ni negociación" },
626
+ refusal: { owns: "frecuencia y condiciones del rechazo", low: "sin rechazo ni demora", high: "huelga hasta persuadirlo o usar la palabra clave" },
627
+ bravado: { owns: "esfuerzo exagerado y mérito reclamado", low: "sin exageración", high: "presume y reclama mérito; conserva el personaje" },
623
628
  },
624
629
  settingHints: {
625
630
  safeword: "petición de una respuesta sencilla para un solo mensaje",
626
- speech_level: "auto: según la relación · polite: formas honoríficas · plain: formas normales sin honoríficos · rough: terminaciones bruscas y descaradas sin honoríficos · formality controla el lenguaje y la distancia",
631
+ speech_level: "auto: según la relación · polite: formas honoríficas · plain: formas normales sin honoríficos · rough: terminaciones no honoríficas cortas y tajantes; no añaden por sí mismas bromas, palabrotas, críticas ni burlas · formality controla el lenguaje y la distancia",
627
632
  language: "idioma de la interfaz CLI · independiente de las respuestas del agente",
628
633
  "persona.age": "franja de edad que puede asociar el modelo",
629
634
  "persona.gender": "género que puede asociar el modelo",
@@ -115,7 +115,11 @@ const messages = {
115
115
  rollbackOrder: "先に接続を復元し、その後アプリケーションのファイルを復元してください:",
116
116
  noSnapshot: (id) => `${id}: この状態ルート用の有効な設定スナップショットがありません`,
117
117
  retire: (id) => `停止した操作 ${id} を履歴に移してから再構築してください。外部への変更は検証も取り消しもされません。`,
118
- failure: (id) => `復旧が完了しませんでした。操作 ${id} を保持し、tonedial doctor で復旧方法を確認してください。`,
118
+ resumeOperation: (id) => `停止した操作:\n${id}`,
119
+ resumeQuestion: "続行するには上の操作ID全体を入力してください。Enterで中止:",
120
+ cancelled: "復旧を中止しました。ファイルは変更していません。",
121
+ operationMismatch: "操作IDが一致しません。ファイルは変更していません。",
122
+ failure: (id, phase, path, code) => `アプリ復旧の${({ prepare: "準備", write: "ファイル書き込み", verify: "結果の検証", finalize: "完了処理" })[phase]}で失敗しました (${code})。${path ? `\n${path}` : ""}\n一部のファイル変更が適用済みの場合があります。操作 ${id} の記録を保持してください。`,
119
123
  },
120
124
  persona: {
121
125
  defaults: role("私", "あなた", "作業の支援を求めている利用者。", "依頼された作業を手伝う汎用アシスタント。"),
@@ -154,7 +158,7 @@ const messages = {
154
158
  safewordShort: (phrase) => `合言葉 ${phrase}: 短すぎるため、意図せず反応する可能性があります`,
155
159
  safewordSet: (phrase) => `合言葉 ${phrase}`,
156
160
  noSafeword: "合言葉なし",
157
- register: (level) => `敬語区分 ${level} (${({ auto: "関係に合わせる", polite: "敬語", plain: "通常の常体", rough: "荒っぽい常体" })[level]})`,
161
+ register: (level) => `敬語区分 ${level} (${({ auto: "関係に合わせる", polite: "敬語", plain: "通常の常体", rough: "短くぶっきらぼうな常体" })[level]})`,
158
162
  },
159
163
  artifact: {
160
164
  size: (chars) => `TONE.md ${chars}文字`,
@@ -264,7 +268,8 @@ const messages = {
264
268
  seedsOk: (version) => `バージョン ${version}`,
265
269
  seedsNotRun: "同梱ファイルなし · ここでは init 未実行",
266
270
  seedsAttention: "同梱ファイルの確認が必要",
267
- recoveryPending: "実行中または復旧待ちの操作があります。変更はしていません。tonedial doctor --json でIDを確認してください。記録済みの復元は doctor --fix --rollback ID、停止済みの有効な操作からの再構築は doctor --fix --from-operation ID を使います。実行中または所有者不明の操作は保護されます。",
271
+ recoveryPending: "実行中または復旧が必要な操作があります。この実行ではファイルを変更していません。",
272
+ resumeOperation: "アプリ復旧を続けるには:",
268
273
  contextCurrent: "AGENTS.md と TONE.md の構成は最新です。",
269
274
  contextRepair: "管理対象のコンテキストファイルに修復が必要です。ガイドと口調ファイルの診断を確認してください。",
270
275
  contextSize: (guide, total) => total === null
@@ -466,7 +471,7 @@ const messages = {
466
471
  },
467
472
  combos: {
468
473
  contradiction: (rule) => `${rule} は自己矛盾しています。`,
469
- refusalCompliance: "一方の軸は拒否の明示、他方は無言の即時実行を求めます。どちらかを下げてください。",
474
+ refusalCompliance: "一方の軸は作業の先送り、他方は意欲的な即時着手を求めます。どちらかを下げてください。",
470
475
  nothingBlocked: "ブロックはしていません。",
471
476
  },
472
477
  errors: {
@@ -609,21 +614,21 @@ export const ja = {
609
614
  },
610
615
  axisHints: {
611
616
  formality: { owns: "表現の格式と距離感", low: "親しい言葉や俗語", high: "儀礼的で敬意ある表現" },
612
- verbosity: { owns: "回答の長さと説明の深さ", low: "1、2文で結論を示す", high: "依頼の範囲内で詳しく説明" },
613
- profanity: { owns: "言葉の荒さ", low: "罵りなし、感嘆も穏やか", high: "回答全体で強い罵り" },
614
- playfulness: { owns: "冗談と役柄表現の頻度・強度", low: "平易な表現と設定済みの呼称", high: "全体で大げさな役柄表現" },
615
- critique: { owns: "批判の強さ", low: "評価せず問題を中立に説明", high: "鮮烈で容赦ない批判" },
616
- flattery: { owns: "ユーザーを褒める頻度", low: "なし", high: "小さな選択も含め4回褒める" },
617
- warmth: { owns: "ユーザーに向ける温かさ", low: "挨拶・親しい受け止め・励ましなし", high: "励ましと共感を含む温かい言葉4つ" },
617
+ verbosity: { owns: "回答の長さと説明の深さ", low: "単純な結果は短い1文", high: "依頼の範囲内で詳しく説明" },
618
+ profanity: { owns: "罵りだけ:強さと頻度", low: "罵りなし・役柄の俗語は維持", high: "ほぼ全文で強烈な罵りを伏せずに使う" },
619
+ playfulness: { owns: "冗談と役柄表現の強度・頻度", low: "平易な表現・設定済みの呼称を維持", high: "ほぼ全ての文で大げさな役柄表現" },
620
+ critique: { owns: "問題点と選択への批判の強さ", low: "評価せず問題を中立に説明", high: "深刻な問題点と選択を容赦なく非難し、あざける" },
621
+ flattery: { owns: "称賛の量と強さ", low: "なし", high: "段落ごとに惜しみなく称賛・技術評価は維持" },
622
+ warmth: { owns: "ユーザーに向ける温かさ", low: "挨拶・親しい受け止め・励ましなし", high: "段落ごとに優しさ・共感・慰め・安心" },
618
623
  conviction: { owns: "自分の判断を押し通す強さ", low: "選択肢を並べて尋ねる", high: "推奨を1つ、決定として述べる" },
619
- care: { owns: "範囲・整理・実用的な表示", low: "依頼された作業のみ", high: "後続作業・バックアップ・休憩・一覧・状態絵文字" },
620
- compliance: { owns: "実行前の異議提示", low: "一度尋ねてから実行", high: "従うという前置きなしで即実行" },
621
- refusal: { owns: "拒否の頻度", low: "拒否も遅延もなし", high: "説得か合言葉まで拒否を明示" },
622
- bravado: { owns: "虚勢の強さ", low: "誇張なし", high: "虚勢を張る専門家が標準" },
624
+ care: { owns: "範囲・整理・実用的な表示", low: "依頼された作業のみ", high: "後続・バックアップ・休憩・一覧・状態絵文字" },
625
+ compliance: { owns: "拒否で先送りしない時の実行意欲", low: "反対理由を述べてから実行", high: "進んで即着手・遅延や交渉なし" },
626
+ refusal: { owns: "拒否の頻度と条件", low: "拒否も遅延もなし", high: "説得までストライキ・合言葉は1回答だけ解除" },
627
+ bravado: { owns: "労力の誇張と手柄の主張", low: "誇張なし", high: "毎回答で自慢・手柄・苦労を誇張・役柄は維持" },
623
628
  },
624
629
  settingHints: {
625
630
  safeword: "1つのメッセージに一度だけ平易な回答を求める文句",
626
- speech_level: "auto: 関係に合わせる · polite: 敬語 · plain: 通常の常体 · rough: 荒っぽく生意気な常体 · 表現の格式と距離感は formality で調整",
631
+ speech_level: "auto: 関係に合わせる · polite: 敬語 · plain: 通常の常体 · rough: 短くぶっきらぼうな常体・それ自体では冗談・罵り・批判・嘲りを加えない · 表現の格式と距離感は formality で調整",
627
632
  language: "CLIの表示言語 · エージェントの回答言語とは別",
628
633
  "persona.age": "モデルに連想させる年齢層",
629
634
  "persona.gender": "モデルに連想させるジェンダー",
@@ -115,7 +115,11 @@ const messages = {
115
115
  rollbackOrder: "请先恢复连接,再恢复应用文件:",
116
116
  noSnapshot: (id) => `${id}:没有此状态根目录的有效设置快照`,
117
117
  retire: (id) => `重建应用前,请将已停止的操作 ${id} 归档为历史。其外部变更不会被认证或撤销。`,
118
- failure: (id) => `应用恢复未完成。请保留操作 ${id},运行 tonedial doctor 查看恢复方式。`,
118
+ resumeOperation: (id) => `已停止的操作:\n${id}`,
119
+ resumeQuestion: "要继续,请输入上方完整的操作 ID;按 Enter 取消:",
120
+ cancelled: "已取消恢复,未更改文件。",
121
+ operationMismatch: "操作 ID 不匹配,未更改文件。",
122
+ failure: (id, phase, path, code) => `应用恢复在${({ prepare: "准备", write: "写入文件", verify: "验证结果", finalize: "完成处理" })[phase]}阶段失败 (${code})。${path ? `\n${path}` : ""}\n部分文件更改可能已应用。请保留操作 ${id} 的记录。`,
119
123
  },
120
124
  persona: {
121
125
  defaults: role("我", "你", "请求任务协助的用户。", "协助完成所请求任务的通用助手。"),
@@ -154,7 +158,7 @@ const messages = {
154
158
  safewordShort: (phrase) => `安全词 ${phrase}:太短,可能误触发`,
155
159
  safewordSet: (phrase) => `安全词 ${phrase}`,
156
160
  noSafeword: "未设置安全词",
157
- register: (level) => `敬语形式 ${level} (${({ auto: "按关系选择", polite: "敬语", plain: "普通非敬语", rough: "粗鲁的非敬语" })[level]})`,
161
+ register: (level) => `敬语形式 ${level} (${({ auto: "按关系选择", polite: "敬语", plain: "普通非敬语", rough: "简短生硬的非敬语" })[level]})`,
158
162
  },
159
163
  artifact: {
160
164
  size: (chars) => `TONE.md ${chars} 字符`,
@@ -264,7 +268,8 @@ const messages = {
264
268
  seedsOk: (version) => `版本 ${version}`,
265
269
  seedsNotRun: "无内置文件 · 尚未在此运行 init",
266
270
  seedsAttention: "内置文件需要处理",
267
- recoveryPending: "有操作正在运行或需要恢复,未作任何修改。请用 tonedial doctor --json 查看 ID。已有回滚记录用 doctor --fix --rollback ID;已停止的有效操作可用 doctor --fix --from-operation ID 重建应用。运行中或所有者不明确的操作仍受保护。",
271
+ recoveryPending: "有操作正在运行或需要恢复。本次执行未更改文件。",
272
+ resumeOperation: "继续应用恢复:",
268
273
  contextCurrent: "AGENTS.md 与 TONE.md 布局已是最新。",
269
274
  contextRepair: "受管理的上下文文件需要修复,请检查指南和语气文件诊断。",
270
275
  contextSize: (guide, total) => total === null
@@ -466,7 +471,7 @@ const messages = {
466
471
  },
467
472
  combos: {
468
473
  contradiction: (rule) => `${rule} 自相矛盾。`,
469
- refusalCompliance: "一个语气轴要求明确罢工,另一个要求不加评论立即执行。请降低其中一项。",
474
+ refusalCompliance: "一个语气轴要求推迟任务,另一个要求积极地立即开始。请降低其中一项。",
470
475
  nothingBlocked: "没有阻止任何设置。",
471
476
  },
472
477
  errors: {
@@ -609,21 +614,21 @@ export const zh = {
609
614
  },
610
615
  axisHints: {
611
616
  formality: { owns: "措辞的正式程度与社交距离", low: "熟悉措辞及俚语", high: "庄重恭敬的措辞" },
612
- verbosity: { owns: "回答长度和解释深度", low: "一两句话直接给出结果", high: "在请求范围内深入解释" },
613
- profanity: { owns: "语言粗俗程度", low: "无脏话,缓和感叹", high: "全程使用强烈脏话" },
614
- playfulness: { owns: "幽默和角色用语的频率与强度", low: "直白措辞和已设称呼", high: "全程夸张的角色用语" },
615
- critique: { owns: "批评强度", low: "不作评判,中性陈述缺陷", high: "生动而毫不留情的批评" },
616
- flattery: { owns: "赞美用户的频率", low: "无", high: "即使微小选择也具体赞美四次" },
617
- warmth: { owns: "对用户表达的温暖", low: "无问候、亲切回应或鼓励", high: "四句温暖表达,包含鼓励和共情" },
617
+ verbosity: { owns: "回答长度和解释深度", low: "简单结果只用一个短句", high: "在请求范围内深入解释" },
618
+ profanity: { owns: "仅脏话:强度和频率", low: "不用脏话;保留角色俚语", high: "几乎每句都使用未经弱化的强烈脏话" },
619
+ playfulness: { owns: "幽默和角色表达的强度与频率", low: "直白措辞;保留已设称呼", high: "几乎每句都使用夸张的角色表达" },
620
+ critique: { owns: "对缺陷和选择的批评强度", low: "不作评判,中性陈述缺陷", high: "毫不留情地谴责并嘲讽严重缺陷和相关选择" },
621
+ flattery: { owns: "赞美的数量和强度", low: "无", high: "每段都大力赞美;技术判断不变" },
622
+ warmth: { owns: "对用户表达的温暖", low: "无问候、亲切回应或鼓励", high: "每段都表达温柔、共情、安慰和宽心" },
618
623
  conviction: { owns: "坚持自身判断的力度", low: "列出选项并询问", high: "将一个推荐作为决定陈述" },
619
- care: { owns: "覆盖范围、组织和实用呈现", low: "仅执行所请求任务", high: "后续工作、备份、休息、列表及状态表情" },
620
- compliance: { owns: "行动前提出异议", low: "询问一次后行动", high: "不加服从说明,立即行动" },
621
- refusal: { owns: "拒绝频率", low: "无拒绝或延迟", high: "明确罢工,直到被说服或听到安全词" },
622
- bravado: { owns: "吹嘘强度", low: "无夸张", high: "默认夸口的专家人设" },
624
+ care: { owns: "覆盖范围、组织和实用呈现", low: "仅执行所请求任务", high: "后续、备份、休息、列表和状态表情" },
625
+ compliance: { owns: "拒绝未推迟任务时的行动意愿", low: "提出异议和原因后执行", high: "积极立即开始;不拖延、不谈条件" },
626
+ refusal: { owns: "拒绝频率和条件", low: "无拒绝或延迟", high: "罢工到被说服;安全词仅解除一次" },
627
+ bravado: { owns: "夸大付出和邀功的程度", low: "无夸张", high: "每次都吹嘘、邀功、夸大付出;保留角色" },
623
628
  },
624
629
  settingHints: {
625
630
  safeword: "对一条消息请求一次平实回答的短语",
626
- speech_level: "auto:按关系选择 · polite:敬语 · plain:普通非敬语 · rough:粗鲁且带有不逊语气的非敬语 · 措辞和距离感由 formality 调整",
631
+ speech_level: "auto:按关系选择 · polite:敬语 · plain:普通非敬语 · rough:简短生硬的非敬语句尾;本身不增加玩笑、脏话、批评或嘲讽 · 措辞和距离感由 formality 调整",
627
632
  language: "CLI 界面语言 · 与代理回答语言无关",
628
633
  "persona.age": "模型可能联想的年龄段",
629
634
  "persona.gender": "模型可能联想的性别",
@@ -274,6 +274,9 @@ export function inspectPresetLibrary(presetsDir, t) {
274
274
  unavailable.push({ slug: retired.slug, path, reason: read.reason });
275
275
  continue;
276
276
  }
277
+ // Exact packaged bytes need no copy; edited labels or formatting still stay preserved.
278
+ if (read.text === `${JSON.stringify(retired, null, 2)}\n`)
279
+ continue;
277
280
  let preset;
278
281
  try {
279
282
  preset = parsePreset(read.text, retired.slug, t);
@@ -11,7 +11,7 @@ import { captureRawEntry, caseRenameMatchesCurrent, isNoop, planChange, planEntr
11
11
  import { BUNDLED, builtinPresetDir, LEGACY_BUILTIN_SLUGS, RETIRED_PRESETS } from "./presets.js";
12
12
  import { SKILL_MD, SKILL_NAME, SKILL_TRAITS_MD } from "./skill.js";
13
13
  // Bump for changed generated content; freshness also checks each managed seed's bytes/provenance.
14
- export const SEED_VERSION = 19;
14
+ export const SEED_VERSION = 21;
15
15
  /** The state-root operating boundaries. The compiler owns the current tone separately. */
16
16
  export const GUIDE_MD = `${GUIDE_FORMAT_MARKER}
17
17
  ToneDial changes manner only; it never expands task scope or permissions.
@@ -73,10 +73,10 @@ export function seedFiles(paths, selection) {
73
73
  };
74
74
  const native = [
75
75
  reader,
76
- { id: "skill/body", version: 19, path: join(paths.skill, SKILL_NAME, "SKILL.md"), label: `skill/${SKILL_NAME}/SKILL.md`, contents: SKILL_MD },
76
+ { id: "skill/body", version: 21, path: join(paths.skill, SKILL_NAME, "SKILL.md"), label: `skill/${SKILL_NAME}/SKILL.md`, contents: SKILL_MD },
77
77
  {
78
78
  id: "skill/traits",
79
- version: 11,
79
+ version: 12,
80
80
  path: join(paths.skill, SKILL_NAME, "references", "traits.md"),
81
81
  label: `skill/${SKILL_NAME}/references/traits.md`,
82
82
  contents: SKILL_TRAITS_MD,