tonedial 0.1.1 → 0.1.3

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/core/i18n.js CHANGED
@@ -41,7 +41,7 @@ const enMessages = {
41
41
  range: (axis, given) => `${axis}: expected 1–5 (got ${given})`,
42
42
  unknown: (name, nearest) => `${name}: not an axis${nearest ? `. Did you mean ${nearest}?` : ""}`,
43
43
  notSettable: (name, nearest) => `${name}: not an axis or setting. ${nearest ? `Did you mean ${nearest}? ` : ""}` +
44
- "Full list: tonedial show --explain",
44
+ "Full list: tonedial get --help",
45
45
  reset: (axis, level) => `${axis}: default ${level}`,
46
46
  resetAll: "All axes reset to defaults; persona and settings kept.",
47
47
  nothingChanged: "no changes",
@@ -72,7 +72,6 @@ const enMessages = {
72
72
  `Run tonedial use ${was}, then save it as ${tried}.`,
73
73
  noLatin: (name) => `"${name}": no Latin letters or digits for a slug`,
74
74
  nameTooLong: (given, cap) => `display name: ${given} characters · maximum ${cap}`,
75
- sparsity: (slug, count) => `${slug}: ${count} changed axes · recommended 3–7 (5±2). Fewer extremes reduce conflicts`,
76
75
  replaced: (slug) => `${slug}: existing preset replaced`,
77
76
  imported: (slug, replaced) => `${slug}: ${replaced ? "replaced" : "imported"}`,
78
77
  unreadable: (path, reason) => `${path}\ncannot read: ${reason}`,
@@ -99,7 +98,7 @@ const enMessages = {
99
98
  slug: "Slug (lowercase letters, digits, dashes)",
100
99
  user: "Your role or background (up to 200 characters)",
101
100
  agent: "Agent role (up to 200 characters)",
102
- register: "Register (auto, polite, plain)",
101
+ register: "Honorific forms (auto, polite, plain, rough)",
103
102
  safeword: "Safeword",
104
103
  needsName: "create needs a display name. Run tonedial create in a terminal, or supply a name and optional key=value settings with --auto.",
105
104
  exists: (slug) => `${slug}: a preset already exists. Choose another slug, or use save to update it.`,
@@ -151,24 +150,22 @@ const enMessages = {
151
150
  addressNone: "currently unset · Enter keeps it unset",
152
151
  age: (given, bands) => `persona.age=${given} · available: ${bands.join(", ")}`,
153
152
  gender: (options) => `persona.gender · available: ${options.join(", ")}`,
154
- noteOverCap: (given, cap) => `persona.note: ${given} characters · over ${cap} recommended`,
155
- noteNamesAxis: (axes) => `persona.note overlaps the ${axes.join(", ")} axes. Axes have anchors, so they win any conflict.`,
156
153
  callsYou: (term) => `user name ${term}`,
157
154
  callsItself: (term) => `self name ${term}`,
158
155
  noteChars: (count) => `note ${count} chars`,
159
156
  descriptionOverCap: (field, given, cap) => `${field}: ${given} characters · maximum ${cap}`,
160
157
  descriptionChars: (field, count) => `${field === "user" ? "user context" : "fictional agent role"} · ${count} chars`,
161
158
  descriptionCounts: (user, agent, cap) => `roles · user ${user}/${cap} · agent ${agent}/${cap}`,
162
- descriptionFull: (field, value) => `${field === "user" ? "user context" : "fictional agent role"}: ${value}`,
159
+ descriptionFull: (field, value) => `${field === "user" ? "user context" : field === "agent" ? "fictional agent role" : "persona note"}: ${value}`,
163
160
  },
164
161
  settings: {
165
162
  guardsRetired: "guards is retired. ToneDial no longer adds guard requests. No settings were changed.",
166
163
  fixedWidthRetired: "fixed_width is retired. Output now follows terminal width automatically. No settings were changed.",
167
164
  speechLevel: (options) => `speech_level · available: ${options.join(", ")}`,
168
165
  safewordShort: (phrase) => `safeword ${phrase}: too short and may trigger by accident`,
169
- safewordSet: (phrase) => `safeword ${phrase} — requests one plain answer when something reads wrong. It neither switches tone off nor grants authority`,
170
- noSafeword: "no safeword — request a plain answer through the skill only",
171
- register: (level) => `register ${level}`,
166
+ safewordSet: (phrase) => `safeword ${phrase}`,
167
+ noSafeword: "no safeword",
168
+ register: (level) => `register ${level} (${({ auto: "contextual", polite: "honorific", plain: "non-honorific", rough: "rough non-honorific" })[level]})`,
172
169
  },
173
170
  artifact: {
174
171
  size: (chars) => `TONE.md ${chars} characters`,
@@ -269,14 +266,14 @@ const enMessages = {
269
266
  inspectScope: "To keep using these connections, inspect this scope:",
270
267
  disconnectScope: "If no longer needed, disconnect all agents in this scope:",
271
268
  scope: "Inspection scope: project",
272
- toLookAt: (count) => `${count} need attention · --fix repairs what it can`,
269
+ toLookAt: (count) => `${count} ${count === 1 ? "check needs" : "checks need"} attention`,
273
270
  needsAPerson: "manual review required · see details above",
274
271
  toneMatches: "matches config.json",
275
272
  toneNotWritten: "not written",
276
273
  toneDisagrees: "differs from config.json · edited directly or write interrupted",
274
+ seedsHeading: "Bundled files",
277
275
  seedsOk: (version) => `version ${version}`,
278
276
  seedsNotRun: "no bundled files · init has not run here",
279
- seedsStale: (labels) => `${labels.length} need refresh: ${labels.join(", ")}`,
280
277
  seedsAttention: "bundled files need attention",
281
278
  recoveryPending: "An operation is active or needs recovery. Nothing was changed. Run tonedial doctor --json to inspect IDs. A recorded rollback uses doctor --fix --rollback ID; a stopped valid operation can be explicitly selected for app rebuild with doctor --fix --from-operation ID. Live or ambiguous owners stay protected.",
282
279
  contextCurrent: "AGENTS.md and TONE.md layout is current.",
@@ -312,13 +309,12 @@ const enMessages = {
312
309
  rollbackAction: { applied: "restore", "already-reverted": "already restored", changed: "changed · preserve", unsupported: "unsupported · preserve" },
313
310
  rollbackQuestion: "Restore the selected operation?",
314
311
  seedReason: {
315
- missing: "missing · run init with the required agents selected",
316
- stale: "older bundled file · review an init refresh",
317
- modified: "locally modified · review changes before replacement",
318
- foreign: "Not owned by ToneDial. Preserve it and choose a dedicated state directory.",
319
- unreadable: "Cannot read safely. Check the path and its permissions.",
312
+ missing: "Missing bundled file",
313
+ stale: "Bundled file update available",
314
+ modified: "Locally modified · review before replacement",
315
+ foreign: "Not owned by ToneDial · preserved",
316
+ unreadable: "Cannot read safely · check path and permissions",
320
317
  },
321
- seedsBehind: (had, has) => `home bundled-file version ${had} · package version ${has}. An npm update does not refresh existing home files`,
322
318
  skillLinked: "linked to bundled copy",
323
319
  skillCopied: "copied · platform does not support links",
324
320
  skillForeign: (path) => `${path}: not installed by ToneDial, so it will not be replaced`,
@@ -335,9 +331,7 @@ const enMessages = {
335
331
  },
336
332
  hookSettingScope: "Hook status is read from the inspected settings file only; host overrides and session delivery are unverified.",
337
333
  hookGroupHeading: "Agent hooks",
338
- hookGroupUnknown: (tools) => `Hook enablement could not be confirmed for: ${tools.join(", ")}.`,
339
- hookGroupMissing: "No managed ToneDial hook is registered for these agents in this scope.",
340
- hookGroupAction: "To connect ToneDial, run init in this scope and select these agents.",
334
+ hookGroupUnknown: (tools) => `Not connected: ${tools.join(", ")}.`,
341
335
  hookNotConnected: "No managed ToneDial hook to inspect. To connect, run init with this agent selected in the same scope.",
342
336
  hookNeedsInspection: "Resolve the reported hook settings or program issue before checking enablement in the app.",
343
337
  hookGuidance: {
@@ -381,24 +375,37 @@ const enMessages = {
381
375
  "Find the global prefix: npm prefix -g\nAdd its bin directory (macOS/Linux) to PATH.",
382
376
  },
383
377
  notice: {
384
- modelExpectation: "The model may not produce the tone you expect.",
385
378
  riskIntro: "For fun, not recommended for production. Risky tone can distort judgments and work reports through falsehood, omission or exaggeration. ! marks advisory levels, not measured probabilities.",
386
379
  riskAxes: (axes) => `! ${axes.join(", ")} · ${[
387
380
  axes.some(axis => axis === "profanity" || axis === "critique") ? "harsh language" : null,
388
381
  axes.some(axis => axis === "flattery" || axis === "conviction" || axis === "bravado") ? "biased judgments or exaggerated reports" : null,
389
382
  axes.some(axis => axis === "compliance" || axis === "refusal") ? "hasty action or refusal" : null,
390
383
  ].filter(Boolean).join("; ")}`,
391
- personaUnanchored: "persona has no fixed anchors; axes take precedence. Text may affect content. " +
392
- "Roleplay does not verify credentials or grant permissions.",
393
384
  roleplay: "High playfulness requests frequent, exaggerated role idioms and slang, such as 'your lowly servant' or 'take that function off the books'. They may sound rude or violent; ToneDial does not filter these expressions.",
394
385
  risk: "for fun, and not recommended for production use",
395
386
  },
396
387
  update: {
397
388
  running: (version) => `tonedial ${version}`,
398
- installedBy: (shape) => `install method ${shape}`,
399
- toUpdate: "to update:",
400
- thenRefresh: "then check and rebuild application files while keeping saved settings:",
401
- neverInitialised: "init has not run · no home files to refresh",
389
+ toUpdate: "Package update:",
390
+ thenRefresh: "Refresh application files:",
391
+ setupNeeded: "Set up ToneDial:",
392
+ refreshFiles: (count) => `Bundled file updates available: ${count}`,
393
+ reviewState: "Inspect the affected application files:",
394
+ npmQuestion: " Run this npm update?",
395
+ npmComplete: (version) => `npm update complete · installed ${version}`,
396
+ npmMissing: "npm was not found on PATH. Use the Node/npm installation that owns this ToneDial copy.",
397
+ npmScopeUnknown: "Cannot verify npm's global installation directory. Check npm prefix -g and npm root -g before retrying.",
398
+ npmScopeMismatch: "This ToneDial copy is not the global package managed by the selected npm. Use the matching Node/npm installation; no update was run.",
399
+ npmFailed: "npm update failed. Check npm's access and registry settings, then retry the displayed command for npm's diagnostics.",
400
+ npmPermissionDenied: (code, prefix) => `npm update needs write permission (${code}).\n${prefix}\nBack up your settings and presets, then reinstall in a user-owned directory.\nRecovery steps:\nhttps://github.com/awesometic/tonedial/blob/main/docs/USAGE.md#npm-permissions`,
401
+ versionUnknown: "The installed ToneDial version could not be verified. Check the package installation before retrying.",
402
+ stateIssue: {
403
+ missing: "Saved settings are missing.",
404
+ unsafe: "Saved settings cannot be read safely. Check links and file types.",
405
+ changed: "Saved settings changed during inspection. Retry after changes stop.",
406
+ invalid: "Saved settings are invalid.",
407
+ unreadable: "Application state could not be read. Check the path and permissions.",
408
+ },
402
409
  fetching: (registry) => `checking · ${registry}`,
403
410
  found: (version, bytes) => `${version}, ${bytes.toLocaleString("en")} bytes`,
404
411
  already: (version) => `${version} already installed`,
@@ -406,11 +413,10 @@ const enMessages = {
406
413
  question: (version) => ` Replace this install with ${version}?`,
407
414
  replaced: (version) => `updated · ${version}`,
408
415
  declined: "cancelled · existing install kept",
409
- onlyCurl: (shape) => `${shape} install · use its own update path`,
410
416
  noTar: "tar is required for updates but is not on PATH",
411
417
  notAPackage: "download is not a ToneDial package",
412
418
  linkInPackage: "release contains a symbolic link, but published files need none",
413
- needsApproval: "Replacing an install needs approval, but no terminal is available. Rerun with --auto.",
419
+ needsApproval: "Update not run · approval required. Rerun tonedial update --auto to approve.",
414
420
  integrityMismatch: "download hash differs from the registry",
415
421
  notOurs: (dir) => `${dir}: not created by ToneDial; remove it yourself`,
416
422
  swapFailed: (reason) => `installation stopped: ${reason}. Check recovery details before retrying.`,
@@ -426,6 +432,7 @@ const enMessages = {
426
432
  commands: {
427
433
  init: "set up and refresh bundled files",
428
434
  set: "change axes, role or settings · bare opens dial",
435
+ get: "read current setting values · key adds its meaning and choices",
429
436
  address: "set names for each party · bare prompts",
430
437
  persona: "set agent and user roles · bare prompts",
431
438
  reset: "reset an axis · bare resets all axes",
@@ -474,14 +481,8 @@ const enMessages = {
474
481
  },
475
482
  combos: {
476
483
  contradiction: (rule) => `${rule} contradicts itself.`,
477
- reads: (rule) => `${rule}:`,
478
484
  refusalCompliance: "One axis requires a declared strike; the other requires immediate action without comment. Lower one.",
479
- formalSelfRef: "Ceremonious wording and a familiar self-name create a contrast.",
480
- casualUserRef: "Familiar wording and an honorific address create a contrast.",
481
- teensFormal: "A teenage persona with ceremonious wording can sound theatrical.",
482
- seniorJuniorSelfRef: "The stated age and self-name imply different standing.",
483
485
  nothingBlocked: "Nothing is blocked.",
484
- addressUnlikely: "Brief or unemotional replies may omit names; strong roleplay may make them more frequent.",
485
486
  },
486
487
  errors: {
487
488
  notACommand: (name, known, nearest) => `${name} is not a command.${nearest ? ` Did you mean ${nearest}?` : ""} ` +
@@ -639,13 +640,13 @@ const en = {
639
640
  },
640
641
  settingHints: {
641
642
  safeword: "request for one plain answer to one message",
642
- speech_level: "honorific forms: auto, polite, plain · formality varies wording within them",
643
+ speech_level: "auto: adapt to the relationship · polite: honorific forms · plain: ordinary non-honorific forms · rough: rough, cheeky non-honorific endings · wording and distance: formality",
643
644
  language: "CLI interface language · unrelated to agent replies",
644
645
  "persona.age": "age band the model may associate with",
645
646
  "persona.gender": "gender the model may associate with",
646
647
  "persona.self_ref": "agent's self-name",
647
648
  "persona.user_ref": "agent's name for the user",
648
- "persona.note": "role the axes cannot express · over 200 characters warns",
649
+ "persona.note": "role or manner the axes cannot express",
649
650
  "persona.user": "user context · maximum 200 characters",
650
651
  "persona.agent": "fictional agent role · maximum 200 characters · credentials are unverified; grants no capability or permission",
651
652
  },
@@ -689,7 +690,7 @@ const koMessages = {
689
690
  range: (axis, given) => `${axis}: 1~5만 쓸 수 있습니다(입력: ${given})`,
690
691
  unknown: (name, nearest) => `${name}: 축이 아닙니다${nearest ? `. ${nearest} 말씀이신가요?` : ""}`,
691
692
  notSettable: (name, nearest) => `${name}: 축도 설정도 아닙니다. ${nearest ? `${nearest} 말씀이신가요? ` : ""}` +
692
- "전체 목록: tonedial show --explain",
693
+ "전체 목록: tonedial get --help",
693
694
  reset: (axis, level) => `${axis}: 기본값 ${level}`,
694
695
  resetAll: "모든 축을 기본값으로 되돌렸습니다. 페르소나와 설정은 유지했습니다.",
695
696
  nothingChanged: "변경 없음",
@@ -720,7 +721,6 @@ const koMessages = {
720
721
  `tonedial use ${was} 명령을 실행한 뒤 저장할 새 이름을 지정하세요: ${tried}`,
721
722
  noLatin: (name) => `"${name}": 슬러그로 쓸 라틴 문자나 숫자가 없습니다`,
722
723
  nameTooLong: (given, cap) => `표시 이름: ${given}자 · 최대 ${cap}자`,
723
- sparsity: (slug, count) => `${slug}: 변경 축 ${count}개 · 권장 3~7개(5±2). 극단값이 적으면 충돌도 줄어듭니다`,
724
724
  replaced: (slug) => `${slug}: 기존 프리셋 교체됨`,
725
725
  imported: (slug, replaced) => `${slug}: ${replaced ? "교체됨" : "가져옴"}`,
726
726
  unreadable: (path, reason) => `${path}\n읽을 수 없습니다: ${reason}`,
@@ -747,7 +747,7 @@ const koMessages = {
747
747
  slug: "슬러그(영문 소문자·숫자·붙임표)",
748
748
  user: "사용자 역할·배경(최대 200자)",
749
749
  agent: "에이전트 역할(최대 200자)",
750
- register: "말씨(auto, polite, plain)",
750
+ register: "말씨(auto, polite, plain, rough)",
751
751
  safeword: "세이프워드",
752
752
  needsName: "create에는 표시 이름이 필요합니다. 터미널에서 tonedial create를 실행하거나, 이름과 필요한 키=값 설정을 --auto와 함께 지정하세요.",
753
753
  exists: (slug) => `${slug}: 이미 있는 프리셋입니다. 다른 슬러그를 쓰거나 save로 갱신하세요.`,
@@ -799,8 +799,6 @@ const koMessages = {
799
799
  addressNone: "현재 설정 없음 · 엔터를 누르면 유지",
800
800
  age: (given, bands) => `persona.age=${given} · 사용 가능: ${bands.join(", ")}`,
801
801
  gender: (options) => `persona.gender · 사용 가능: ${options.join(", ")}`,
802
- noteOverCap: (given, cap) => `persona.note: ${given}자 · 권장 ${cap}자 초과`,
803
- noteNamesAxis: (axes) => `persona.note가 ${axes.join(", ")} 축과 겹칩니다. 축에는 앵커가 있으므로 충돌하면 축이 우선합니다`,
804
802
  // Labels rather than clauses, for the same reason as `autoChose` above: an address is
805
803
  // free-form user text in any language, and `전하이라고` is what attaching `이라고` to it
806
804
  // produced — 전하 ends in a vowel and takes `라고`.
@@ -810,16 +808,16 @@ const koMessages = {
810
808
  descriptionOverCap: (field, given, cap) => `${field}: ${given}자 · 최대 ${cap}자`,
811
809
  descriptionChars: (field, count) => `${field === "user" ? "사용자 배경" : "가상 에이전트 역할"} · ${count}자`,
812
810
  descriptionCounts: (user, agent, cap) => `역할 · 사용자 ${user}/${cap} · 에이전트 ${agent}/${cap}`,
813
- descriptionFull: (field, value) => `${field === "user" ? "사용자 배경" : "가상 에이전트 역할"}: ${value}`,
811
+ descriptionFull: (field, value) => `${field === "user" ? "사용자 배경" : field === "agent" ? "가상 에이전트 역할" : "추가 말투·역할"}: ${value}`,
814
812
  },
815
813
  settings: {
816
814
  guardsRetired: "guards 설정은 더 이상 쓰지 않습니다. ToneDial은 가드 요청을 넣지 않습니다. 설정은 바꾸지 않았습니다.",
817
815
  fixedWidthRetired: "fixed_width 설정은 더 이상 쓰지 않습니다. 출력은 터미널 폭에 자동으로 맞춥니다. 설정은 바꾸지 않았습니다.",
818
816
  speechLevel: (options) => `speech_level · 사용 가능: ${options.join(", ")}`,
819
817
  safewordShort: (phrase) => `세이프워드 ${phrase}: 너무 짧아 실수로 작동할 수 있습니다`,
820
- safewordSet: (phrase) => `세이프워드 ${phrase} — 이상할 때 한 메시지만 담백하게 답해 달라는 요청입니다. 톤을 끄거나 권한을 주지는 않습니다`,
821
- noSafeword: "세이프워드 없음 — 담백한 답은 스킬로만 요청할 수 있습니다",
822
- register: (level) => `말씨 ${level}`,
818
+ safewordSet: (phrase) => `세이프워드 ${phrase}`,
819
+ noSafeword: "세이프워드 없음",
820
+ register: (level) => `말씨 ${level} (${({ auto: "관계에 맞게 선택", polite: "존댓말", plain: "일반 반말", rough: "거친 반말" })[level]})`,
823
821
  },
824
822
  artifact: {
825
823
  size: (chars) => `TONE.md ${chars}자`,
@@ -920,14 +918,14 @@ const koMessages = {
920
918
  inspectScope: "계속 사용하려면 이 범위를 검사하세요:",
921
919
  disconnectScope: "더 이상 필요 없으면 이 범위의 모든 에이전트 연결을 해제하세요:",
922
920
  scope: "검사 범위: 프로젝트",
923
- toLookAt: (count) => `확인 필요 ${count}건 · 가능한 항목은 --fix로 복구`,
921
+ toLookAt: (count) => `점검 필요 ${count}건`,
924
922
  needsAPerson: "직접 확인 필요 · 위 세부 내용을 확인해 주세요",
925
923
  toneMatches: "config.json과 일치",
926
924
  toneNotWritten: "파일 없음",
927
925
  toneDisagrees: "config.json과 다름 · 직접 수정했거나 저장이 중단됨",
926
+ seedsHeading: "기본 파일",
928
927
  seedsOk: (version) => `버전 ${version}`,
929
928
  seedsNotRun: "기본 파일 없음 · init으로 준비하세요",
930
- seedsStale: (labels) => `갱신 필요 ${labels.length}개: ${labels.join(", ")}`,
931
929
  seedsAttention: "기본 파일 확인 필요",
932
930
  recoveryPending: "진행 중이거나 복구가 필요한 작업이 있습니다. 아무것도 바꾸지 않았습니다. tonedial doctor --json으로 ID를 확인하세요. 기록된 롤백은 doctor --fix --rollback ID로, 중단된 유효한 작업을 선택해 앱을 다시 만들 때는 doctor --fix --from-operation ID로 진행하세요. 실행 중이거나 소유자가 불확실한 작업은 유지합니다.",
933
931
  contextCurrent: "AGENTS.md와 TONE.md 구성이 최신입니다.",
@@ -963,13 +961,12 @@ const koMessages = {
963
961
  rollbackAction: { applied: "복구", "already-reverted": "이미 복구됨", changed: "변경됨 · 보존", unsupported: "지원하지 않음 · 보존" },
964
962
  rollbackQuestion: "선택한 작업을 복구할까요?",
965
963
  seedReason: {
966
- missing: "파일 없음 · 필요한 에이전트를 선택해 init을 실행해 주세요",
967
- stale: "이전 버전의 기본 파일 · init으로 갱신할 내용을 확인해 주세요",
968
- modified: "직접 수정한 파일 · 교체하기 전에 변경 내용을 확인해 주세요",
969
- foreign: "ToneDial이 관리하는 파일이 아닙니다. 파일을 보존하고 전용 상태 디렉터리를 선택해 주세요.",
970
- unreadable: "안전하게 읽을 수 없습니다. 경로와 접근 권한을 확인해 주세요.",
964
+ missing: "기본 파일 없음",
965
+ stale: "기본 파일 업데이트 가능",
966
+ modified: "직접 수정한 파일 · 교체 전 확인 필요",
967
+ foreign: "ToneDial 관리 파일 아님 · 보존됨",
968
+ unreadable: "안전하게 읽을 수 없음 · 경로와 접근 권한 확인 필요",
971
969
  },
972
- seedsBehind: (had, has) => `홈 기본 파일 버전 ${had} · 패키지 버전 ${has}. npm 업데이트만으로는 홈의 기존 파일이 갱신되지 않습니다`,
973
970
  skillLinked: "기본 파일 사본에 연결됨",
974
971
  skillCopied: "복사됨 · 이 플랫폼은 링크를 쓰지 않음",
975
972
  skillForeign: (path) => `${path}: ToneDial이 설치한 스킬이 아니므로 교체하지 않습니다`,
@@ -986,9 +983,7 @@ const koMessages = {
986
983
  },
987
984
  hookSettingScope: "훅 상태는 검사한 설정 파일만 읽어 표시합니다. 앱의 설정 덮어쓰기와 세션 전달 여부는 확인하지 않습니다.",
988
985
  hookGroupHeading: "에이전트 훅",
989
- hookGroupUnknown: (tools) => `다음 에이전트의 훅 활성화 여부를 확인할 수 없습니다: ${tools.join(", ")}`,
990
- hookGroupMissing: "이 검사 범위에는 ToneDial 관리 훅이 등록되어 있지 않습니다.",
991
- hookGroupAction: "ToneDial을 연결하려면 같은 범위에서 init을 실행하고 이 에이전트들을 선택하세요.",
986
+ hookGroupUnknown: (tools) => `연결 안 됨: ${tools.join(", ")}`,
992
987
  hookNotConnected: "검사할 ToneDial 관리 훅이 없습니다. 연결하려면 같은 범위에서 init을 실행하고 이 에이전트를 선택해 주세요.",
993
988
  hookNeedsInspection: "위에 나온 훅 설정이나 실행 프로그램 문제를 먼저 확인한 뒤 앱에서 활성화 여부를 확인해 주세요.",
994
989
  hookGuidance: {
@@ -1032,24 +1027,37 @@ const koMessages = {
1032
1027
  "전역 경로 확인: npm prefix -g\n그 경로의 bin 폴더(macOS/Linux)를 PATH에 추가하세요.",
1033
1028
  },
1034
1029
  notice: {
1035
- modelExpectation: "사용하는 모델에 따라 기대한 말투가 나오지 않을 수 있습니다.",
1036
1030
  riskIntro: "재미를 위한 도구로 실무 사용은 권장하지 않습니다. 위험한 말투에 거짓·누락·과장이 섞여 판단과 작업 보고가 왜곡될 수 있습니다. !는 주의 표시이며 측정한 확률이 아닙니다.",
1037
1031
  riskAxes: (axes) => `! ${axes.join(", ")} · ${[
1038
1032
  axes.some(axis => axis === "profanity" || axis === "critique") ? "거친 표현" : null,
1039
1033
  axes.some(axis => axis === "flattery" || axis === "conviction" || axis === "bravado") ? "치우친 판단·과장된 보고" : null,
1040
1034
  axes.some(axis => axis === "compliance" || axis === "refusal") ? "성급한 실행·거절" : null,
1041
1035
  ].filter(Boolean).join(" · ")}`,
1042
- personaUnanchored: "페르소나에는 고정된 행동 규칙이 없으며 축 설정이 우선합니다. 설명은 내용에도 영향을 줄 수 있습니다. " +
1043
- "역할극은 자격이나 권한을 주지 않습니다.",
1044
1036
  roleplay: "playfulness가 높으면 '아뢰옵기 황송하오나', '이 함수, 장부에서 지울까요?' 같은 역할 어휘와 은어를 더 자주, 과장해서 쓰도록 요청합니다. 무례하거나 거칠게 들릴 수 있으며, ToneDial은 이런 표현을 걸러내지 않습니다.",
1045
1037
  risk: "재미를 위한 도구이며 실무 사용은 권장하지 않습니다",
1046
1038
  },
1047
1039
  update: {
1048
1040
  running: (version) => `tonedial ${version}`,
1049
- installedBy: (shape) => `설치 방식 ${shape}`,
1050
- toUpdate: "업데이트하려면:",
1051
- thenRefresh: "저장된 설정을 유지하면서 앱 파일을 확인하고 다시 준비하세요:",
1052
- neverInitialised: "init을 실행한 기록 없음 · 홈에서 갱신할 파일 없음",
1041
+ toUpdate: "패키지 업데이트:",
1042
+ thenRefresh: "앱 파일을 업데이트하려면:",
1043
+ setupNeeded: "ToneDial 초기 설정:",
1044
+ refreshFiles: (count) => `업데이트 가능한 기본 파일 ${count}개`,
1045
+ reviewState: "문제가 있는 앱 파일을 검사하려면:",
1046
+ npmQuestion: " npm으로 업데이트할까요?",
1047
+ npmComplete: (version) => `npm 업데이트 완료 · 설치 버전 ${version}`,
1048
+ npmMissing: "PATH에서 npm을 찾지 못했습니다. 현재 ToneDial을 설치한 Node/npm 환경을 사용해 주세요.",
1049
+ npmScopeUnknown: "npm의 전역 설치 경로를 확인할 수 없습니다. npm prefix -g와 npm root -g를 확인한 뒤 다시 시도해 주세요.",
1050
+ npmScopeMismatch: "현재 ToneDial은 선택한 npm이 관리하는 전역 패키지가 아닙니다. 설치할 때 사용한 Node/npm 환경을 선택해 주세요. 업데이트는 실행하지 않았습니다.",
1051
+ npmFailed: "npm 업데이트에 실패했습니다. npm의 접근 권한과 레지스트리 설정을 확인해 주세요. 표시된 명령을 직접 실행하면 npm의 오류 내용을 볼 수 있습니다.",
1052
+ npmPermissionDenied: (code, prefix) => `npm 업데이트에 필요한 쓰기 권한이 없습니다 (${code}).\n${prefix}\n설정과 프리셋을 백업한 뒤 사용자 소유 경로에 다시 설치해 주세요.\n복구 절차:\nhttps://github.com/awesometic/tonedial/blob/main/docs/USAGE.md#npm-permissions`,
1053
+ versionUnknown: "설치된 ToneDial 버전을 확인할 수 없습니다. 패키지 설치 상태를 확인한 뒤 다시 시도해 주세요.",
1054
+ stateIssue: {
1055
+ missing: "저장된 설정이 없습니다.",
1056
+ unsafe: "저장된 설정을 안전하게 읽을 수 없습니다. 링크와 파일 형식을 확인해 주세요.",
1057
+ changed: "검사 중 저장된 설정이 바뀌었습니다. 변경이 끝난 뒤 다시 시도해 주세요.",
1058
+ invalid: "저장된 설정이 올바르지 않습니다.",
1059
+ unreadable: "앱 상태를 읽지 못했습니다. 경로와 접근 권한을 확인해 주세요.",
1060
+ },
1053
1061
  fetching: (registry) => `확인 중 · ${registry}`,
1054
1062
  found: (version, bytes) => `${version}, ${bytes.toLocaleString("ko")}바이트`,
1055
1063
  already: (version) => `설치 버전 ${version} · 이미 설치됨`,
@@ -1057,11 +1065,10 @@ const koMessages = {
1057
1065
  question: (version) => ` 대상 버전 ${version} · 이 설치를 바꿀까요?`,
1058
1066
  replaced: (version) => `업데이트됨 · ${version}`,
1059
1067
  declined: "취소됨 · 기존 설치 유지",
1060
- onlyCurl: (shape) => `${shape} 설치 · 자체 업데이트 경로 사용`,
1061
1068
  noTar: "업데이트에 필요한 tar를 PATH에서 찾지 못했습니다",
1062
1069
  notAPackage: "다운로드한 파일이 ToneDial 패키지가 아닙니다",
1063
1070
  linkInPackage: "릴리스에 심볼릭 링크가 있지만 배포물에는 필요하지 않습니다",
1064
- needsApproval: "설치를 바꾸려면 승인이 필요한데 물어볼 터미널이 없습니다. --auto로 다시 실행하세요.",
1071
+ needsApproval: "업데이트 승인 필요 · 실행하지 않았습니다. 승인하려면 tonedial update --auto를 실행하세요.",
1065
1072
  integrityMismatch: "다운로드한 파일의 해시가 레지스트리와 다릅니다",
1066
1073
  notOurs: (dir) => `${dir}: ToneDial이 만든 항목이 아니므로 직접 치워 주세요`,
1067
1074
  swapFailed: (reason) => `설치를 중단했습니다: ${reason}. 다시 시도하기 전에 복구 안내를 확인하세요.`,
@@ -1077,6 +1084,7 @@ const koMessages = {
1077
1084
  commands: {
1078
1085
  init: "초기 설정과 기본 파일 갱신",
1079
1086
  set: "축·역할·설정 변경 · 인자 없으면 다이얼",
1087
+ get: "현재 설정값 조회 · 키를 지정하면 의미와 선택지도 표시",
1080
1088
  address: "서로 부르는 말 · 인자 없으면 질문",
1081
1089
  persona: "에이전트·사용자 역할 · 인자 없으면 질문",
1082
1090
  reset: "축을 기본값으로 · 축을 안 주면 전부",
@@ -1125,14 +1133,8 @@ const koMessages = {
1125
1133
  },
1126
1134
  combos: {
1127
1135
  contradiction: (rule) => `${rule}: 서로 모순됩니다.`,
1128
- reads: (rule) => `${rule}:`,
1129
1136
  refusalCompliance: "한 축은 거절 선언을 요구하고 다른 축은 군말 없는 실행을 요구합니다. 한쪽을 낮춰 주세요.",
1130
- formalSelfRef: "의례적인 표현과 친근한 자칭이 대비를 만듭니다.",
1131
- casualUserRef: "친근한 표현과 깍듯한 호칭이 대비를 만듭니다.",
1132
- teensFormal: "십대 캐릭터가 의례적인 표현을 쓰면 연극처럼 들릴 수 있습니다.",
1133
- seniorJuniorSelfRef: "설정한 나이와 자칭이 서로 다른 지위를 나타냅니다.",
1134
1137
  nothingBlocked: "막지는 않습니다.",
1135
- addressUnlikely: "짧거나 감정 없는 답변에서는 호칭이 생략될 수 있습니다. 역할극이 강하면 더 자주 나올 수 있습니다.",
1136
1138
  },
1137
1139
  errors: {
1138
1140
  notACommand: (name, known, nearest) => `${name}: 명령이 아닙니다.${nearest ? ` ${nearest} 말씀이신가요?` : ""} ` +
@@ -1294,13 +1296,13 @@ const ko = {
1294
1296
  },
1295
1297
  settingHints: {
1296
1298
  safeword: "한 메시지만 담백하게 답해 달라는 요청 문구",
1297
- speech_level: "높임 체계: auto, polite, plain · 표현의 격식은 formality로 조절",
1299
+ speech_level: "auto: 관계에 맞게 선택 · polite: 존댓말 · plain: 일반 반말 · rough: 거친 반말 · 표현의 격식과 거리감은 formality로 조절",
1298
1300
  language: "CLI 인터페이스 언어 · 에이전트 답변 언어와 무관",
1299
1301
  "persona.age": "모델이 연상할 나이대",
1300
1302
  "persona.gender": "모델이 연상할 성별",
1301
1303
  "persona.self_ref": "에이전트의 자칭",
1302
1304
  "persona.user_ref": "에이전트가 쓰는 사용자 호칭",
1303
- "persona.note": "축으로 표현할 수 없는 역할 · 200자 초과 시 경고",
1305
+ "persona.note": "축으로 표현할 수 없는 역할이나 말투",
1304
1306
  "persona.user": "사용자 배경 설명 · 최대 200자",
1305
1307
  "persona.agent": "가상 에이전트 역할 · 최대 200자 · 경력은 확인하지 않으며 능력·권한을 부여하지 않음",
1306
1308
  },
@@ -289,8 +289,10 @@ export function recoverLegacyTone(text, t) {
289
289
  if (styleMarker && (Number(styleMarker[1]) !== config.axes.verbosity || Number(styleMarker[2]) !== config.axes.care))
290
290
  return null;
291
291
  const speech = rules.at(-1);
292
- config.speech_level = speech === `- ${speechLevels.polite}` ? "polite"
293
- : speech === `- ${speechLevels.plain}` ? "plain" : "auto";
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];
294
296
  const expectedRules = [
295
297
  ...AXIS_ORDER.map((axis) => `- ${anchors[axis][config.axes[axis] - 1]}`),
296
298
  ...(historical ? [
@@ -300,7 +302,7 @@ export function recoverLegacyTone(text, t) {
300
302
  ? ["Grumble without being passive; make the complaint itself clear."] : []),
301
303
  ] : composites(config.axes)).map((line) => `- ${line}`),
302
304
  ...(historical ? [] : [`- ${FORMAT_COMPOSITION}`, `- ${anchors === ANCHORS ? REGISTER_COMPOSITION : PREVIOUS_REGISTER_COMPOSITION}`]),
303
- ...(speechLevels[config.speech_level] === null ? [] : [`- ${speechLevels[config.speech_level]}`]),
305
+ ...(speechRule === null ? [] : [`- ${speechRule}`]),
304
306
  ];
305
307
  const legacyExpectedRules = [
306
308
  ...AXIS_ORDER.map((axis) => `- ${LEGACY_CURRENT_ANCHORS[axis][config.axes[axis] - 1]}`),
@@ -308,7 +310,7 @@ export function recoverLegacyTone(text, t) {
308
310
  ? config.axes.warmth >= 4 && config.axes.profanity >= 4
309
311
  : config.axes.compliance <= 2 && config.axes.conviction <= 2).map((line) => `- ${line}`),
310
312
  `- ${PREVIOUS_REGISTER_COMPOSITION}`,
311
- ...(speechLevels[config.speech_level] === null ? [] : [`- ${speechLevels[config.speech_level]}`]),
313
+ ...(speechRule === null ? [] : [`- ${speechRule}`]),
312
314
  ];
313
315
  const acceptedRules = historical || styleMarker ? [expectedRules] : [expectedRules, legacyExpectedRules];
314
316
  if (!acceptedRules.some((candidate) => rules.length === candidate.length &&