tonedial 0.1.5 → 0.1.7

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,6 +1,7 @@
1
1
  // Hooks may run without PATH or after the package moves. The seeded reader is standalone.
2
2
  import { CONTEXT_ARTIFACT_FILENAME, CONTEXT_GUIDE_FILENAME, } from "./paths.js";
3
- export const READER_PROTOCOL_VERSION = 2;
3
+ import { DELIVERY_OFF_MARKER } from "./anchors.js";
4
+ export const READER_PROTOCOL_VERSION = 3;
4
5
  export const GUIDE_FORMAT_VERSION = 1;
5
6
  export const GUIDE_FORMAT_MARKER = `<!-- tonedial-guide:${GUIDE_FORMAT_VERSION} -->`;
6
7
  /** Stable, short repair guidance shared by the emitted source and diagnostics. */
@@ -25,6 +26,7 @@ export const READER_REPAIR_HINTS = {
25
26
  event: "use a supported hook event",
26
27
  input: "retry the hook with a valid event payload",
27
28
  response: "retry the hook when the response is available",
29
+ "delivery-marker": "regenerate the tone artifact",
28
30
  "style-marker": "regenerate the tone artifact",
29
31
  unreadable: "run tonedial doctor and resolve the reported path",
30
32
  };
@@ -50,6 +52,7 @@ const GUIDE_FILENAME = ${JSON.stringify(CONTEXT_GUIDE_FILENAME)};
50
52
  const ARTIFACT_FILENAME = ${JSON.stringify(CONTEXT_ARTIFACT_FILENAME)};
51
53
  const LOCK_FILENAME = "operations.lock";
52
54
  const GUIDE_MARKER = ${JSON.stringify(GUIDE_FORMAT_MARKER)};
55
+ const DELIVERY_OFF_MARKER = ${JSON.stringify(DELIVERY_OFF_MARKER)};
53
56
  const REPAIR_HINTS = ${JSON.stringify(READER_REPAIR_HINTS)};
54
57
  const PROVIDERS = new Set(["codex", "claude", "antigravity"]);
55
58
  const EVENTS = new Set(["SessionStart", "UserPromptSubmit", "Stop", "PreInvocation"]);
@@ -338,6 +341,13 @@ function styleMarker(artifact) {
338
341
  return match === null ? null : { verbosity: Number(match[1]), care: Number(match[2]) };
339
342
  }
340
343
 
344
+ function deliveryEnabled(artifact) {
345
+ const line = artifact.split(/\r?\n/, 3)[2] ?? "";
346
+ if (line === DELIVERY_OFF_MARKER) return false;
347
+ if (line.startsWith("<!-- tonedial-delivery:")) throw new ReaderFailure("delivery-marker");
348
+ return true;
349
+ }
350
+
341
351
  // shortcut: ambiguous data-like prose skips feedback; refine only with verified host format metadata.
