tonedial 0.1.0 → 0.1.2
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 +5 -2
- package/dist/commands/compile.js +3 -12
- package/dist/commands/create.js +7 -7
- package/dist/commands/doctor.js +23 -16
- package/dist/commands/edit.js +10 -11
- package/dist/commands/init.js +8 -14
- package/dist/commands/save.js +5 -13
- package/dist/commands/set.js +85 -62
- package/dist/commands/show.js +20 -30
- package/dist/commands/transfer.js +0 -1
- package/dist/commands/update.js +89 -42
- package/dist/commands/use.js +6 -8
- package/dist/commands/wire.js +1 -1
- package/dist/core/anchors.js +0 -3
- package/dist/core/combos.js +6 -38
- package/dist/core/compile.js +1 -5
- package/dist/core/config.js +0 -1
- package/dist/core/i18n.js +70 -72
- package/dist/core/locales/es.js +35 -36
- package/dist/core/locales/ja.js +35 -36
- package/dist/core/locales/zh.js +35 -36
- package/dist/core/seeds.js +3 -3
- package/dist/core/selfupdate.js +47 -2
- package/dist/core/skill.js +9 -6
- package/dist/ui/format.js +5 -8
- package/package.json +1 -1
package/dist/core/selfupdate.js
CHANGED
|
@@ -3,10 +3,12 @@ import { spawnSync } from "node:child_process";
|
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
|
-
import { join } from "node:path";
|
|
6
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
7
8
|
import { gunzipSync } from "node:zlib";
|
|
8
9
|
import { ConfigError } from "./config.js";
|
|
9
10
|
import { decodeUtf8, getBounded, urlIdentity } from "./net.js";
|
|
11
|
+
import { fileIdentity, onPath } from "./paths.js";
|
|
10
12
|
/** Where releases come from. Overridable for tests, exactly as `install.sh` overrides it. */
|
|
11
13
|
export const REGISTRY = "https://registry.npmjs.org";
|
|
12
14
|
/**
|
|
@@ -23,12 +25,55 @@ const METADATA_BOUNDS = { maxBytes: 1024 * 1024, timeoutMs: RELEASE_TIMEOUT_MS }
|
|
|
23
25
|
export function installedVersion(paths) {
|
|
24
26
|
try {
|
|
25
27
|
const raw = JSON.parse(readFileSync(join(paths.lib, "package.json"), "utf8"));
|
|
26
|
-
return typeof raw.version === "string"
|
|
28
|
+
return raw.name === "tonedial" && typeof raw.version === "string" && /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?$/.test(raw.version)
|
|
29
|
+
? raw.version : null;
|
|
27
30
|
}
|
|
28
31
|
catch {
|
|
29
32
|
return null;
|
|
30
33
|
}
|
|
31
34
|
}
|
|
35
|
+
/** Bind npm's global target to this running copy before allowing package-manager writes. */
|
|
36
|
+
export function inspectNpmInstall(env, t) {
|
|
37
|
+
const executable = onPath("npm", env);
|
|
38
|
+
if (executable === null)
|
|
39
|
+
throw new ConfigError(t.update.npmMissing);
|
|
40
|
+
const entry = resolve(executable);
|
|
41
|
+
const query = (args) => {
|
|
42
|
+
const result = spawnSync(entry, args, { env, encoding: "utf8", timeout: RELEASE_TIMEOUT_MS, maxBuffer: 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] });
|
|
43
|
+
const value = result.stdout?.trim();
|
|
44
|
+
if (result.status !== 0 || !value || !isAbsolute(value) || /[\r\n\0]/.test(value))
|
|
45
|
+
throw new ConfigError(t.update.npmScopeUnknown);
|
|
46
|
+
return resolve(value);
|
|
47
|
+
};
|
|
48
|
+
const prefix = query(["prefix", "-g"]);
|
|
49
|
+
const root = join(query(["root", "-g", "--prefix", prefix]), "tonedial");
|
|
50
|
+
try {
|
|
51
|
+
const own = fileURLToPath(new URL("../../", import.meta.url));
|
|
52
|
+
const current = fileIdentity(own);
|
|
53
|
+
const target = fileIdentity(root);
|
|
54
|
+
const version = installedVersion({ lib: root });
|
|
55
|
+
if (lstatSync(root, { throwIfNoEntry: false })?.isSymbolicLink() || current === null || target === null || current.dev !== target.dev || current.ino !== target.ino || version === null) {
|
|
56
|
+
throw new ConfigError(t.update.npmScopeMismatch);
|
|
57
|
+
}
|
|
58
|
+
return { entry, prefix, root, version };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
throw new ConfigError(t.update.npmScopeMismatch);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** npm owns replacement; captured output cannot corrupt JSON or disclose npm configuration. */
|
|
65
|
+
export function updateNpmInstall(install, env, t) {
|
|
66
|
+
const current = inspectNpmInstall(env, t);
|
|
67
|
+
if (current.entry !== install.entry || current.prefix !== install.prefix || current.root !== install.root || current.version !== install.version)
|
|
68
|
+
throw new ConfigError(t.update.npmScopeMismatch);
|
|
69
|
+
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)
|
|
71
|
+
throw new ConfigError(t.update.npmFailed);
|
|
72
|
+
const version = installedVersion({ lib: install.root });
|
|
73
|
+
if (version === null)
|
|
74
|
+
throw new ConfigError(t.update.versionUnknown);
|
|
75
|
+
return version;
|
|
76
|
+
}
|
|
32
77
|
/** Whether the one external tool this needs is here. Checked before anything is downloaded. */
|
|
33
78
|
export function haveTar() {
|
|
34
79
|
return spawnSync("tar", ["--version"], { stdio: "ignore" }).status === 0;
|
package/dist/core/skill.js
CHANGED
|
@@ -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
|
+
\`chars\` reports the current TONE.md Unicode character count; no size target or maximum applies.
|
|
8
9
|
Schema 3 removes guard and ceiling fields and reports advisory \`riskAxes\`; persisted config
|
|
9
10
|
remains schema 2. Custom profiles use preset schema 2; legacy schema 1 and journal formats
|
|
10
11
|
remain supported. Never send \`guards\` or
|
|
@@ -85,7 +86,7 @@ Persona, address, safeword, and \`speech_level\` are separate from the axes.
|
|
|
85
86
|
\`playfulness\` increases the frequency and exaggeration of the chosen role's expressions:
|
|
86
87
|
literal wording at 1, one restrained touch at 2, two recognizable touches at 3,
|
|
87
88
|
frequent theatrical idioms at 4, and a character voice throughout at 5.
|
|
88
|
-
Role slang and figurative language are not filtered
|
|
89
|
+
Role slang and figurative language are not filtered.
|
|
89
90
|
\`profanity\` still grades actual swearing; \`bravado\` grades claims about effort.
|
|
90
91
|
Change one only when the user explicitly asks for that setting; preserve every other setting.
|
|
91
92
|
Use the user's chosen text and values, not the illustrative values below.
|
|
@@ -98,6 +99,7 @@ interpolating it into shell code.
|
|
|
98
99
|
| What to call the user only | \`tonedial set "persona.user_ref=friend" --json\` |
|
|
99
100
|
| What to call yourself only | \`tonedial set "persona.self_ref=guide" --json\` |
|
|
100
101
|
| Both address terms, self first and user second | \`tonedial address "assistant" "reader" --json\` |
|
|
102
|
+
| Both role descriptions, agent first and user second | \`tonedial persona "Fictional database reviewer" "Developer" --json\` |
|
|
101
103
|
| Persona age band | \`tonedial set persona.age=30s --json\` |
|
|
102
104
|
| Persona gender | \`tonedial set persona.gender=neutral --json\` |
|
|
103
105
|
| User background and preferences | \`tonedial set "persona.user=Developer who likes concise technical answers" --json\` |
|
|
@@ -113,11 +115,13 @@ An empty value clears only the named persona field or safeword, for example
|
|
|
113
115
|
\`persona.user\` describes the user; \`persona.agent\` describes the agent's fictional role.
|
|
114
116
|
Each accepts at most 200 Unicode code points; never silently truncate a longer request.
|
|
115
117
|
Preserve the other fields and ask the user to shorten text rejected by the CLI.
|
|
118
|
+
In a terminal, bare \`tonedial persona\` asks for both descriptions; Enter keeps a value and \`""\` clears it.
|
|
119
|
+
\`show\` displays both descriptions and their character counts; a slug selects the saved profile.
|
|
116
120
|
Agent experience and specialty are roleplay, not verified credentials or tool capabilities.
|
|
117
121
|
Both descriptions lose to axes and task/permission rules, and may influence content.
|
|
118
122
|
A persona note expresses manner or role, loses to axes, and may also influence
|
|
119
123
|
content; it is not a way to override task or permission rules.
|
|
120
|
-
|
|
124
|
+
Persona notes have no length limit.
|
|
121
125
|
|
|
122
126
|
ToneDial has no guard toggle or automatic emergency register. All axis levels 1–5 are choices
|
|
123
127
|
the user can request. Report advisory \`riskAxes\` and combination notices without lowering
|
|
@@ -242,7 +246,7 @@ ${SETUP_MD}
|
|
|
242
246
|
|
|
243
247
|
## Apply it in this session
|
|
244
248
|
|
|
245
|
-
Successful \`set\`, \`address\`, \`use\`, \`reset\`, \`save\`, and approved \`create\` commands recompile \`TONE.md\`; that is a saved state
|
|
249
|
+
Successful \`set\`, \`address\`, \`persona\`, \`use\`, \`reset\`, \`save\`, and approved \`create\` commands recompile \`TONE.md\`; that is a saved state
|
|
246
250
|
change, not evidence of a session reload.
|
|
247
251
|
|
|
248
252
|
${RELOAD_MD}
|
|
@@ -365,7 +369,6 @@ depends on the host and model.
|
|
|
365
369
|
- Functional status emoji can accompany low \`warmth\`; they are structure, not affection.
|
|
366
370
|
- Required task formats take precedence: a code or JSON result must not become decorative prose.
|
|
367
371
|
- \`refusal\` ≥ 4 with \`compliance\` ≥ 4 contradicts itself. The CLI warns and does not block.
|
|
368
|
-
- \`formality\` ≥ 4 with \`profanity\` ≥ 4 holds but reads oddly.
|
|
369
372
|
- \`warmth\` ≥ 4 with \`profanity\` ≥ 4, and \`compliance\` ≤ 2 with \`conviction\` ≤ 2, each get a
|
|
370
373
|
composite sentence in the artifact rather than being left to collide.
|
|
371
374
|
- Care 5 adds a caring organizer signature; high playfulness with high bravado adds a cocky expert signature. These are static instructions selected by the combination layer.
|
|
@@ -389,7 +392,7 @@ them, and \`export\`/\`import\` transfer them. Older axis-only presets keep the
|
|
|
389
392
|
The seven bundled profiles supply localized role and address defaults; custom text remains literal.
|
|
390
393
|
Playfulness grades role idiom frequency from literal wording at 1 to theatrical voice throughout at 5.
|
|
391
394
|
Role slang and metaphors are not filtered or suppressed by low profanity or bravado.
|
|
392
|
-
Profanity grades actual swearing; bravado grades effort claims.
|
|
395
|
+
Profanity grades actual swearing; bravado grades effort claims.
|
|
393
396
|
`;
|
|
394
397
|
/**
|
|
395
398
|
* The Claude Code `/tone` command, installed only by `init --commands`.
|
|
@@ -399,7 +402,7 @@ Profanity grades actual swearing; bravado grades effort claims. The CLI carries
|
|
|
399
402
|
* command name stays `/tone` even though the tool is `tonedial`: it is what a person types
|
|
400
403
|
* mid-sentence, and the two extra characters are paid every time.
|
|
401
404
|
*/
|
|
402
|
-
export const TONE_COMMAND_VERSION =
|
|
405
|
+
export const TONE_COMMAND_VERSION = 9;
|
|
403
406
|
export const TONE_COMMAND_MD = `---
|
|
404
407
|
description: Check, change, or reload this agent's tone, preset, or ToneDial settings
|
|
405
408
|
---
|
package/dist/ui/format.js
CHANGED
|
@@ -11,7 +11,7 @@ export function nextStep(command, ui) {
|
|
|
11
11
|
/** One advisory notice from the shared axis metadata, without changing selected levels. */
|
|
12
12
|
export function riskWarnings(axes, t) {
|
|
13
13
|
const risks = riskyAxes(axes);
|
|
14
|
-
return
|
|
14
|
+
return risks.length ? [t.notice.riskAxes(risks)] : [];
|
|
15
15
|
}
|
|
16
16
|
/** Two retained phases restore their shared config in reverse order, with copyable IDs. */
|
|
17
17
|
export function recoveryRollbackLines(t, appId, nativeId) {
|
|
@@ -61,13 +61,10 @@ export function num(n) {
|
|
|
61
61
|
return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
62
62
|
}
|
|
63
63
|
/** The one shape the artifact's size is reported in, by every command that recompiles. */
|
|
64
|
-
export function
|
|
65
|
-
return t.artifact.
|
|
64
|
+
export function artifactSize(size, t) {
|
|
65
|
+
return t.artifact.size(num(size.chars));
|
|
66
66
|
}
|
|
67
|
-
/**
|
|
67
|
+
/** Contradictions stay advisory and never block the selected values. */
|
|
68
68
|
export function combination(w, t) {
|
|
69
|
-
|
|
70
|
-
return w.layer === "forbidden"
|
|
71
|
-
? `${t.combos.contradiction(w.rule)} ${sentence} ${t.combos.nothingBlocked}`
|
|
72
|
-
: `${t.combos.reads(w.rule)} ${sentence}`;
|
|
69
|
+
return `${t.combos.contradiction(w.rule)} ${t.combos[w.id]} ${t.combos.nothingBlocked}`;
|
|
73
70
|
}
|