aiden-runtime 4.8.1 → 4.9.1
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/README.md +88 -1
- package/dist/cli/v4/aidenCLI.js +37 -6
- package/dist/cli/v4/chatSession.js +53 -13
- package/dist/cli/v4/commands/daemon.js +53 -3
- package/dist/cli/v4/commands/daemonDoctor.js +212 -0
- package/dist/cli/v4/commands/daemonStatus.js +45 -26
- package/dist/cli/v4/commands/help.js +5 -0
- package/dist/cli/v4/commands/hooks.js +466 -0
- package/dist/cli/v4/commands/hooksSlash.js +33 -0
- package/dist/cli/v4/commands/index.js +13 -1
- package/dist/cli/v4/commands/mcp.js +89 -1
- package/dist/cli/v4/commands/mcpClientInstall.js +359 -0
- package/dist/cli/v4/commands/memory.js +707 -0
- package/dist/cli/v4/commands/memorySlash.js +38 -0
- package/dist/cli/v4/commands/recovery.js +1 -1
- package/dist/cli/v4/commands/skin.js +7 -0
- package/dist/cli/v4/commands/theme.js +217 -0
- package/dist/cli/v4/commands/trigger.js +1 -1
- package/dist/cli/v4/design/tokens.js +52 -4
- package/dist/cli/v4/display.js +39 -26
- package/dist/cli/v4/replyRenderer.js +6 -5
- package/dist/cli/v4/skinEngine.js +67 -0
- package/dist/cli/v4/ui/progressBar.js +179 -0
- package/dist/cli/v4/util/closestAction.js +48 -0
- package/dist/core/v4/aidenAgent.js +45 -2
- package/dist/core/v4/daemon/api/runs.js +131 -0
- package/dist/core/v4/daemon/bootstrap.js +368 -13
- package/dist/core/v4/daemon/db/migrations.js +169 -0
- package/dist/core/v4/daemon/idempotency/runIdempotencyStore.js +128 -0
- package/dist/core/v4/daemon/incarnationStore.js +47 -0
- package/dist/core/v4/daemon/runs/attemptStore.js +67 -0
- package/dist/core/v4/daemon/runs/reclaim.js +88 -0
- package/dist/core/v4/daemon/runs/retryPolicy.js +73 -0
- package/dist/core/v4/daemon/runs/runWithRetry.js +80 -0
- package/dist/core/v4/daemon/runs/stuckAttemptWatchdog.js +72 -0
- package/dist/core/v4/daemon/spans/spanHelpers.js +272 -0
- package/dist/core/v4/daemon/spans/spanStore.js +113 -0
- package/dist/core/v4/daemon/triggerBus.js +50 -19
- package/dist/core/v4/hooks/auditQuery.js +67 -0
- package/dist/core/v4/hooks/dispatcher.js +286 -0
- package/dist/core/v4/hooks/index.js +46 -0
- package/dist/core/v4/hooks/lifecycle.js +27 -0
- package/dist/core/v4/hooks/manifest.js +142 -0
- package/dist/core/v4/hooks/registry.js +149 -0
- package/dist/core/v4/hooks/runtime/subprocessRunner.js +188 -0
- package/dist/core/v4/hooks/toolHookGate.js +76 -0
- package/dist/core/v4/hooks/trust.js +14 -0
- package/dist/core/v4/identity/contextManager.js +83 -0
- package/dist/core/v4/identity/daemonId.js +85 -0
- package/dist/core/v4/identity/enforcement.js +103 -0
- package/dist/core/v4/identity/executionContext.js +153 -0
- package/dist/core/v4/identity/hookExecution.js +62 -0
- package/dist/core/v4/identity/httpContext.js +68 -0
- package/dist/core/v4/identity/ids.js +185 -0
- package/dist/core/v4/identity/index.js +60 -0
- package/dist/core/v4/identity/subprocessContext.js +98 -0
- package/dist/core/v4/identity/traceparent.js +114 -0
- package/dist/core/v4/logger/index.js +3 -1
- package/dist/core/v4/logger/logger.js +28 -1
- package/dist/core/v4/logger/redact.js +149 -0
- package/dist/core/v4/logger/sinks/fileSink.js +13 -0
- package/dist/core/v4/logger/sinks/stdSink.js +19 -1
- package/dist/core/v4/mcp/install/backup.js +78 -0
- package/dist/core/v4/mcp/install/clientPaths.js +90 -0
- package/dist/core/v4/mcp/install/clients.js +203 -0
- package/dist/core/v4/mcp/install/healthCheck.js +83 -0
- package/dist/core/v4/mcp/install/jsoncMerge.js +174 -0
- package/dist/core/v4/mcp/install/profiles.js +109 -0
- package/dist/core/v4/mcp/install/wslDetect.js +62 -0
- package/dist/core/v4/memory/namespaceRegistry.js +117 -0
- package/dist/core/v4/memory/projectRoot.js +76 -0
- package/dist/core/v4/memory/reviewer/index.js +162 -0
- package/dist/core/v4/memory/reviewer/pendingStore.js +136 -0
- package/dist/core/v4/memory/reviewer/prompt.js +105 -0
- package/dist/core/v4/memory/reviewer/skipRules.js +92 -0
- package/dist/core/v4/memoryManager.js +57 -10
- package/dist/core/v4/paths.js +2 -0
- package/dist/core/v4/subagent/spawnSubAgent.js +20 -7
- package/dist/core/v4/theme/bundledThemes.js +106 -0
- package/dist/core/v4/theme/themeLoader.js +160 -0
- package/dist/core/v4/theme/themeRegistry.js +97 -0
- package/dist/core/v4/theme/themeWatcher.js +95 -0
- package/dist/core/v4/toolRegistry.js +71 -8
- package/dist/core/v4/update/depWarningFilter.js +76 -0
- package/dist/core/v4/update/executeInstall.js +41 -35
- package/dist/core/v4/update/platformInstructions.js +128 -0
- package/dist/moat/approvalEngine.js +4 -0
- package/dist/moat/memoryGuard.js +8 -1
- package/dist/providers/v4/anthropicAdapter.js +10 -4
- package/dist/tools/v4/backends/local.js +19 -2
- package/dist/tools/v4/sessions/recallSession.js +6 -1
- package/package.json +3 -1
- package/plugins/aiden-plugin-chatgpt-plus/index.js +10 -1
- package/themes/default.yaml +52 -0
- package/themes/dracula.yaml +32 -0
- package/themes/light.yaml +32 -0
- package/themes/monochrome.yaml +31 -0
- package/themes/tokyo-night.yaml +32 -0
- package/dist/core/pluginSystem.js +0 -121
- package/dist/tools/v4/ui/_uiSmokeTool.js +0 -60
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.memory = exports.MEMORY_SHELL_ONLY = void 0;
|
|
4
|
+
exports.dispatchMemorySlash = dispatchMemorySlash;
|
|
5
|
+
const memory_1 = require("./memory");
|
|
6
|
+
/** Actions that need the full CLI surface (confirmation / destructive). */
|
|
7
|
+
exports.MEMORY_SHELL_ONLY = new Set(['remove', 'restore']);
|
|
8
|
+
/**
|
|
9
|
+
* Pure dispatch — exported for tests + reuse. Either prints a shell
|
|
10
|
+
* hint OR delegates to the provided `runMemory` runner. Side effects
|
|
11
|
+
* confined to the supplied `write` sink.
|
|
12
|
+
*/
|
|
13
|
+
async function dispatchMemorySlash(opts) {
|
|
14
|
+
const a = (opts.action || 'list').toLowerCase();
|
|
15
|
+
if (exports.MEMORY_SHELL_ONLY.has(a)) {
|
|
16
|
+
opts.write(`⚠ /memory ${a} not available inside chat (destructive operation)\n`);
|
|
17
|
+
opts.write(' Quit (/quit) and run from shell:\n\n');
|
|
18
|
+
const tail = opts.args.length > 0 ? ' ' + opts.args.join(' ') : '';
|
|
19
|
+
opts.write(` aiden memory ${a}${tail}\n`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
await opts.runMemory(a, opts.args, { writeOut: opts.write, writeErr: opts.write });
|
|
23
|
+
}
|
|
24
|
+
exports.memory = {
|
|
25
|
+
name: 'memory',
|
|
26
|
+
description: 'Manage memory (list / show / add / namespaces / pending / approve / review).',
|
|
27
|
+
category: 'system',
|
|
28
|
+
icon: '🧠',
|
|
29
|
+
handler: async (ctx) => {
|
|
30
|
+
await dispatchMemorySlash({
|
|
31
|
+
action: ctx.args[0] ?? 'list',
|
|
32
|
+
args: ctx.args.slice(1),
|
|
33
|
+
write: (s) => ctx.display.write(s),
|
|
34
|
+
runMemory: memory_1.runMemorySubcommand,
|
|
35
|
+
});
|
|
36
|
+
return {};
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -35,7 +35,7 @@ function pad(value, width) {
|
|
|
35
35
|
}
|
|
36
36
|
exports.recovery = {
|
|
37
37
|
name: 'recovery',
|
|
38
|
-
description: 'Inspect recurring failure patterns + recoveries
|
|
38
|
+
description: 'Inspect recurring failure patterns + recoveries.',
|
|
39
39
|
category: 'system',
|
|
40
40
|
icon: '🩹',
|
|
41
41
|
handler: async (ctx) => {
|
|
@@ -7,6 +7,13 @@ exports.skin = {
|
|
|
7
7
|
category: 'system',
|
|
8
8
|
icon: '🎨',
|
|
9
9
|
handler: async (ctx) => {
|
|
10
|
+
// v4.9.0 Slice 1a — /skin is now an alias for the new /theme system.
|
|
11
|
+
// The legacy color skins (~/.aiden/skins/*.yaml, RGB-tuple format)
|
|
12
|
+
// continue to work alongside the new theme system; /skin still
|
|
13
|
+
// manages the SkinEngine palette. Theme tokens (panel chrome,
|
|
14
|
+
// status footer glyphs, shimmer) are controlled by /theme.
|
|
15
|
+
ctx.display.warn('/skin is deprecated in v4.9 — use /theme for full visual customisation. ' +
|
|
16
|
+
'/skin continues to work for legacy colour skins.');
|
|
10
17
|
const engine = ctx.skin;
|
|
11
18
|
if (!engine) {
|
|
12
19
|
ctx.display.warn('Skin engine not wired in this context.');
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.theme = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
9
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
10
|
+
*
|
|
11
|
+
* Aiden — local-first agent.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* cli/v4/commands/theme.ts — v4.9.0 Slice 1a.
|
|
15
|
+
*
|
|
16
|
+
* `/theme` show current theme name + path + hint
|
|
17
|
+
* `/theme reload` force re-read of ~/.aiden/theme.yaml
|
|
18
|
+
* `/theme reset` delete ~/.aiden/theme.yaml, restore baseline
|
|
19
|
+
* `/theme edit` print ~/.aiden/theme.yaml path
|
|
20
|
+
*
|
|
21
|
+
* `/theme list` and `/theme set <name>` ship in Slice 1b alongside
|
|
22
|
+
* the remaining 4 bundled themes.
|
|
23
|
+
*/
|
|
24
|
+
const node_fs_1 = require("node:fs");
|
|
25
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
26
|
+
const themeLoader_1 = require("../../../core/v4/theme/themeLoader");
|
|
27
|
+
const themeRegistry_1 = require("../../../core/v4/theme/themeRegistry");
|
|
28
|
+
const bundledThemes_1 = require("../../../core/v4/theme/bundledThemes");
|
|
29
|
+
function themeFilePath(ctx) {
|
|
30
|
+
const root = ctx.paths?.root;
|
|
31
|
+
if (!root)
|
|
32
|
+
return null;
|
|
33
|
+
return node_path_1.default.join(root, 'theme.yaml');
|
|
34
|
+
}
|
|
35
|
+
function userThemesDir(ctx) {
|
|
36
|
+
const root = ctx.paths?.root;
|
|
37
|
+
if (!root)
|
|
38
|
+
return null;
|
|
39
|
+
return node_path_1.default.join(root, 'themes');
|
|
40
|
+
}
|
|
41
|
+
function listUserThemes(dir) {
|
|
42
|
+
if (!dir || !(0, node_fs_1.existsSync)(dir))
|
|
43
|
+
return [];
|
|
44
|
+
try {
|
|
45
|
+
return (0, node_fs_1.readdirSync)(dir)
|
|
46
|
+
.filter((f) => f.endsWith('.yaml'))
|
|
47
|
+
.map((f) => {
|
|
48
|
+
const name = f.replace(/\.yaml$/, '');
|
|
49
|
+
let description = '';
|
|
50
|
+
try {
|
|
51
|
+
const yaml = (0, node_fs_1.readFileSync)(node_path_1.default.join(dir, f), 'utf8');
|
|
52
|
+
const { parsed } = (0, themeLoader_1.parseThemeYaml)(yaml);
|
|
53
|
+
description = parsed?.description ?? '';
|
|
54
|
+
}
|
|
55
|
+
catch { /* tolerate unreadable */ }
|
|
56
|
+
return { name, description };
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.theme = {
|
|
64
|
+
name: 'theme',
|
|
65
|
+
description: 'Show, reload, reset, or open the user theme.yaml.',
|
|
66
|
+
category: 'system',
|
|
67
|
+
icon: '🎨',
|
|
68
|
+
handler: async (ctx) => {
|
|
69
|
+
const sub = ctx.rawArgs.trim();
|
|
70
|
+
const themePath = themeFilePath(ctx);
|
|
71
|
+
if (sub === '' || sub === 'show') {
|
|
72
|
+
const current = (0, themeRegistry_1.getCurrentName)();
|
|
73
|
+
const active = (0, themeRegistry_1.getActivePath)();
|
|
74
|
+
ctx.display.info(`Active theme: ${current}`);
|
|
75
|
+
if (active) {
|
|
76
|
+
ctx.display.info(`Source: ${active}`);
|
|
77
|
+
}
|
|
78
|
+
else if (themePath) {
|
|
79
|
+
ctx.display.info(`No user theme.yaml at ${themePath} — using bundled default.`);
|
|
80
|
+
}
|
|
81
|
+
ctx.display.info('Use /theme list, /theme set <name>, /theme reload, /theme reset, or /theme edit.');
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
if (sub === 'list') {
|
|
85
|
+
const current = (0, themeRegistry_1.getCurrentName)();
|
|
86
|
+
const bundled = (0, bundledThemes_1.listBundled)();
|
|
87
|
+
const userDir = userThemesDir(ctx);
|
|
88
|
+
const userList = listUserThemes(userDir);
|
|
89
|
+
ctx.display.info(`Active theme: ${current}`);
|
|
90
|
+
ctx.display.info('Available themes:');
|
|
91
|
+
const labelW = Math.max(...bundled.map((b) => b.name.length), ...userList.map((u) => u.name.length), 4);
|
|
92
|
+
for (const b of bundled) {
|
|
93
|
+
const marker = b.name === current ? '●' : '○';
|
|
94
|
+
const padded = b.name.padEnd(labelW);
|
|
95
|
+
ctx.display.info(` ${marker} ${padded} (bundled) ${b.description}`);
|
|
96
|
+
}
|
|
97
|
+
for (const u of userList) {
|
|
98
|
+
const marker = u.name === current ? '●' : '○';
|
|
99
|
+
const padded = u.name.padEnd(labelW);
|
|
100
|
+
ctx.display.info(` ${marker} ${padded} (user) ${u.description}`);
|
|
101
|
+
}
|
|
102
|
+
if (bundled.length === 0 && userList.length === 0) {
|
|
103
|
+
ctx.display.warn('No themes found. Reinstall aiden-runtime or use /theme edit to author one.');
|
|
104
|
+
}
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
if (sub.startsWith('set ') || sub === 'set') {
|
|
108
|
+
const name = sub.replace(/^set\s*/, '').trim();
|
|
109
|
+
if (!name) {
|
|
110
|
+
ctx.display.printError('Usage: /theme set <name>', `Try one of: ${bundledThemes_1.BUNDLED_NAMES.join(', ')} or /theme list for full options.`);
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
if (!themePath) {
|
|
114
|
+
ctx.display.warn('/theme set needs Aiden user-data paths — try in a real session.');
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
// Resolution order: bundled → user themes/ dir.
|
|
118
|
+
let yamlText = null;
|
|
119
|
+
let sourceLabel = '';
|
|
120
|
+
if ((0, bundledThemes_1.isBundled)(name)) {
|
|
121
|
+
yamlText = (0, bundledThemes_1.getYaml)(name);
|
|
122
|
+
sourceLabel = 'bundled';
|
|
123
|
+
}
|
|
124
|
+
if (!yamlText) {
|
|
125
|
+
const userDir = userThemesDir(ctx);
|
|
126
|
+
if (userDir) {
|
|
127
|
+
const userFile = node_path_1.default.join(userDir, `${name}.yaml`);
|
|
128
|
+
if ((0, node_fs_1.existsSync)(userFile)) {
|
|
129
|
+
try {
|
|
130
|
+
yamlText = (0, node_fs_1.readFileSync)(userFile, 'utf8');
|
|
131
|
+
sourceLabel = 'user';
|
|
132
|
+
}
|
|
133
|
+
catch { /* fall through to not-found */ }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!yamlText) {
|
|
138
|
+
ctx.display.printError(`Theme not found: "${name}"`, `Available bundled: ${bundledThemes_1.BUNDLED_NAMES.join(', ')}. Or create ~/.aiden/themes/${name}.yaml.`);
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
// Copy to ~/.aiden/theme.yaml and apply immediately (don't wait
|
|
142
|
+
// for the chokidar watcher debounce — slash-command-driven
|
|
143
|
+
// changes should feel instant).
|
|
144
|
+
try {
|
|
145
|
+
(0, node_fs_1.mkdirSync)(node_path_1.default.dirname(themePath), { recursive: true });
|
|
146
|
+
(0, node_fs_1.writeFileSync)(themePath, yamlText, 'utf8');
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
ctx.display.printError(`Could not write ${themePath}: ${err.message}`, 'Check filesystem permissions and try again.');
|
|
150
|
+
return {};
|
|
151
|
+
}
|
|
152
|
+
const { parsed, warnings } = (0, themeLoader_1.parseThemeYaml)(yamlText);
|
|
153
|
+
for (const w of warnings)
|
|
154
|
+
ctx.display.warn(`theme: ${w}`);
|
|
155
|
+
if (parsed) {
|
|
156
|
+
(0, themeRegistry_1.applyTheme)(parsed, themePath);
|
|
157
|
+
ctx.display.success(`✓ Theme set to ${name} (${sourceLabel}). Run /theme reset to revert to default.`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
ctx.display.printError(`Theme "${name}" parsed empty; current theme retained.`, 'Check the warnings above for the specific YAML / hex error.');
|
|
161
|
+
}
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
if (sub === 'reload') {
|
|
165
|
+
if (!themePath) {
|
|
166
|
+
ctx.display.warn('/theme reload needs Aiden user-data paths — try in a real session.');
|
|
167
|
+
return {};
|
|
168
|
+
}
|
|
169
|
+
if (!(0, node_fs_1.existsSync)(themePath)) {
|
|
170
|
+
ctx.display.warn(`No theme file at ${themePath}. Use /theme edit to create one.`);
|
|
171
|
+
return {};
|
|
172
|
+
}
|
|
173
|
+
const { parsed, warnings } = (0, themeLoader_1.loadThemeFile)(themePath);
|
|
174
|
+
for (const w of warnings)
|
|
175
|
+
ctx.display.warn(`theme: ${w}`);
|
|
176
|
+
if (parsed) {
|
|
177
|
+
(0, themeRegistry_1.applyTheme)(parsed, themePath);
|
|
178
|
+
ctx.display.success(`Theme reloaded: ${parsed.name}`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
ctx.display.printError('Theme parse failed; current theme retained.', 'Check the warnings above for the specific YAML / hex error.');
|
|
182
|
+
}
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
if (sub === 'reset') {
|
|
186
|
+
if (!themePath) {
|
|
187
|
+
ctx.display.warn('/theme reset needs Aiden user-data paths — try in a real session.');
|
|
188
|
+
return {};
|
|
189
|
+
}
|
|
190
|
+
if ((0, node_fs_1.existsSync)(themePath)) {
|
|
191
|
+
try {
|
|
192
|
+
(0, node_fs_1.unlinkSync)(themePath);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
ctx.display.warn(`Could not delete ${themePath}: ${err.message}`);
|
|
196
|
+
return {};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
(0, themeRegistry_1.resetToDefault)();
|
|
200
|
+
ctx.display.success('Theme reset to bundled default.');
|
|
201
|
+
return {};
|
|
202
|
+
}
|
|
203
|
+
if (sub === 'edit') {
|
|
204
|
+
if (!themePath) {
|
|
205
|
+
ctx.display.warn('/theme edit needs Aiden user-data paths — try in a real session.');
|
|
206
|
+
return {};
|
|
207
|
+
}
|
|
208
|
+
ctx.display.info(`Theme file: ${themePath}`);
|
|
209
|
+
if (!(0, node_fs_1.existsSync)(themePath)) {
|
|
210
|
+
ctx.display.info('File does not exist yet. Create it with your editor; Aiden hot-reloads on save.');
|
|
211
|
+
}
|
|
212
|
+
return {};
|
|
213
|
+
}
|
|
214
|
+
ctx.display.printError(`Unknown /theme subcommand: ${sub}`, 'Available: /theme | /theme list | /theme set <name> | /theme reload | /theme reset | /theme edit');
|
|
215
|
+
return {};
|
|
216
|
+
},
|
|
217
|
+
};
|
|
@@ -369,7 +369,7 @@ async function runAddEmail(db, argv, out, err) {
|
|
|
369
369
|
out(`poll interval: ${spec.pollIntervalMs}ms\n`);
|
|
370
370
|
out(`allow-senders: ${spec.allowedSenders.join(', ')}\n`);
|
|
371
371
|
out(`⚠ Password stored in plaintext in daemon.db (chmod 600 on POSIX,\n`);
|
|
372
|
-
out(` user-private on Windows). Encryption-at-rest is deferred to
|
|
372
|
+
out(` user-private on Windows). Encryption-at-rest is deferred to a future release.\n`);
|
|
373
373
|
out(`Restart the daemon to activate the trigger.\n`);
|
|
374
374
|
// Note: runAddEmail returns a Promise<number>, so the outer switch must
|
|
375
375
|
// await it. (Already handled — runTriggerSubcommand is async.)
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* isVerbose() reads env at call time (Ollama-options precedent).
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.VERBOSE_MODE_ENV = exports.spacing = exports.glyphs = exports.colors = exports.TRAIL_PIPE = void 0;
|
|
19
|
+
exports.VERBOSE_MODE_ENV = exports.spacing = exports.BASELINE_GLYPHS = exports.BASELINE_COLORS = exports.glyphs = exports.colors = exports.TRAIL_PIPE = void 0;
|
|
20
|
+
exports._restoreBaselineForTokens = _restoreBaselineForTokens;
|
|
20
21
|
exports.isVerbose = isVerbose;
|
|
21
22
|
// Re-export so consumers can import the trail gutter from tokens
|
|
22
23
|
// alongside the rest of the design system. The original constant
|
|
@@ -24,6 +25,20 @@ exports.isVerbose = isVerbose;
|
|
|
24
25
|
// that imports from `display/toolTrail` directly.
|
|
25
26
|
var toolTrail_1 = require("../display/toolTrail");
|
|
26
27
|
Object.defineProperty(exports, "TRAIL_PIPE", { enumerable: true, get: function () { return toolTrail_1.TRAIL_PIPE; } });
|
|
28
|
+
/**
|
|
29
|
+
* v4.9.0 Slice 1a — `colors` and `glyphs` were previously exported with
|
|
30
|
+
* `as const` (frozen literal-typed object). They are now mutable
|
|
31
|
+
* singletons so the ThemeRegistry can swap values at runtime when a
|
|
32
|
+
* user theme is loaded from `~/.aiden/theme.yaml`. Every existing
|
|
33
|
+
* consumer (`import { colors, glyphs } from '../design/tokens'`)
|
|
34
|
+
* continues to work because property reads now reflect the LIVE
|
|
35
|
+
* current values.
|
|
36
|
+
*
|
|
37
|
+
* `BASELINE_COLORS` / `BASELINE_GLYPHS` are deep-frozen snapshots
|
|
38
|
+
* captured at module load. The registry's `resetToDefault()` restores
|
|
39
|
+
* from these. Consumers that need the unmodified default (e.g. tests)
|
|
40
|
+
* can import these directly.
|
|
41
|
+
*/
|
|
27
42
|
// ── Colors ────────────────────────────────────────────────────────────────
|
|
28
43
|
/**
|
|
29
44
|
* Hex color tokens. Mirrors skinEngine RGB tuples for existing kinds;
|
|
@@ -119,9 +134,9 @@ exports.glyphs = {
|
|
|
119
134
|
dotOpen: '○',
|
|
120
135
|
/** Status footer column separator. */
|
|
121
136
|
sep: '│',
|
|
122
|
-
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
137
|
+
/** Turn counter retired per v4.9.0 pre-ship UX feedback —
|
|
138
|
+
* value-to-pixel ratio too low. Field kept (empty) so the
|
|
139
|
+
* token-table shape stays stable for downstream consumers. */
|
|
125
140
|
turn: '',
|
|
126
141
|
/** Slice 7 — session timer prefix. Slice 9 hotfix: `⌛` (U+231B
|
|
127
142
|
* HOURGLASS WITH FLOWING SAND) restored. The font set Shiva
|
|
@@ -224,6 +239,39 @@ exports.glyphs = {
|
|
|
224
239
|
track: '─',
|
|
225
240
|
},
|
|
226
241
|
};
|
|
242
|
+
/**
|
|
243
|
+
* Deep-frozen snapshots of the baseline values, captured at module
|
|
244
|
+
* load. The ThemeRegistry uses these to reset to defaults via
|
|
245
|
+
* `resetToDefault()`, and tests can import them as the source of
|
|
246
|
+
* truth even after a user theme has been applied to the mutable
|
|
247
|
+
* `colors` / `glyphs` exports above.
|
|
248
|
+
*/
|
|
249
|
+
function deepFreeze(o) {
|
|
250
|
+
if (o === null || typeof o !== 'object')
|
|
251
|
+
return o;
|
|
252
|
+
for (const k of Object.keys(o)) {
|
|
253
|
+
deepFreeze(o[k]);
|
|
254
|
+
}
|
|
255
|
+
return Object.freeze(o);
|
|
256
|
+
}
|
|
257
|
+
function deepClone(o) {
|
|
258
|
+
if (o === null || typeof o !== 'object')
|
|
259
|
+
return o;
|
|
260
|
+
if (Array.isArray(o))
|
|
261
|
+
return o.map(deepClone);
|
|
262
|
+
const out = {};
|
|
263
|
+
for (const k of Object.keys(o)) {
|
|
264
|
+
out[k] = deepClone(o[k]);
|
|
265
|
+
}
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
exports.BASELINE_COLORS = deepFreeze(deepClone(exports.colors));
|
|
269
|
+
exports.BASELINE_GLYPHS = deepFreeze(deepClone(exports.glyphs));
|
|
270
|
+
/** Used by ThemeRegistry to restore baseline values into the live singletons. */
|
|
271
|
+
function _restoreBaselineForTokens() {
|
|
272
|
+
Object.assign(exports.colors, deepClone(exports.BASELINE_COLORS));
|
|
273
|
+
Object.assign(exports.glyphs, deepClone(exports.BASELINE_GLYPHS));
|
|
274
|
+
}
|
|
227
275
|
// ── Spacing ───────────────────────────────────────────────────────────────
|
|
228
276
|
/**
|
|
229
277
|
* Integer spacing tokens. 0-indexed column counts; subagent depth
|
package/dist/cli/v4/display.js
CHANGED
|
@@ -23,6 +23,8 @@ exports.iconForTool = iconForTool;
|
|
|
23
23
|
exports.detectConfiguredChannels = detectConfiguredChannels;
|
|
24
24
|
exports.summarizeConfiguredChannels = summarizeConfiguredChannels;
|
|
25
25
|
exports.summarizeChannelState = summarizeChannelState;
|
|
26
|
+
exports.computeContextBarFill = computeContextBarFill;
|
|
27
|
+
exports.renderContextBar = renderContextBar;
|
|
26
28
|
exports.voiceIndicator = voiceIndicator;
|
|
27
29
|
exports.makeNoOpToolRowHandle = makeNoOpToolRowHandle;
|
|
28
30
|
exports.previewToolArgs = previewToolArgs;
|
|
@@ -214,6 +216,22 @@ const AIDEN_BANNER = String.raw `
|
|
|
214
216
|
██║ ██║██║██████╔╝███████╗██║ ╚████║
|
|
215
217
|
╚═╝ ╚═╝╚═╝╚═════╝ ╚══════╝╚═╝ ╚═══╝
|
|
216
218
|
`;
|
|
219
|
+
/**
|
|
220
|
+
* v4.9.0 pre-ship UI hotfix — pure context-bar helpers. Extracted
|
|
221
|
+
* for testability + to fix the "always empty" symptom. Scale:
|
|
222
|
+
* 0% → 0 cells, 1-19% → 1, 20-39% → 2, 40-59% → 3, 60-79% → 4,
|
|
223
|
+
* 80-100% → 5. `renderContextBar` returns the glyph array.
|
|
224
|
+
*/
|
|
225
|
+
function computeContextBarFill(pct, barW = 5) {
|
|
226
|
+
if (pct <= 0)
|
|
227
|
+
return 0;
|
|
228
|
+
if (pct >= 100)
|
|
229
|
+
return barW;
|
|
230
|
+
return Math.min(barW, Math.floor(pct / 20) + 1);
|
|
231
|
+
}
|
|
232
|
+
function renderContextBar(filled, barW = 5) {
|
|
233
|
+
return Array.from({ length: barW }, (_, i) => i < filled ? tokens_1.glyphs.bar.filled : tokens_1.glyphs.bar.empty);
|
|
234
|
+
}
|
|
217
235
|
class Display {
|
|
218
236
|
constructor(opts = {}) {
|
|
219
237
|
// ── Phase 16c: streaming surface ─────────────────────────────────────
|
|
@@ -605,23 +623,23 @@ class Display {
|
|
|
605
623
|
statusFooter(args) {
|
|
606
624
|
const sk = this.skin;
|
|
607
625
|
const SEP = sk.applyColors(' │ ', 'muted');
|
|
608
|
-
|
|
609
|
-
//
|
|
610
|
-
//
|
|
611
|
-
|
|
612
|
-
const provModel = `${tri} ${sk.applyColors(args.provider, 'muted')}` +
|
|
626
|
+
// v4.9.0 pre-ship UI hotfix — dropped the leading `▲` from
|
|
627
|
+
// provModel (prompt `▲` owns the marker; footer `▲` read as
|
|
628
|
+
// a duplicate orphan). Slice 7 per-metric palette preserved.
|
|
629
|
+
const provModel = `${sk.applyColors(args.provider, 'muted')}` +
|
|
613
630
|
`${sk.applyColors(' · ', 'muted')}` +
|
|
614
631
|
sk.applyColors(args.model, 'tool');
|
|
615
632
|
const pct = args.ctxMax > 0
|
|
616
633
|
? Math.min(100, Math.round((args.ctxUsed / args.ctxMax) * 100))
|
|
617
634
|
: 0;
|
|
618
|
-
//
|
|
619
|
-
//
|
|
620
|
-
//
|
|
635
|
+
// v4.9.0 pre-ship UI hotfix — bar math extracted to
|
|
636
|
+
// `computeContextBarFill` (1 cell per 20% bucket, ≥1 when any
|
|
637
|
+
// context used). Old `Math.round(pct/100 * barW)` floored to 0
|
|
638
|
+
// below ~10% so the bar stayed empty all session at typical use.
|
|
621
639
|
const barW = 5;
|
|
622
|
-
const filled =
|
|
640
|
+
const filled = computeContextBarFill(pct, barW);
|
|
623
641
|
const ctxKind = pct < 60 ? 'success' : pct < 85 ? 'warn' : 'error';
|
|
624
|
-
const cells =
|
|
642
|
+
const cells = renderContextBar(filled, barW);
|
|
625
643
|
const bar = sk.applyColors(cells.join(' '), ctxKind);
|
|
626
644
|
const ctxRatio = sk.applyColors(`${formatCompactTokens(args.ctxUsed)}/${formatCompactTokens(args.ctxMax)}`, 'warn');
|
|
627
645
|
const ctxPctText = sk.applyColors(`${pct}%`, ctxKind);
|
|
@@ -638,12 +656,10 @@ class Display {
|
|
|
638
656
|
const stateDot = args.state
|
|
639
657
|
? sk.applyColors(tokens_1.glyphs.status.dot, this.stateKind(args.state))
|
|
640
658
|
: '';
|
|
641
|
-
// v4.
|
|
642
|
-
//
|
|
643
|
-
//
|
|
644
|
-
|
|
645
|
-
? sk.applyColors(String(args.turnCount), 'metric_turn')
|
|
646
|
-
: '';
|
|
659
|
+
// v4.9.0 pre-ship UI: turn counter retired entirely — value-to-pixel
|
|
660
|
+
// ratio too low. `args.turnCount` stays in the signature for caller
|
|
661
|
+
// back-compat; ignored here.
|
|
662
|
+
void args.turnCount;
|
|
647
663
|
// v4.8.0 Slice 9 hotfix — ⌛ restored ahead of the bare elapsed
|
|
648
664
|
// string. Wider font support than the retired ⏱. `sessionMs` arg
|
|
649
665
|
// stays plumbed-but-unused for backward compat with the field name.
|
|
@@ -654,17 +670,14 @@ class Display {
|
|
|
654
670
|
const ctxSegFull = `${ctxRatio} ${bar} ${ctxPctText}`;
|
|
655
671
|
const ctxSegCompact = `${bar} ${ctxPctText}`;
|
|
656
672
|
let segments;
|
|
657
|
-
if (cols >= 120 && stateDot &&
|
|
658
|
-
segments = [provModel, ctxSegFull,
|
|
673
|
+
if (cols >= 120 && stateDot && sessionSeg) {
|
|
674
|
+
segments = [provModel, ctxSegFull, sessionSeg, stateDot];
|
|
659
675
|
}
|
|
660
|
-
else if (cols >= 100
|
|
661
|
-
// v4.8.1 Slice 2 hotfix —
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
-
// glyph). The glyph is single-cell, cheap, and load-bearing as
|
|
666
|
-
// the timer's identity affordance.
|
|
667
|
-
segments = [provModel, ctxSegFull, turnSeg, sessionSeg || elapsed];
|
|
676
|
+
else if (cols >= 100) {
|
|
677
|
+
// v4.8.1 Slice 2 hotfix — sessionSeg keeps the ⌛ identity glyph
|
|
678
|
+
// (single-cell, cheap) even at this tier. v4.9.0 pre-ship UI:
|
|
679
|
+
// turn counter retired; mid tier collapses to 2 separators.
|
|
680
|
+
segments = [provModel, ctxSegFull, sessionSeg || elapsed];
|
|
668
681
|
}
|
|
669
682
|
else {
|
|
670
683
|
segments = [provModel, ctxSegCompact, sessionSeg || elapsed];
|
|
@@ -183,7 +183,7 @@ function renderBlockquote(quote) {
|
|
|
183
183
|
return quote
|
|
184
184
|
.split('\n')
|
|
185
185
|
.map((ln) => (ln.length === 0 ? rail.trimEnd() : `${rail}${ln}`))
|
|
186
|
-
.join('\n') + '\n';
|
|
186
|
+
.join('\n') + '\n\n'; // v4.9.0 pre-ship UI: blank line after blockquote
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
189
189
|
* v4.1.3-essentials reply-polish: 4-tier heading hierarchy using the
|
|
@@ -633,11 +633,12 @@ function getReplyRenderer() {
|
|
|
633
633
|
}
|
|
634
634
|
}
|
|
635
635
|
proto._listDepth -= 1;
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
//
|
|
636
|
+
// v4.9.0 pre-ship UI: top-level list closes with a BLANK LINE
|
|
637
|
+
// (`\n\n`) so a following paragraph / heading / table reads with
|
|
638
|
+
// breathing room. Nested lists stay tight (`\n`) so they nest
|
|
639
|
+
// cleanly under their parent item.
|
|
639
640
|
const out = lines.join('\n');
|
|
640
|
-
return proto._listDepth === 0 ? out + '\n' : out + '\n';
|
|
641
|
+
return proto._listDepth === 0 ? out + '\n\n' : out + '\n';
|
|
641
642
|
};
|
|
642
643
|
// ── v4.8.1 Slice 2 — markdown table override ──────────────────────────
|
|
643
644
|
//
|
|
@@ -32,10 +32,58 @@ const node_fs_1 = require("node:fs");
|
|
|
32
32
|
const node_path_1 = __importDefault(require("node:path"));
|
|
33
33
|
const node_os_1 = __importDefault(require("node:os"));
|
|
34
34
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
35
|
+
// v4.9.0 Slice 1a hotfix — read live theme overrides from tokens.ts.
|
|
36
|
+
const tokens_1 = require("./design/tokens");
|
|
37
|
+
const themeRegistry_1 = require("../../core/v4/theme/themeRegistry");
|
|
35
38
|
/** Wrap text with a 24-bit ANSI foreground colour. */
|
|
36
39
|
function ansiRgb(text, r, g, b) {
|
|
37
40
|
return `\x1b[38;2;${r};${g};${b}m${text}\x1b[39m`;
|
|
38
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* v4.9.0 Slice 1a hotfix — map each SkinEngine `ColorKind` to a dotted
|
|
44
|
+
* path inside the v4.8 `tokens.ts` colour tree so the legacy paint API
|
|
45
|
+
* (`applyColors(text, kind)`) can resolve user-theme overrides without
|
|
46
|
+
* touching the legacy skin YAML cache. Each kind picks the closest
|
|
47
|
+
* semantic equivalent from the new tree; kinds without a natural fit
|
|
48
|
+
* (e.g. `agent`, `user`) fall through to the skin's own RGB tuple.
|
|
49
|
+
*
|
|
50
|
+
* The lookup is only consulted when a user theme is active (i.e.
|
|
51
|
+
* `getActiveThemePath() !== null`). When no theme is loaded, the
|
|
52
|
+
* legacy skin path runs unchanged — preserves /skin custom-palette
|
|
53
|
+
* users from being silently overridden by tokens.ts baselines.
|
|
54
|
+
*/
|
|
55
|
+
const COLOR_KIND_TO_TOKEN_PATH = {
|
|
56
|
+
brand: 'brand.primary',
|
|
57
|
+
accent: 'brand.primary',
|
|
58
|
+
heading: 'brand.primary',
|
|
59
|
+
tool: 'metrics.model',
|
|
60
|
+
session: 'metrics.model',
|
|
61
|
+
error: 'semantic.error',
|
|
62
|
+
warn: 'semantic.warn',
|
|
63
|
+
success: 'semantic.success',
|
|
64
|
+
muted: 'content.secondary',
|
|
65
|
+
tertiary: 'content.tertiary',
|
|
66
|
+
metric_turn: 'metrics.turnCount',
|
|
67
|
+
degraded: 'semantic.warn',
|
|
68
|
+
};
|
|
69
|
+
function hexToRgb(hex) {
|
|
70
|
+
const m3 = /^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/.exec(hex);
|
|
71
|
+
if (m3)
|
|
72
|
+
return [parseInt(m3[1] + m3[1], 16), parseInt(m3[2] + m3[2], 16), parseInt(m3[3] + m3[3], 16)];
|
|
73
|
+
const m6 = /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(hex);
|
|
74
|
+
if (m6)
|
|
75
|
+
return [parseInt(m6[1], 16), parseInt(m6[2], 16), parseInt(m6[3], 16)];
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function readDottedPath(root, dotted) {
|
|
79
|
+
let node = root;
|
|
80
|
+
for (const seg of dotted.split('.')) {
|
|
81
|
+
if (node === null || typeof node !== 'object')
|
|
82
|
+
return undefined;
|
|
83
|
+
node = node[seg];
|
|
84
|
+
}
|
|
85
|
+
return node;
|
|
86
|
+
}
|
|
39
87
|
const BRAND_ORANGE = [0xff, 0x6b, 0x35];
|
|
40
88
|
const DEFAULT_SKIN = {
|
|
41
89
|
name: 'default',
|
|
@@ -250,6 +298,25 @@ class SkinEngine {
|
|
|
250
298
|
applyColors(text, kind) {
|
|
251
299
|
if (this.forceMono)
|
|
252
300
|
return text;
|
|
301
|
+
// v4.9.0 Slice 1a hotfix — when a user theme is active, resolve
|
|
302
|
+
// the colour from the live tokens.ts tree FIRST. This lets a
|
|
303
|
+
// ~/.aiden/theme.yaml override every paint surface that routes
|
|
304
|
+
// through SkinEngine (Aiden reply chrome, panel bars, status
|
|
305
|
+
// footer text, tool rows) without requiring users to also
|
|
306
|
+
// re-author a parallel ~/.aiden/skins/<name>.yaml. When no user
|
|
307
|
+
// theme is active, the legacy skin RGB path runs unchanged —
|
|
308
|
+
// preserves /skin custom-palette users from regression.
|
|
309
|
+
if ((0, themeRegistry_1.getActivePath)() !== null) {
|
|
310
|
+
const dotted = COLOR_KIND_TO_TOKEN_PATH[kind];
|
|
311
|
+
if (dotted) {
|
|
312
|
+
const hex = readDottedPath(tokens_1.colors, dotted);
|
|
313
|
+
if (typeof hex === 'string') {
|
|
314
|
+
const rgb = hexToRgb(hex);
|
|
315
|
+
if (rgb)
|
|
316
|
+
return ansiRgb(text, rgb[0], rgb[1], rgb[2]);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
253
320
|
const rgb = this.current.colors[kind];
|
|
254
321
|
if (!rgb)
|
|
255
322
|
return text;
|