342
352
  function literalPayload(text) {
343
353
  const value = text.trim();
@@ -400,8 +410,12 @@ async function main() {
400
410
  const hint = providerHint(args);
401
411
  try {
402
412
  const { provider, state, event } = parseArgs(args);
403
- const input = event === "Stop" ? await hookInput() : null;
404
413
  const pair = readPair(state);
414
+ if (!deliveryEnabled(pair.artifact)) {
415
+ empty(provider, event);
416
+ return;
417
+ }
418
+ const input = event === "Stop" ? await hookInput() : null;
405
419
  if (event === "Stop") {
406
420
  const result = styleFeedback(pair, input);
407
421
  if (result.diagnostic !== null) {
@@ -5,6 +5,7 @@ export function toCompileInput(stored) {
5
5
  // The slug rides in the generation comment, so the model reads it. `unsaved` is what the
6
6
  // artifact says once tone settings have moved away from the profile that named them.
7
7
  preset: stored.unsaved ? null : stored.preset,
8
+ ...(stored.enabled ? {} : { enabled: false }),
8
9
  axes: stored.axes,
9
10
  speech_level: stored.speech_level,
10
11
  persona: hasPersona ? p : null,
@@ -11,13 +11,13 @@ 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 = 21;
14
+ export const SEED_VERSION = 24;
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.
18
18
  Use the ToneDial CLI for configuration changes.
19
19
  AGENTS.md is the operating guide, TONE.md is the current compiled tone, and the ToneDial skill
20
- explains explicit in-session tuning and reloads.
20
+ explains delivery controls, explicit in-session tuning and reloads.
21
21
  `;
22
22
  /** Existing generated runtime families are repaired without inferring any native connection. */
23
23
  export function generatedSeedSelection(paths, config, native, t = catalogue("en").cli) {
@@ -73,7 +73,7 @@ export function seedFiles(paths, selection) {
73
73
  };
74
74
  const native = [
75
75
  reader,
76
- { id: "skill/body", version: 21, path: join(paths.skill, SKILL_NAME, "SKILL.md"), label: `skill/${SKILL_NAME}/SKILL.md`, contents: SKILL_MD },
76
+ { id: "skill/body", version: 22, 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
79
  version: 12,
@@ -5,6 +5,7 @@ export const SKILL_NAME = "tonedial";
5
5
  /** The current command envelope, shared by both installed control entry points. */
6
6
  const JSON_MD = `Require zero exit status and a JSON result with \`ok: true\` and \`schema: 3\`
7
7
  for every successful CLI step before continuing. Parse JSON rather than scraping human output.
8
+ \`get --json\`, \`show --json\`, and \`compile --check --json\` report the shared \`enabled\` delivery state.
8
9
  \`chars\` reports the current TONE.md Unicode character count; no size target or maximum applies.
9
10
  Schema 3 removes guard and ceiling fields and reports advisory \`riskAxes\`; persisted config
10
11
  remains schema 2. Custom profiles use preset schema 2; legacy schema 1 and journal formats
@@ -65,6 +66,10 @@ Require zero exit status, \`ok: true\`, and \`schema: 3\` before continuing.
65
66
  This read-only command reports destinations; it does not prove that either file exists,
66
67
  matches the saved settings, or has been loaded.
67
68
 
69
+ If it reports \`enabled: false\`, do not read or apply the context pair. Report that the settings
70
+ are saved but delivery remains off. This also applies to tone changes made while delivery is off.
71
+ Continue below only when it reports \`enabled: true\`.
72
+
68
73
  Read the complete UTF-8 file at the returned absolute \`agentsPath\` first (\`AGENTS.md\`).
69
74
  Its first line must be \`${GUIDE_FORMAT_MARKER}\`; otherwise stop the reload.
70
75
  Then read the complete UTF-8 file at the returned absolute \`path\` (\`TONE.md\`).
@@ -86,6 +91,22 @@ This reload affects only the current conversation. It does not fire a lifecycle
86
91
  earlier context, or reload other open sessions. Future delivery still depends on an installed,
87
92
  enabled, and trusted native hook.
88
93
  `;
94
+ const DELIVERY_MD = `## Pause and resume delivery
95
+
96
+ For an explicit request to turn ToneDial off, run \`tonedial off --json\`, then run
97
+ \`tonedial compile --check --json\`. Require both results to report \`enabled: false\`.
98
+ Do not read the guide or artifact afterward. Report that saved settings, profiles and the full
99
+ \`TONE.md\` remain. The current ToneDial reader emits no ToneDial context.
100
+ Already delivered context cannot be removed from an open conversation; stop applying the saved
101
+ manner where possible and name a fresh conversation as the clean check. Do not promise a perfect reset.
102
+ The successful command proves the marker was saved, not that a native reader ran.
103
+ A reader installed before this switch must be refreshed through the user's existing tools and scope
104
+ before it can honor the marker.
105
+
106
+ For an explicit request to turn ToneDial on, run \`tonedial on --json\`, then follow the reload
107
+ procedure below. Turning delivery on does not prove that a native hook is installed, trusted,
108
+ enabled or running. Neither command changes axes, persona, address, register, safeword or profiles.
109
+ `;
89
110
  /** Explicit setting requests use the same CLI mapping through either installed entry point. */
90
111
  const SETTINGS_MD = `## Explicit settings
91
112
 
@@ -195,6 +216,12 @@ If repair fails, report its phase, affected path, safe error code and operation
195
216
  Some file changes may already be applied; a failure is not evidence that all files stayed unchanged.
196
217
  Preserve the operation record and follow the reported scoped read-only diagnosis.
197
218
  Default doctor output focuses on blockers; \`--verbose\` and JSON retain the operation history.
219
+ If doctor reports an unreadable operation record, use its exact scoped
220
+ \`doctor --fix --quarantine-operation <id>\` command. Preview or approve that replacement through
221
+ the normal repair gate; never delete the record directly. The replacement stops the damaged
222
+ record from blocking recovery, while the repair journal keeps its original bytes and mode for
223
+ explicit rollback. If several records are unreadable, run the next exact command doctor reports;
224
+ each invocation quarantines one record, and the last continues the application rebuild.
198
225
  For an explicitly selected stopped operation, \`doctor --fix --from-operation <id>\` continues
199
226
  application repair using valid current settings first; it does not undo the earlier changes.
200
227
  Preview that exact ID and scope with \`--dry-run\` before applying the approved repair.
@@ -214,7 +241,7 @@ Do not claim that setup, repair, or migration proves host activation or model ad
214
241
  */
215
242
  export const SKILL_MD = `---
216
243
  name: tonedial
217
- description: Use when the user wants to check, change, or reload ToneDial manner, tone, personality, register, preset, user description, agent role, address terms, or safeword — including requests like "be gentler", "act as a database specialist", "remember my background in ToneDial", "more formal", "call me friend", or asking what the current tone settings are.
244
+ description: Use when the user wants to check, change, pause, resume, or reload ToneDial manner, tone, personality, register, preset, user description, agent role, address terms, or safeword — including requests like "turn ToneDial off", "be gentler", "act as a database specialist", "remember my background in ToneDial", "more formal", "call me friend", or asking what the current tone settings are.
218
245
  ---
219
246
 
220
247
  # tonedial
@@ -233,6 +260,7 @@ the user to complete CLI installation and explicit setup; do not install it or g
233
260
  tonedial get --json
234
261
  \`\`\`
235
262
 
263
+ The top-level \`enabled\` field reports whether native delivery is on.
236
264
  The \`values\` object carries the twelve axis levels and the separate settings under their
237
265
  editable keys. An unset setting is \`null\`; description text is returned in full.
238
266
  For the formatted overview, preset state and advisory \`riskAxes\`, use \`tonedial show --json\`.
@@ -277,9 +305,11 @@ ${SETTINGS_MD}
277
305
 
278
306
  ${SETUP_MD}
279
307
 
308
+ ${DELIVERY_MD}
309
+
280
310
  ## Apply it in this session
281
311
 
282
- Successful \`set\`, \`address\`, \`persona\`, \`use\`, \`reset\`, \`save\`, and approved \`create\` commands recompile \`TONE.md\`; that is a saved state
312
+ Successful \`on\`, \`off\`, \`set\`, \`address\`, \`persona\`, \`use\`, \`reset\`, \`save\`, and approved \`create\` commands recompile \`TONE.md\`; that is a saved state
283
313
  change, not evidence of a session reload.
284
314
 
285
315
  ${RELOAD_MD}
@@ -294,9 +324,9 @@ in the same message still faces every confirmation and permission check it other
294
324
  For an explicit request to restore the default axes, run \`tonedial reset --json\`, then follow
295
325
  the reload steps above. Saved presets remain available through \`tonedial use <slug> --json\`.
296
326
  Reset restores axes only; it does not disable hooks, unload instructions, or clear persona,
297
- safeword, or other settings. ToneDial has no persistent tone-off switch.
298
- For "turn tone off" or an ambiguous "go back to normal", explain that limit and ask whether
299
- the user wants an axis reset before changing state; do not silently equate off with reset.
327
+ safeword, or other settings. Use \`tonedial off --json\` to pause delivery without changing them.
328
+ For an ambiguous "go back to normal", ask whether the user means delivery off or an axis reset;
329
+ do not silently equate them.
300
330
 
301
331
  ## A separate test conversation
302
332
 
@@ -452,12 +482,13 @@ selected by critique or persona. Bravado grades effort claims without replacing
452
482
  * command name stays `/tone` even though the tool is `tonedial`: it is what a person types
453
483
  * mid-sentence, and the two extra characters are paid every time.
454
484
  */
455
- export const TONE_COMMAND_VERSION = 13;
485
+ export const TONE_COMMAND_VERSION = 16;
456
486
  export const TONE_COMMAND_MD = `---
457
- description: Check, change, or reload this agent's tone, preset, or ToneDial settings
487
+ description: Check, change, pause, resume, or reload this agent's tone, preset, or ToneDial settings
458
488
  ---
459
489
 
460
- Run \`tonedial get --json\` and report the requested current settings from \`values\` in plain language.
490
+ Run \`tonedial get --json\` and report the requested current settings from \`values\` and the
491
+ delivery state from \`enabled\` in plain language.
461
492
  Use the same installed CLI and environment throughout.
462
493
  ${JSON_MD}
463
494
  Otherwise report the failed step and stop that flow.
@@ -479,13 +510,15 @@ ${SETTINGS_MD}
479
510
 
480
511
  ${SETUP_MD}
481
512
 
513
+ ${DELIVERY_MD}
514
+
482
515
  ${RELOAD_MD}
483
516
 
484
517
  An explicit axis-reset request uses \`tonedial reset --json\` followed by the same reload steps.
485
518
  Reset restores axes only; it does not disable hooks, unload instructions, or clear persona,
486
519
  safeword, or other settings. Saved presets remain available.
487
- \`/tone off\` is not a persistent tone-off switch. Explain that limit and ask whether the user
488
- wants an axis reset before changing state; never silently equate off with reset.
520
+ For an ambiguous \`/tone normal\`, ask whether the user means delivery off or an axis reset;
521
+ never silently equate them.
489
522
 
490
523
  Never edit ToneDial's config, guide, or artifact directly; the CLI is the source of truth.
491
524
  `;
@@ -832,7 +832,7 @@ function validateEntryRebuildAction(action, prepared = false) {
832
832
  const value = snapshotRecord(action.metadata ?? null);
833
833
  if (action.kind !== "entry-rebuild" || !normalizedPath(action.path) || action.realPath !== action.path || value === null ||
834
834
  Object.keys(value).sort().join(",") !== ["schema", "reason", "boundary", "boundaryIdentity", "parents", "stash", "stashIdentity", "archive", "prepared", "preparedIdentity", "createdParents", ...(Object.hasOwn(value, "recipes") ? ["recipes"] : [])].sort().join(",") ||
835
- value.schema !== 1 || !["generated", "restore-config", "restore-preset"].includes(value.reason) ||
835
+ value.schema !== 1 || !["generated", "restore-config", "restore-preset", "quarantine-operation"].includes(value.reason) ||
836
836
  !normalizedPath(value.boundary) || !lexicallyInside(action.path, value.boundary) || !identityTextValue(value.boundaryIdentity) ||
837
837
  !Array.isArray(value.parents) || value.parents.length === 0 || !normalizedPath(value.stash) ||
838
838
  dirname(value.stash) !== dirname(action.path) || !new RegExp(`^\\.${basename(action.path).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.tonedial-rebuild-[0-9a-f]{32}$`).test(basename(value.stash)) ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tonedial",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Set a coding agent's manner once and keep it across every project.",
5
5
  "license": "MIT",
6
6
  "type": "module",