tonedial 0.1.2 → 0.1.4
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/cli.js +22 -8
- package/dist/commands/doctor.js +27 -4
- package/dist/commands/get.js +41 -0
- package/dist/commands/rebuild.js +37 -16
- package/dist/commands/save.js +1 -1
- package/dist/commands/set.js +12 -26
- package/dist/commands/show.js +24 -19
- package/dist/core/anchors.js +1 -0
- package/dist/core/compile.js +2 -2
- package/dist/core/config.js +20 -8
- package/dist/core/i18n.js +32 -18
- package/dist/core/legacy-tone.js +6 -4
- package/dist/core/locales/es.js +16 -9
- package/dist/core/locales/ja.js +16 -9
- package/dist/core/locales/zh.js +16 -9
- package/dist/core/presets.js +3 -0
- package/dist/core/seeds.js +3 -3
- package/dist/core/selfupdate.js +25 -4
- package/dist/core/skill.js +33 -12
- package/dist/wiring/apply.js +55 -5
- package/package.json +1 -1
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
|
|
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",
|
|
@@ -98,7 +98,7 @@ const enMessages = {
|
|
|
98
98
|
slug: "Slug (lowercase letters, digits, dashes)",
|
|
99
99
|
user: "Your role or background (up to 200 characters)",
|
|
100
100
|
agent: "Agent role (up to 200 characters)",
|
|
101
|
-
register: "
|
|
101
|
+
register: "Honorific forms (auto, polite, plain, rough)",
|
|
102
102
|
safeword: "Safeword",
|
|
103
103
|
needsName: "create needs a display name. Run tonedial create in a terminal, or supply a name and optional key=value settings with --auto.",
|
|
104
104
|
exists: (slug) => `${slug}: a preset already exists. Choose another slug, or use save to update it.`,
|
|
@@ -126,7 +126,11 @@ const enMessages = {
|
|
|
126
126
|
rollbackOrder: "Restore connections first, then application files:",
|
|
127
127
|
noSnapshot: (id) => `${id}: no valid settings snapshot for this state root`,
|
|
128
128
|
retire: (id) => `Retire stopped operation ${id} as history before a new application rebuild. Its external changes are not certified or undone.`,
|
|
129
|
-
|
|
129
|
+
resumeOperation: (id) => `Stopped operation:\n${id}`,
|
|
130
|
+
resumeQuestion: "Type the full operation ID above to continue; Enter cancels:",
|
|
131
|
+
cancelled: "Recovery cancelled. No files were changed.",
|
|
132
|
+
operationMismatch: "The operation ID did not match. No files were changed.",
|
|
133
|
+
failure: (id, phase, path, code) => `Application recovery failed during ${({ prepare: "preparation", write: "file writes", verify: "verification", finalize: "completion" })[phase]} (${code}).${path ? `\n${path}` : ""}\nSome file changes may already be applied. Keep operation ${id}.`,
|
|
130
134
|
},
|
|
131
135
|
persona: {
|
|
132
136
|
defaults: role("I", "you", "The person requesting help with a task.", "A general-purpose assistant helping with the requested task."),
|
|
@@ -156,16 +160,16 @@ const enMessages = {
|
|
|
156
160
|
descriptionOverCap: (field, given, cap) => `${field}: ${given} characters · maximum ${cap}`,
|
|
157
161
|
descriptionChars: (field, count) => `${field === "user" ? "user context" : "fictional agent role"} · ${count} chars`,
|
|
158
162
|
descriptionCounts: (user, agent, cap) => `roles · user ${user}/${cap} · agent ${agent}/${cap}`,
|
|
159
|
-
descriptionFull: (field, value) => `${field === "user" ? "user context" : "fictional agent role"}: ${value}`,
|
|
163
|
+
descriptionFull: (field, value) => `${field === "user" ? "user context" : field === "agent" ? "fictional agent role" : "persona note"}: ${value}`,
|
|
160
164
|
},
|
|
161
165
|
settings: {
|
|
162
166
|
guardsRetired: "guards is retired. ToneDial no longer adds guard requests. No settings were changed.",
|
|
163
167
|
fixedWidthRetired: "fixed_width is retired. Output now follows terminal width automatically. No settings were changed.",
|
|
164
168
|
speechLevel: (options) => `speech_level · available: ${options.join(", ")}`,
|
|
165
169
|
safewordShort: (phrase) => `safeword ${phrase}: too short and may trigger by accident`,
|
|
166
|
-
safewordSet: (phrase) => `safeword ${phrase}
|
|
167
|
-
noSafeword: "no safeword
|
|
168
|
-
register: (level) => `register ${level}`,
|
|
170
|
+
safewordSet: (phrase) => `safeword ${phrase}`,
|
|
171
|
+
noSafeword: "no safeword",
|
|
172
|
+
register: (level) => `register ${level} (${({ auto: "contextual", polite: "honorific", plain: "non-honorific", rough: "rough non-honorific" })[level]})`,
|
|
169
173
|
},
|
|
170
174
|
artifact: {
|
|
171
175
|
size: (chars) => `TONE.md ${chars} characters`,
|
|
@@ -275,7 +279,8 @@ const enMessages = {
|
|
|
275
279
|
seedsOk: (version) => `version ${version}`,
|
|
276
280
|
seedsNotRun: "no bundled files · init has not run here",
|
|
277
281
|
seedsAttention: "bundled files need attention",
|
|
278
|
-
recoveryPending: "An operation is active or needs recovery.
|
|
282
|
+
recoveryPending: "An operation is active or needs recovery. No files were changed by this command.",
|
|
283
|
+
resumeOperation: "Continue application repair:",
|
|
279
284
|
contextCurrent: "AGENTS.md and TONE.md layout is current.",
|
|
280
285
|
contextRepair: "Managed context files need repair. Check the guide and tone-file diagnostics.",
|
|
281
286
|
contextSize: (guide, total) => total === null
|
|
@@ -397,6 +402,7 @@ const enMessages = {
|
|
|
397
402
|
npmScopeUnknown: "Cannot verify npm's global installation directory. Check npm prefix -g and npm root -g before retrying.",
|
|
398
403
|
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
404
|
npmFailed: "npm update failed. Check npm's access and registry settings, then retry the displayed command for npm's diagnostics.",
|
|
405
|
+
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`,
|
|
400
406
|
versionUnknown: "The installed ToneDial version could not be verified. Check the package installation before retrying.",
|
|
401
407
|
stateIssue: {
|
|
402
408
|
missing: "Saved settings are missing.",
|
|
@@ -431,6 +437,7 @@ const enMessages = {
|
|
|
431
437
|
commands: {
|
|
432
438
|
init: "set up and refresh bundled files",
|
|
433
439
|
set: "change axes, role or settings · bare opens dial",
|
|
440
|
+
get: "read current setting values · key adds its meaning and choices",
|
|
434
441
|
address: "set names for each party · bare prompts",
|
|
435
442
|
persona: "set agent and user roles · bare prompts",
|
|
436
443
|
reset: "reset an axis · bare resets all axes",
|
|
@@ -638,7 +645,7 @@ const en = {
|
|
|
638
645
|
},
|
|
639
646
|
settingHints: {
|
|
640
647
|
safeword: "request for one plain answer to one message",
|
|
641
|
-
speech_level: "honorific forms:
|
|
648
|
+
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",
|
|
642
649
|
language: "CLI interface language · unrelated to agent replies",
|
|
643
650
|
"persona.age": "age band the model may associate with",
|
|
644
651
|
"persona.gender": "gender the model may associate with",
|
|
@@ -688,7 +695,7 @@ const koMessages = {
|
|
|
688
695
|
range: (axis, given) => `${axis}: 1~5만 쓸 수 있습니다(입력: ${given})`,
|
|
689
696
|
unknown: (name, nearest) => `${name}: 축이 아닙니다${nearest ? `. ${nearest} 말씀이신가요?` : ""}`,
|
|
690
697
|
notSettable: (name, nearest) => `${name}: 축도 설정도 아닙니다. ${nearest ? `${nearest} 말씀이신가요? ` : ""}` +
|
|
691
|
-
"전체 목록: tonedial
|
|
698
|
+
"전체 목록: tonedial get --help",
|
|
692
699
|
reset: (axis, level) => `${axis}: 기본값 ${level}`,
|
|
693
700
|
resetAll: "모든 축을 기본값으로 되돌렸습니다. 페르소나와 설정은 유지했습니다.",
|
|
694
701
|
nothingChanged: "변경 없음",
|
|
@@ -745,7 +752,7 @@ const koMessages = {
|
|
|
745
752
|
slug: "슬러그(영문 소문자·숫자·붙임표)",
|
|
746
753
|
user: "사용자 역할·배경(최대 200자)",
|
|
747
754
|
agent: "에이전트 역할(최대 200자)",
|
|
748
|
-
register: "말씨(auto, polite, plain)",
|
|
755
|
+
register: "말씨(auto, polite, plain, rough)",
|
|
749
756
|
safeword: "세이프워드",
|
|
750
757
|
needsName: "create에는 표시 이름이 필요합니다. 터미널에서 tonedial create를 실행하거나, 이름과 필요한 키=값 설정을 --auto와 함께 지정하세요.",
|
|
751
758
|
exists: (slug) => `${slug}: 이미 있는 프리셋입니다. 다른 슬러그를 쓰거나 save로 갱신하세요.`,
|
|
@@ -773,7 +780,11 @@ const koMessages = {
|
|
|
773
780
|
rollbackOrder: "연결 파일을 먼저 되돌린 뒤 앱 파일을 되돌리세요:",
|
|
774
781
|
noSnapshot: (id) => `${id}: 이 상태 경로에서 쓸 수 있는 설정 기록이 없습니다`,
|
|
775
782
|
retire: (id) => `중단된 작업 ${id}를 기록으로 보관한 뒤 새 앱 복구를 시작합니다. 그 작업의 외부 파일 변경은 확인하거나 되돌리지 않습니다.`,
|
|
776
|
-
|
|
783
|
+
resumeOperation: (id) => `중단된 작업:\n${id}`,
|
|
784
|
+
resumeQuestion: "계속하려면 위 작업 ID 전체를 입력하세요. Enter를 누르면 취소합니다:",
|
|
785
|
+
cancelled: "복구를 취소했습니다. 파일은 바꾸지 않았습니다.",
|
|
786
|
+
operationMismatch: "작업 ID가 일치하지 않습니다. 파일은 바꾸지 않았습니다.",
|
|
787
|
+
failure: (id, phase, path, code) => `앱 복구의 ${({ prepare: "준비", write: "파일 쓰기", verify: "결과 검증", finalize: "완료 처리" })[phase]} 단계에서 실패했습니다 (${code}).${path ? `\n${path}` : ""}\n일부 파일 변경이 이미 적용되었을 수 있습니다. 작업 ${id}의 기록을 보존해 주세요.`,
|
|
777
788
|
},
|
|
778
789
|
persona: {
|
|
779
790
|
defaults: role("저", "사용자님", "작업에 필요한 도움을 요청하는 사용자.", "요청한 작업을 돕는 범용 조수."),
|
|
@@ -806,16 +817,16 @@ const koMessages = {
|
|
|
806
817
|
descriptionOverCap: (field, given, cap) => `${field}: ${given}자 · 최대 ${cap}자`,
|
|
807
818
|
descriptionChars: (field, count) => `${field === "user" ? "사용자 배경" : "가상 에이전트 역할"} · ${count}자`,
|
|
808
819
|
descriptionCounts: (user, agent, cap) => `역할 · 사용자 ${user}/${cap} · 에이전트 ${agent}/${cap}`,
|
|
809
|
-
descriptionFull: (field, value) => `${field === "user" ? "사용자 배경" : "가상 에이전트 역할"}: ${value}`,
|
|
820
|
+
descriptionFull: (field, value) => `${field === "user" ? "사용자 배경" : field === "agent" ? "가상 에이전트 역할" : "추가 말투·역할"}: ${value}`,
|
|
810
821
|
},
|
|
811
822
|
settings: {
|
|
812
823
|
guardsRetired: "guards 설정은 더 이상 쓰지 않습니다. ToneDial은 가드 요청을 넣지 않습니다. 설정은 바꾸지 않았습니다.",
|
|
813
824
|
fixedWidthRetired: "fixed_width 설정은 더 이상 쓰지 않습니다. 출력은 터미널 폭에 자동으로 맞춥니다. 설정은 바꾸지 않았습니다.",
|
|
814
825
|
speechLevel: (options) => `speech_level · 사용 가능: ${options.join(", ")}`,
|
|
815
826
|
safewordShort: (phrase) => `세이프워드 ${phrase}: 너무 짧아 실수로 작동할 수 있습니다`,
|
|
816
|
-
safewordSet: (phrase) => `세이프워드 ${phrase}
|
|
817
|
-
noSafeword: "세이프워드 없음
|
|
818
|
-
register: (level) => `말씨 ${level}`,
|
|
827
|
+
safewordSet: (phrase) => `세이프워드 ${phrase}`,
|
|
828
|
+
noSafeword: "세이프워드 없음",
|
|
829
|
+
register: (level) => `말씨 ${level} (${({ auto: "관계에 맞게 선택", polite: "존댓말", plain: "일반 반말", rough: "거친 반말" })[level]})`,
|
|
819
830
|
},
|
|
820
831
|
artifact: {
|
|
821
832
|
size: (chars) => `TONE.md ${chars}자`,
|
|
@@ -925,7 +936,8 @@ const koMessages = {
|
|
|
925
936
|
seedsOk: (version) => `버전 ${version}`,
|
|
926
937
|
seedsNotRun: "기본 파일 없음 · init으로 준비하세요",
|
|
927
938
|
seedsAttention: "기본 파일 확인 필요",
|
|
928
|
-
recoveryPending: "진행 중이거나 복구가 필요한 작업이 있습니다.
|
|
939
|
+
recoveryPending: "진행 중이거나 복구가 필요한 작업이 있습니다. 이번 실행에서는 파일을 바꾸지 않았습니다.",
|
|
940
|
+
resumeOperation: "앱 복구를 이어가려면:",
|
|
929
941
|
contextCurrent: "AGENTS.md와 TONE.md 구성이 최신입니다.",
|
|
930
942
|
contextRepair: "관리 중인 컨텍스트 파일을 복구해야 합니다. 안내 파일과 톤 파일의 진단을 확인해 주세요.",
|
|
931
943
|
contextSize: (guide, total) => total === null
|
|
@@ -1047,6 +1059,7 @@ const koMessages = {
|
|
|
1047
1059
|
npmScopeUnknown: "npm의 전역 설치 경로를 확인할 수 없습니다. npm prefix -g와 npm root -g를 확인한 뒤 다시 시도해 주세요.",
|
|
1048
1060
|
npmScopeMismatch: "현재 ToneDial은 선택한 npm이 관리하는 전역 패키지가 아닙니다. 설치할 때 사용한 Node/npm 환경을 선택해 주세요. 업데이트는 실행하지 않았습니다.",
|
|
1049
1061
|
npmFailed: "npm 업데이트에 실패했습니다. npm의 접근 권한과 레지스트리 설정을 확인해 주세요. 표시된 명령을 직접 실행하면 npm의 오류 내용을 볼 수 있습니다.",
|
|
1062
|
+
npmPermissionDenied: (code, prefix) => `npm 업데이트에 필요한 쓰기 권한이 없습니다 (${code}).\n${prefix}\n설정과 프리셋을 백업한 뒤 사용자 소유 경로에 다시 설치해 주세요.\n복구 절차:\nhttps://github.com/awesometic/tonedial/blob/main/docs/USAGE.md#npm-permissions`,
|
|
1050
1063
|
versionUnknown: "설치된 ToneDial 버전을 확인할 수 없습니다. 패키지 설치 상태를 확인한 뒤 다시 시도해 주세요.",
|
|
1051
1064
|
stateIssue: {
|
|
1052
1065
|
missing: "저장된 설정이 없습니다.",
|
|
@@ -1081,6 +1094,7 @@ const koMessages = {
|
|
|
1081
1094
|
commands: {
|
|
1082
1095
|
init: "초기 설정과 기본 파일 갱신",
|
|
1083
1096
|
set: "축·역할·설정 변경 · 인자 없으면 다이얼",
|
|
1097
|
+
get: "현재 설정값 조회 · 키를 지정하면 의미와 선택지도 표시",
|
|
1084
1098
|
address: "서로 부르는 말 · 인자 없으면 질문",
|
|
1085
1099
|
persona: "에이전트·사용자 역할 · 인자 없으면 질문",
|
|
1086
1100
|
reset: "축을 기본값으로 · 축을 안 주면 전부",
|
|
@@ -1292,7 +1306,7 @@ const ko = {
|
|
|
1292
1306
|
},
|
|
1293
1307
|
settingHints: {
|
|
1294
1308
|
safeword: "한 메시지만 담백하게 답해 달라는 요청 문구",
|
|
1295
|
-
speech_level: "
|
|
1309
|
+
speech_level: "auto: 관계에 맞게 선택 · polite: 존댓말 · plain: 일반 반말 · rough: 거친 반말 · 표현의 격식과 거리감은 formality로 조절",
|
|
1296
1310
|
language: "CLI 인터페이스 언어 · 에이전트 답변 언어와 무관",
|
|
1297
1311
|
"persona.age": "모델이 연상할 나이대",
|
|
1298
1312
|
"persona.gender": "모델이 연상할 성별",
|
package/dist/core/legacy-tone.js
CHANGED
|
@@ -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 === `- ${
|
|
293
|
-
: speech === `- ${speechLevels.
|
|
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
|
-
...(
|
|
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
|
-
...(
|
|
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 &&
|
package/dist/core/locales/es.js
CHANGED
|
@@ -30,7 +30,7 @@ const messages = {
|
|
|
30
30
|
range: (axis, given) => `${axis}: se esperaba 1–5 (se recibió ${given})`,
|
|
31
31
|
unknown: (name, nearest) => `${name}: no es un eje${nearest ? `. ¿Querías decir ${nearest}?` : ""}`,
|
|
32
32
|
notSettable: (name, nearest) => `${name}: no es un eje ni un ajuste. ${nearest ? `¿Querías decir ${nearest}? ` : ""}` +
|
|
33
|
-
"Lista completa: tonedial
|
|
33
|
+
"Lista completa: tonedial get --help",
|
|
34
34
|
reset: (axis, level) => `${axis}: valor predeterminado ${level}`,
|
|
35
35
|
resetAll: "Se restablecieron todos los ejes; se conservaron la personalidad y los ajustes.",
|
|
36
36
|
nothingChanged: "sin cambios",
|
|
@@ -87,7 +87,7 @@ const messages = {
|
|
|
87
87
|
slug: "Identificador (minúsculas, dígitos y guiones)",
|
|
88
88
|
user: "Tu rol o contexto (hasta 200 caracteres)",
|
|
89
89
|
agent: "Rol del agente (hasta 200 caracteres)",
|
|
90
|
-
register: "Registro (auto, polite, plain)",
|
|
90
|
+
register: "Registro (auto, polite, plain, rough)",
|
|
91
91
|
safeword: "Palabra clave",
|
|
92
92
|
needsName: "create necesita un nombre visible. Ejecuta tonedial create en un terminal o indica un nombre y ajustes key=value opcionales con --auto.",
|
|
93
93
|
exists: (slug) => `${slug}: ya existe un preajuste. Elige otro identificador o actualízalo con save.`,
|
|
@@ -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
|
-
|
|
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."),
|
|
@@ -145,16 +149,16 @@ const messages = {
|
|
|
145
149
|
descriptionOverCap: (field, given, cap) => `${field}: ${given} caracteres · máximo ${cap}`,
|
|
146
150
|
descriptionChars: (field, count) => `${field === "user" ? "contexto del usuario" : "rol ficticio del agente"} · ${count} caracteres`,
|
|
147
151
|
descriptionCounts: (user, agent, cap) => `Roles · usuario ${user}/${cap} · agente ${agent}/${cap}`,
|
|
148
|
-
descriptionFull: (field, value) => `${field === "user" ? "contexto del usuario" : "rol ficticio del agente"}: ${value}`,
|
|
152
|
+
descriptionFull: (field, value) => `${field === "user" ? "contexto del usuario" : field === "agent" ? "rol ficticio del agente" : "nota de personalidad"}: ${value}`,
|
|
149
153
|
},
|
|
150
154
|
settings: {
|
|
151
155
|
guardsRetired: "guards se ha retirado. ToneDial ya no añade solicitudes de protección. No se cambió ningún ajuste.",
|
|
152
156
|
fixedWidthRetired: "fixed_width se ha retirado. La salida se adapta al ancho del terminal. No se cambió ningún ajuste.",
|
|
153
157
|
speechLevel: (options) => `speech_level · disponibles: ${options.join(", ")}`,
|
|
154
158
|
safewordShort: (phrase) => `Palabra clave ${phrase}: demasiado corta; podría activarse por accidente`,
|
|
155
|
-
safewordSet: (phrase) => `Palabra clave ${phrase}
|
|
156
|
-
noSafeword: "sin palabra clave
|
|
157
|
-
register: (level) => `registro ${level}`,
|
|
159
|
+
safewordSet: (phrase) => `Palabra clave ${phrase}`,
|
|
160
|
+
noSafeword: "sin palabra clave",
|
|
161
|
+
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]})`,
|
|
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
|
|
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
|
|
@@ -382,6 +387,7 @@ const messages = {
|
|
|
382
387
|
npmScopeUnknown: "No se puede verificar el directorio global de npm. Comprueba npm prefix -g y npm root -g antes de reintentar.",
|
|
383
388
|
npmScopeMismatch: "Esta copia de ToneDial no es el paquete global gestionado por el npm seleccionado. Usa la instalación de Node/npm correspondiente. No se ejecutó la actualización.",
|
|
384
389
|
npmFailed: "La actualización de npm falló. Comprueba los permisos y el registro, y ejecuta el comando mostrado para ver el diagnóstico de npm.",
|
|
390
|
+
npmPermissionDenied: (code, prefix) => `La actualización de npm necesita permiso de escritura (${code}).\n${prefix}\nHaz una copia de seguridad de los ajustes y preajustes, y reinstala en un directorio de tu usuario.\nPasos de recuperación:\nhttps://github.com/awesometic/tonedial/blob/main/docs/USAGE.md#npm-permissions`,
|
|
385
391
|
versionUnknown: "No se pudo verificar la versión instalada de ToneDial. Comprueba la instalación del paquete antes de reintentar.",
|
|
386
392
|
stateIssue: {
|
|
387
393
|
missing: "Faltan los ajustes guardados.",
|
|
@@ -416,6 +422,7 @@ const messages = {
|
|
|
416
422
|
commands: {
|
|
417
423
|
init: "configurar y actualizar archivos incluidos",
|
|
418
424
|
set: "cambiar ejes, rol o ajustes · sin argumentos abre el dial",
|
|
425
|
+
get: "consultar valores actuales · una clave muestra su significado y opciones",
|
|
419
426
|
address: "definir los nombres de ambas partes · sin argumentos pregunta",
|
|
420
427
|
persona: "definir roles del agente y usuario · sin argumentos pregunta",
|
|
421
428
|
reset: "restablecer un eje · sin argumentos restablece todos",
|
|
@@ -621,7 +628,7 @@ export const es = {
|
|
|
621
628
|
},
|
|
622
629
|
settingHints: {
|
|
623
630
|
safeword: "petición de una respuesta sencilla para un solo mensaje",
|
|
624
|
-
speech_level: "
|
|
631
|
+
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",
|
|
625
632
|
language: "idioma de la interfaz CLI · independiente de las respuestas del agente",
|
|
626
633
|
"persona.age": "franja de edad que puede asociar el modelo",
|
|
627
634
|
"persona.gender": "género que puede asociar el modelo",
|
package/dist/core/locales/ja.js
CHANGED
|
@@ -30,7 +30,7 @@ const messages = {
|
|
|
30
30
|
range: (axis, given) => `${axis}: 1–5を指定してください(入力:${given})`,
|
|
31
31
|
unknown: (name, nearest) => `${name}: 軸ではありません${nearest ? `。${nearest} のことですか?` : ""}`,
|
|
32
32
|
notSettable: (name, nearest) => `${name}: 軸でも設定項目でもありません。${nearest ? `${nearest} のことですか? ` : ""}` +
|
|
33
|
-
"全項目:tonedial
|
|
33
|
+
"全項目:tonedial get --help",
|
|
34
34
|
reset: (axis, level) => `${axis}: 既定値 ${level}`,
|
|
35
35
|
resetAll: "全軸を既定値に戻しました。ペルソナと設定は保持しました。",
|
|
36
36
|
nothingChanged: "変更なし",
|
|
@@ -87,7 +87,7 @@ const messages = {
|
|
|
87
87
|
slug: "スラッグ(英小文字・数字・ハイフン)",
|
|
88
88
|
user: "あなたの役割や背景(200文字まで)",
|
|
89
89
|
agent: "エージェントの役割(200文字まで)",
|
|
90
|
-
register: "敬語区分(auto, polite, plain)",
|
|
90
|
+
register: "敬語区分(auto, polite, plain, rough)",
|
|
91
91
|
safeword: "合言葉",
|
|
92
92
|
needsName: "create には表示名が必要です。ターミナルで tonedial create を実行するか、名前と任意の key=value 設定を --auto とともに指定してください。",
|
|
93
93
|
exists: (slug) => `${slug}: 既に存在します。別のスラッグを選ぶか、save で更新してください。`,
|
|
@@ -115,7 +115,11 @@ const messages = {
|
|
|
115
115
|
rollbackOrder: "先に接続を復元し、その後アプリケーションのファイルを復元してください:",
|
|
116
116
|
noSnapshot: (id) => `${id}: この状態ルート用の有効な設定スナップショットがありません`,
|
|
117
117
|
retire: (id) => `停止した操作 ${id} を履歴に移してから再構築してください。外部への変更は検証も取り消しもされません。`,
|
|
118
|
-
|
|
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("私", "あなた", "作業の支援を求めている利用者。", "依頼された作業を手伝う汎用アシスタント。"),
|
|
@@ -145,16 +149,16 @@ const messages = {
|
|
|
145
149
|
descriptionOverCap: (field, given, cap) => `${field}: ${given}文字 · 上限 ${cap}`,
|
|
146
150
|
descriptionChars: (field, count) => `${field === "user" ? "ユーザー背景" : "架空の役割"} · ${count}文字`,
|
|
147
151
|
descriptionCounts: (user, agent, cap) => `役割 · ユーザー ${user}/${cap} · エージェント ${agent}/${cap}`,
|
|
148
|
-
descriptionFull: (field, value) => `${field === "user" ? "ユーザー背景" : "架空の役割"}: ${value}`,
|
|
152
|
+
descriptionFull: (field, value) => `${field === "user" ? "ユーザー背景" : field === "agent" ? "架空の役割" : "追加の話し方・役割"}: ${value}`,
|
|
149
153
|
},
|
|
150
154
|
settings: {
|
|
151
155
|
guardsRetired: "guards は廃止されました。ToneDialはガード要求を追加しません。設定は変更していません。",
|
|
152
156
|
fixedWidthRetired: "fixed_width は廃止されました。出力はターミナル幅に自動追従します。設定は変更していません。",
|
|
153
157
|
speechLevel: (options) => `speech_level · 選択肢:${options.join(", ")}`,
|
|
154
158
|
safewordShort: (phrase) => `合言葉 ${phrase}: 短すぎるため、意図せず反応する可能性があります`,
|
|
155
|
-
safewordSet: (phrase) => `合言葉 ${phrase}
|
|
156
|
-
noSafeword: "合言葉なし
|
|
157
|
-
register: (level) => `敬語区分 ${level}`,
|
|
159
|
+
safewordSet: (phrase) => `合言葉 ${phrase}`,
|
|
160
|
+
noSafeword: "合言葉なし",
|
|
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: "
|
|
271
|
+
recoveryPending: "実行中または復旧が必要な操作があります。この実行ではファイルを変更していません。",
|
|
272
|
+
resumeOperation: "アプリ復旧を続けるには:",
|
|
268
273
|
contextCurrent: "AGENTS.md と TONE.md の構成は最新です。",
|
|
269
274
|
contextRepair: "管理対象のコンテキストファイルに修復が必要です。ガイドと口調ファイルの診断を確認してください。",
|
|
270
275
|
contextSize: (guide, total) => total === null
|
|
@@ -382,6 +387,7 @@ const messages = {
|
|
|
382
387
|
npmScopeUnknown: "npmのグローバルインストール先を確認できません。npm prefix -g と npm root -g を確認して再試行してください。",
|
|
383
388
|
npmScopeMismatch: "このToneDialは選択したnpmが管理するグローバルパッケージではありません。対応するNode/npm環境を使用してください。更新は実行していません。",
|
|
384
389
|
npmFailed: "npmの更新に失敗しました。権限とレジストリ設定を確認し、表示されたコマンドを実行してnpmの診断を確認してください。",
|
|
390
|
+
npmPermissionDenied: (code, prefix) => `npmの更新に必要な書き込み権限がありません (${code})。\n${prefix}\n設定とプリセットをバックアップしてから、ユーザー所有の場所に再インストールしてください。\n復旧手順:\nhttps://github.com/awesometic/tonedial/blob/main/docs/USAGE.md#npm-permissions`,
|
|
385
391
|
versionUnknown: "インストールされたToneDialのバージョンを確認できません。パッケージの状態を確認して再試行してください。",
|
|
386
392
|
stateIssue: {
|
|
387
393
|
missing: "保存済み設定がありません。",
|
|
@@ -416,6 +422,7 @@ const messages = {
|
|
|
416
422
|
commands: {
|
|
417
423
|
init: "初期設定と同梱ファイルの更新",
|
|
418
424
|
set: "軸・役割・設定を変更 · 引数なしでダイヤル",
|
|
425
|
+
get: "現在の設定値を確認 · キー指定で意味と選択肢も表示",
|
|
419
426
|
address: "双方の呼称を設定 · 引数なしで質問",
|
|
420
427
|
persona: "エージェントとユーザーの役割を設定 · 引数なしで質問",
|
|
421
428
|
reset: "軸を初期化 · 引数なしで全軸",
|
|
@@ -621,7 +628,7 @@ export const ja = {
|
|
|
621
628
|
},
|
|
622
629
|
settingHints: {
|
|
623
630
|
safeword: "1つのメッセージに一度だけ平易な回答を求める文句",
|
|
624
|
-
speech_level: "
|
|
631
|
+
speech_level: "auto: 関係に合わせる · polite: 敬語 · plain: 通常の常体 · rough: 荒っぽく生意気な常体 · 表現の格式と距離感は formality で調整",
|
|
625
632
|
language: "CLIの表示言語 · エージェントの回答言語とは別",
|
|
626
633
|
"persona.age": "モデルに連想させる年齢層",
|
|
627
634
|
"persona.gender": "モデルに連想させるジェンダー",
|
package/dist/core/locales/zh.js
CHANGED
|
@@ -30,7 +30,7 @@ const messages = {
|
|
|
30
30
|
range: (axis, given) => `${axis}:应为 1–5(收到 ${given})`,
|
|
31
31
|
unknown: (name, nearest) => `${name}:不是语气轴${nearest ? `。是否想输入 ${nearest}?` : ""}`,
|
|
32
32
|
notSettable: (name, nearest) => `${name}:不是语气轴或设置项。${nearest ? `是否想输入 ${nearest}? ` : ""}` +
|
|
33
|
-
"完整列表:tonedial
|
|
33
|
+
"完整列表:tonedial get --help",
|
|
34
34
|
reset: (axis, level) => `${axis}:默认值 ${level}`,
|
|
35
35
|
resetAll: "已将所有语气轴重置为默认值,保留人设和其他设置。",
|
|
36
36
|
nothingChanged: "无变更",
|
|
@@ -87,7 +87,7 @@ const messages = {
|
|
|
87
87
|
slug: "标识名(小写字母、数字、连字符)",
|
|
88
88
|
user: "你的角色或背景(最多 200 字符)",
|
|
89
89
|
agent: "代理角色(最多 200 字符)",
|
|
90
|
-
register: "敬语形式(auto, polite, plain)",
|
|
90
|
+
register: "敬语形式(auto, polite, plain, rough)",
|
|
91
91
|
safeword: "安全词",
|
|
92
92
|
needsName: "create 需要显示名称。请在终端运行 tonedial create,或提供名称、可选的 key=value 设置及 --auto。",
|
|
93
93
|
exists: (slug) => `${slug}:预设已存在。请选择其他标识名,或用 save 更新。`,
|
|
@@ -115,7 +115,11 @@ const messages = {
|
|
|
115
115
|
rollbackOrder: "请先恢复连接,再恢复应用文件:",
|
|
116
116
|
noSnapshot: (id) => `${id}:没有此状态根目录的有效设置快照`,
|
|
117
117
|
retire: (id) => `重建应用前,请将已停止的操作 ${id} 归档为历史。其外部变更不会被认证或撤销。`,
|
|
118
|
-
|
|
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("我", "你", "请求任务协助的用户。", "协助完成所请求任务的通用助手。"),
|
|
@@ -145,16 +149,16 @@ const messages = {
|
|
|
145
149
|
descriptionOverCap: (field, given, cap) => `${field}:${given} 字符 · 上限 ${cap}`,
|
|
146
150
|
descriptionChars: (field, count) => `${field === "user" ? "用户背景" : "虚构代理角色"} · ${count} 字符`,
|
|
147
151
|
descriptionCounts: (user, agent, cap) => `角色 · 用户 ${user}/${cap} · 代理 ${agent}/${cap}`,
|
|
148
|
-
descriptionFull: (field, value) => `${field === "user" ? "用户背景" : "虚构代理角色"}:${value}`,
|
|
152
|
+
descriptionFull: (field, value) => `${field === "user" ? "用户背景" : field === "agent" ? "虚构代理角色" : "补充语气与角色"}:${value}`,
|
|
149
153
|
},
|
|
150
154
|
settings: {
|
|
151
155
|
guardsRetired: "guards 已停用,ToneDial 不再添加防护请求。设置未变更。",
|
|
152
156
|
fixedWidthRetired: "fixed_width 已停用,输出会自动跟随终端宽度。设置未变更。",
|
|
153
157
|
speechLevel: (options) => `speech_level · 可选:${options.join(", ")}`,
|
|
154
158
|
safewordShort: (phrase) => `安全词 ${phrase}:太短,可能误触发`,
|
|
155
|
-
safewordSet: (phrase) => `安全词 ${phrase}
|
|
156
|
-
noSafeword: "未设置安全词
|
|
157
|
-
register: (level) => `敬语形式 ${level}`,
|
|
159
|
+
safewordSet: (phrase) => `安全词 ${phrase}`,
|
|
160
|
+
noSafeword: "未设置安全词",
|
|
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: "
|
|
271
|
+
recoveryPending: "有操作正在运行或需要恢复。本次执行未更改文件。",
|
|
272
|
+
resumeOperation: "继续应用恢复:",
|
|
268
273
|
contextCurrent: "AGENTS.md 与 TONE.md 布局已是最新。",
|
|
269
274
|
contextRepair: "受管理的上下文文件需要修复,请检查指南和语气文件诊断。",
|
|
270
275
|
contextSize: (guide, total) => total === null
|
|
@@ -382,6 +387,7 @@ const messages = {
|
|
|
382
387
|
npmScopeUnknown: "无法确认 npm 的全局安装目录。请检查 npm prefix -g 和 npm root -g 后重试。",
|
|
383
388
|
npmScopeMismatch: "当前 ToneDial 不是所选 npm 管理的全局软件包。请使用对应的 Node/npm 环境。未执行更新。",
|
|
384
389
|
npmFailed: "npm 更新失败。请检查权限和镜像设置,并直接运行显示的命令查看 npm 的诊断信息。",
|
|
390
|
+
npmPermissionDenied: (code, prefix) => `npm 更新缺少写入权限 (${code})。\n${prefix}\n请先备份设置和预设,再重新安装到用户拥有的目录。\n恢复步骤:\nhttps://github.com/awesometic/tonedial/blob/main/docs/USAGE.md#npm-permissions`,
|
|
385
391
|
versionUnknown: "无法确认安装的 ToneDial 版本。请检查软件包的安装状态后重试。",
|
|
386
392
|
stateIssue: {
|
|
387
393
|
missing: "保存的设置缺失。",
|
|
@@ -416,6 +422,7 @@ const messages = {
|
|
|
416
422
|
commands: {
|
|
417
423
|
init: "初始化并刷新内置文件",
|
|
418
424
|
set: "修改语气轴、角色或设置 · 无参数打开面板",
|
|
425
|
+
get: "读取当前设置值 · 指定键可查看含义和选项",
|
|
419
426
|
address: "设置双方称呼 · 无参数则交互输入",
|
|
420
427
|
persona: "设置代理和用户角色 · 无参数则交互输入",
|
|
421
428
|
reset: "重置语气轴 · 无参数重置全部",
|
|
@@ -621,7 +628,7 @@ export const zh = {
|
|
|
621
628
|
},
|
|
622
629
|
settingHints: {
|
|
623
630
|
safeword: "对一条消息请求一次平实回答的短语",
|
|
624
|
-
speech_level: "
|
|
631
|
+
speech_level: "auto:按关系选择 · polite:敬语 · plain:普通非敬语 · rough:粗鲁且带有不逊语气的非敬语 · 措辞和距离感由 formality 调整",
|
|
625
632
|
language: "CLI 界面语言 · 与代理回答语言无关",
|
|
626
633
|
"persona.age": "模型可能联想的年龄段",
|
|
627
634
|
"persona.gender": "模型可能联想的性别",
|
package/dist/core/presets.js
CHANGED
|
@@ -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);
|
package/dist/core/seeds.js
CHANGED
|
@@ -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 =
|
|
14
|
+
export const SEED_VERSION = 20;
|
|
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:
|
|
76
|
+
{ id: "skill/body", version: 20, 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:
|
|
79
|
+
version: 11,
|
|
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,
|
package/dist/core/selfupdate.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Bounded release download and extraction; the candidate installer owns package replacement.
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
-
import { lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { accessSync, constants, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
6
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { gunzipSync } from "node:zlib";
|
|
9
9
|
import { ConfigError } from "./config.js";
|
|
@@ -47,6 +47,7 @@ export function inspectNpmInstall(env, t) {
|
|
|
47
47
|
};
|
|
48
48
|
const prefix = query(["prefix", "-g"]);
|
|
49
49
|
const root = join(query(["root", "-g", "--prefix", prefix]), "tonedial");
|
|
50
|
+
let install;
|
|
50
51
|
try {
|
|
51
52
|
const own = fileURLToPath(new URL("../../", import.meta.url));
|
|
52
53
|
const current = fileIdentity(own);
|
|
@@ -55,11 +56,22 @@ export function inspectNpmInstall(env, t) {
|
|
|
55
56
|
if (lstatSync(root, { throwIfNoEntry: false })?.isSymbolicLink() || current === null || target === null || current.dev !== target.dev || current.ino !== target.ino || version === null) {
|
|
56
57
|
throw new ConfigError(t.update.npmScopeMismatch);
|
|
57
58
|
}
|
|
58
|
-
|
|
59
|
+
install = { entry, prefix, root, version };
|
|
59
60
|
}
|
|
60
61
|
catch {
|
|
61
62
|
throw new ConfigError(t.update.npmScopeMismatch);
|
|
62
63
|
}
|
|
64
|
+
try {
|
|
65
|
+
// npm replaces the package by renaming it in this parent, not by editing its files.
|
|
66
|
+
accessSync(dirname(root), constants.W_OK | constants.X_OK);
|
|
67
|
+
}
|
|
68
|
+
catch (cause) {
|
|
69
|
+
const code = cause.code;
|
|
70
|
+
if (code === "EACCES" || code === "EPERM")
|
|
71
|
+
throw new ConfigError(t.update.npmPermissionDenied(code, prefix));
|
|
72
|
+
throw new ConfigError(t.update.npmScopeUnknown);
|
|
73
|
+
}
|
|
74
|
+
return install;
|
|
63
75
|
}
|
|
64
76
|
/** npm owns replacement; captured output cannot corrupt JSON or disclose npm configuration. */
|
|
65
77
|
export function updateNpmInstall(install, env, t) {
|
|
@@ -67,8 +79,17 @@ export function updateNpmInstall(install, env, t) {
|
|
|
67
79
|
if (current.entry !== install.entry || current.prefix !== install.prefix || current.root !== install.root || current.version !== install.version)
|
|
68
80
|
throw new ConfigError(t.update.npmScopeMismatch);
|
|
69
81
|
const result = spawnSync(install.entry, ["update", "-g", "tonedial", "--prefix", install.prefix, "--json"], { env, encoding: "utf8", timeout: 120_000, maxBuffer: RELEASE_MAX_BYTES, stdio: ["ignore", "pipe", "pipe"] });
|
|
70
|
-
if (result.status !== 0)
|
|
82
|
+
if (result.status !== 0) {
|
|
83
|
+
let code;
|
|
84
|
+
try {
|
|
85
|
+
code = JSON.parse(result.stdout ?? "null")?.error?.code;
|
|
86
|
+
}
|
|
87
|
+
catch { /* npm may fail before JSON output. */ }
|
|
88
|
+
code ??= /^npm (?:error|ERR!) code (EACCES|EPERM)\s*$/m.exec(result.stderr ?? "")?.[1];
|
|
89
|
+
if (code === "EACCES" || code === "EPERM")
|
|
90
|
+
throw new ConfigError(t.update.npmPermissionDenied(code, install.prefix));
|
|
71
91
|
throw new ConfigError(t.update.npmFailed);
|
|
92
|
+
}
|
|
72
93
|
const version = installedVersion({ lib: install.root });
|
|
73
94
|
if (version === null)
|
|
74
95
|
throw new ConfigError(t.update.versionUnknown);
|