aiden-runtime 4.6.0 → 4.6.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/dist/cli/v4/aidenCLI.js +22 -1
- package/dist/cli/v4/chatSession.js +13 -0
- package/dist/cli/v4/commands/help.js +2 -0
- package/dist/cli/v4/commands/index.js +6 -1
- package/dist/cli/v4/commands/walkthrough.js +140 -0
- package/dist/cli/v4/onboarding/disclaimer.js +162 -0
- package/dist/cli/v4/onboarding/loading.js +208 -0
- package/dist/cli/v4/onboarding/providerPicker.js +126 -0
- package/dist/cli/v4/onboarding/successScreen.js +68 -0
- package/dist/cli/v4/repl/firstRunHint.js +107 -0
- package/dist/cli/v4/setupWizard.js +201 -31
- package/dist/core/v4/providers/modelFetch.js +179 -0
- package/dist/core/v4/providers/probe.js +275 -0
- package/dist/core/v4/ui/banner.js +133 -0
- package/dist/core/v4/ui/theme.js +164 -0
- package/dist/core/version.js +1 -1
- package/dist/tools/v4/ui/_uiSmokeTool.js +60 -0
- package/package.json +1 -1
package/dist/cli/v4/aidenCLI.js
CHANGED
|
@@ -809,7 +809,28 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
809
809
|
process.stdout.write(`\n${(0, providerDetection_1.summarizeDetection)(detection)}\n`);
|
|
810
810
|
process.stdout.write('config.yaml is empty — let\'s pick a provider that matches.\n');
|
|
811
811
|
}
|
|
812
|
-
|
|
812
|
+
// ONB1-WIRE — disclaimer + loading screens land BEFORE the wizard.
|
|
813
|
+
// Only inside this TTY-guarded branch (the non-TTY branch at the
|
|
814
|
+
// outer else already bails into explore mode). The detection
|
|
815
|
+
// summary above gives the user context for what was found; the
|
|
816
|
+
// disclaimer then asks for explicit consent before we walk them
|
|
817
|
+
// through setup. Declining exits 0 — the user chose not to
|
|
818
|
+
// continue, that's not an error.
|
|
819
|
+
//
|
|
820
|
+
// Lazy-required so the test harness paths that stub the wizard
|
|
821
|
+
// don't pay the load cost.
|
|
822
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
823
|
+
const { showDisclaimer } = require('./onboarding/disclaimer');
|
|
824
|
+
const disc = await showDisclaimer();
|
|
825
|
+
if (!disc.ok) {
|
|
826
|
+
// User typed 'n' / 'no'. The disclaimer already printed the
|
|
827
|
+
// friendly goodbye line; just exit cleanly.
|
|
828
|
+
process.exit(0);
|
|
829
|
+
}
|
|
830
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
831
|
+
const { runLoadingSequence, defaultLoadingSteps } = require('./onboarding/loading');
|
|
832
|
+
await runLoadingSequence(defaultLoadingSteps(paths));
|
|
833
|
+
process.stdout.write('\n');
|
|
813
834
|
const result = await (0, setupWizard_1.runSetupWizard)({ paths });
|
|
814
835
|
// Phase 30.2.1: three exit states.
|
|
815
836
|
if (result.status === 'exited') {
|
|
@@ -1600,6 +1600,19 @@ class ChatSession {
|
|
|
1600
1600
|
await this.maybeShowBootUpdatePrompt();
|
|
1601
1601
|
}
|
|
1602
1602
|
catch { /* never let the update prompt crash boot */ }
|
|
1603
|
+
// ONB1 slice 9 — one-time first-run hint banner. Renders below
|
|
1604
|
+
// the boot card on the very first session after a successful
|
|
1605
|
+
// setup; dismissed when the user sends their first message or
|
|
1606
|
+
// runs /dismiss. Lazy-required so test-harness sessions that
|
|
1607
|
+
// omit `paths` don't pay the fs cost.
|
|
1608
|
+
try {
|
|
1609
|
+
if (this.opts.paths) {
|
|
1610
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1611
|
+
const { renderFirstRunHint } = require('./repl/firstRunHint');
|
|
1612
|
+
await renderFirstRunHint({ paths: this.opts.paths, out: process.stdout });
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
catch { /* never let a missing marker crash boot */ }
|
|
1603
1616
|
// Bottom prompt hint — final line of the boot card.
|
|
1604
1617
|
display.write('\n');
|
|
1605
1618
|
display.write(display.bottomPromptHint() + '\n');
|
|
@@ -71,6 +71,8 @@ exports.SUBSECTION_MAP = {
|
|
|
71
71
|
'spawn-pause': 'System',
|
|
72
72
|
// v4.6 Phase 3b — self-improvement loop operator surface.
|
|
73
73
|
recovery: 'System',
|
|
74
|
+
// v4.6 ONB1 slice 10 — new-user guided tour.
|
|
75
|
+
walkthrough: 'System',
|
|
74
76
|
// ── Authentication ──
|
|
75
77
|
auth: 'Authentication',
|
|
76
78
|
// ── Help ──
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* and registers each on the global CommandRegistry at boot.
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.allCommands = exports.recovery = exports.spawnPause = exports.plannerGuard = exports.suggestions = exports.daemonStatus = exports.browserDepth = exports.tce = exports.sandbox = exports.update = exports.reloadSoul = exports.history = exports.show = exports.status = exports.voice = exports.channel = exports.setup = exports.cron = exports.doctor = exports.license = exports.auth = exports.plugins = exports.streaming = exports.debugPrompt = exports.identity = exports.providers = exports.quit = exports.clear = exports.verbose = exports.reasoning = exports.reloadMcp = exports.skills = exports.skin = exports.yolo = exports.usage = exports.compress = exports.title = exports.save = exports.personality = exports.model = exports.tools = exports.help = void 0;
|
|
15
|
+
exports.allCommands = exports.walkthrough = exports.recovery = exports.spawnPause = exports.plannerGuard = exports.suggestions = exports.daemonStatus = exports.browserDepth = exports.tce = exports.sandbox = exports.update = exports.reloadSoul = exports.history = exports.show = exports.status = exports.voice = exports.channel = exports.setup = exports.cron = exports.doctor = exports.license = exports.auth = exports.plugins = exports.streaming = exports.debugPrompt = exports.identity = exports.providers = exports.quit = exports.clear = exports.verbose = exports.reasoning = exports.reloadMcp = exports.skills = exports.skin = exports.yolo = exports.usage = exports.compress = exports.title = exports.save = exports.personality = exports.model = exports.tools = exports.help = void 0;
|
|
16
16
|
const help_1 = require("./help");
|
|
17
17
|
Object.defineProperty(exports, "help", { enumerable: true, get: function () { return help_1.help; } });
|
|
18
18
|
const tools_1 = require("./tools");
|
|
@@ -100,6 +100,9 @@ Object.defineProperty(exports, "spawnPause", { enumerable: true, get: function (
|
|
|
100
100
|
// v4.6 Phase 3b — self-improvement loop operator surface.
|
|
101
101
|
const recovery_1 = require("./recovery");
|
|
102
102
|
Object.defineProperty(exports, "recovery", { enumerable: true, get: function () { return recovery_1.recovery; } });
|
|
103
|
+
// ONB1 slice 10 — new-user guided tour.
|
|
104
|
+
const walkthrough_1 = require("./walkthrough");
|
|
105
|
+
Object.defineProperty(exports, "walkthrough", { enumerable: true, get: function () { return walkthrough_1.walkthrough; } });
|
|
103
106
|
/** All built-in system commands, in canonical order. */
|
|
104
107
|
exports.allCommands = [
|
|
105
108
|
help_1.help,
|
|
@@ -149,6 +152,8 @@ exports.allCommands = [
|
|
|
149
152
|
spawnPause_1.spawnPause,
|
|
150
153
|
// v4.6 Phase 3b — self-improvement loop operator surface.
|
|
151
154
|
recovery_1.recovery,
|
|
155
|
+
// ONB1 slice 10 — new-user guided tour.
|
|
156
|
+
walkthrough_1.walkthrough,
|
|
152
157
|
clear_1.clear,
|
|
153
158
|
quit_1.quit,
|
|
154
159
|
];
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
4
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
5
|
+
*
|
|
6
|
+
* Aiden — local-first agent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* cli/v4/commands/walkthrough.ts — ONB1 slice 10.
|
|
10
|
+
*
|
|
11
|
+
* `/walkthrough` — 5-screen guided tour of what Aiden can do. Pointed
|
|
12
|
+
* at by the first-run hint banner (slice 9) but available any time.
|
|
13
|
+
*
|
|
14
|
+
* Each screen:
|
|
15
|
+
* 1. A short title + one paragraph describing a capability area.
|
|
16
|
+
* 2. A "Try this now" example prompt the user can run verbatim.
|
|
17
|
+
* 3. A muted line on what tools/skills will fire.
|
|
18
|
+
*
|
|
19
|
+
* Screens:
|
|
20
|
+
* 1. Files & shell
|
|
21
|
+
* 2. Browser & web
|
|
22
|
+
* 3. Memory & SOUL
|
|
23
|
+
* 4. Skills & MCP
|
|
24
|
+
* 5. Operator controls
|
|
25
|
+
*
|
|
26
|
+
* Navigation: the command renders all five screens in order with
|
|
27
|
+
* separator lines between, then drops back to the REPL. No interactive
|
|
28
|
+
* paging — the user can scroll the buffer if they want to revisit
|
|
29
|
+
* something. Re-running `/walkthrough` is cheap.
|
|
30
|
+
*/
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.walkthrough = void 0;
|
|
33
|
+
const theme_1 = require("../../../core/v4/ui/theme");
|
|
34
|
+
const SCREENS = [
|
|
35
|
+
{
|
|
36
|
+
title: 'Files & shell',
|
|
37
|
+
body: [
|
|
38
|
+
'Aiden can read, patch, and organise files on your machine, and run',
|
|
39
|
+
'shell commands when a task needs the OS. Patches go through a diff',
|
|
40
|
+
'preview before anything writes to disk.',
|
|
41
|
+
],
|
|
42
|
+
tryThis: 'summarize the files in this folder and flag anything stale',
|
|
43
|
+
fires: 'file_read · shell · diff_preview',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
title: 'Browser & web',
|
|
47
|
+
body: [
|
|
48
|
+
'Persistent Chromium session for navigation, screenshots, form fill,',
|
|
49
|
+
'and content extraction. The browser stays warm across turns so the',
|
|
50
|
+
'agent can chain research steps without re-logging in.',
|
|
51
|
+
],
|
|
52
|
+
tryThis: 'research the top three local-first AI tools and save a comparison to notes.md',
|
|
53
|
+
fires: 'open_browser · browser_extract · file_write',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
title: 'Memory & SOUL',
|
|
57
|
+
body: [
|
|
58
|
+
'Aiden remembers across sessions via SOUL.md (identity), MEMORY.md',
|
|
59
|
+
'(curated notes), and the sessions database. `/identity` shows what',
|
|
60
|
+
'persona is loaded; edit SOUL.md to shape it.',
|
|
61
|
+
],
|
|
62
|
+
tryThis: 'remember that I prefer concise replies and Python over TypeScript',
|
|
63
|
+
fires: 'memory_write · session_log',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
title: 'Skills & MCP',
|
|
67
|
+
body: [
|
|
68
|
+
'Skills are bundled workflows (graphify, voice, kanban, more). MCP',
|
|
69
|
+
'servers extend the toolset further — each MCP exposes its own tools',
|
|
70
|
+
'as if they were native. `/skills` lists what is loaded; `/tools` lists',
|
|
71
|
+
'every active tool.',
|
|
72
|
+
],
|
|
73
|
+
tryThis: '/skills',
|
|
74
|
+
fires: '(no agent call — local registry walk)',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
title: 'Operator controls',
|
|
78
|
+
body: [
|
|
79
|
+
'You stay in charge. `/spawn-pause on` halts sub-agent spawning;',
|
|
80
|
+
'`/recovery list` shows what Aiden has learned from past failures;',
|
|
81
|
+
'`/doctor` prints a health snapshot; `/quit` exits cleanly.',
|
|
82
|
+
],
|
|
83
|
+
tryThis: '/doctor',
|
|
84
|
+
fires: '(no agent call — local self-check)',
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
function wrap(text, width) {
|
|
88
|
+
const words = text.split(/\s+/);
|
|
89
|
+
const lines = [];
|
|
90
|
+
let cur = '';
|
|
91
|
+
for (const w of words) {
|
|
92
|
+
if (!cur) {
|
|
93
|
+
cur = w;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (cur.length + 1 + w.length > width) {
|
|
97
|
+
lines.push(cur);
|
|
98
|
+
cur = w;
|
|
99
|
+
}
|
|
100
|
+
else
|
|
101
|
+
cur += ' ' + w;
|
|
102
|
+
}
|
|
103
|
+
if (cur)
|
|
104
|
+
lines.push(cur);
|
|
105
|
+
return lines;
|
|
106
|
+
}
|
|
107
|
+
function renderScreen(ctx, screen, idx, total) {
|
|
108
|
+
const out = ctx.display;
|
|
109
|
+
const w = (0, theme_1.termWidth)();
|
|
110
|
+
const bodyW = Math.min(w - 6, 72);
|
|
111
|
+
out.write('\n ' + (0, theme_1.separator)(Math.min(w - 4, 64)) + '\n\n');
|
|
112
|
+
out.write(' ' + theme_1.c.muted(`(${idx + 1}/${total})`) + ' ' + (0, theme_1.bold)(theme_1.c.primary(screen.title)) + '\n\n');
|
|
113
|
+
const joined = screen.body.join(' ');
|
|
114
|
+
for (const line of wrap(joined, bodyW)) {
|
|
115
|
+
out.write(' ' + theme_1.c.text(line) + '\n');
|
|
116
|
+
}
|
|
117
|
+
out.write('\n');
|
|
118
|
+
out.write(' ' + theme_1.c.muted('Try this now:') + '\n');
|
|
119
|
+
out.write(' ' + theme_1.c.accent('▸ ') + theme_1.c.accent(screen.tryThis) + '\n');
|
|
120
|
+
out.write(' ' + (0, theme_1.italic)(theme_1.c.muted(`fires: ${screen.fires}`)) + '\n');
|
|
121
|
+
}
|
|
122
|
+
exports.walkthrough = {
|
|
123
|
+
name: 'walkthrough',
|
|
124
|
+
description: '5-screen guided tour of what Aiden can do.',
|
|
125
|
+
category: 'system',
|
|
126
|
+
icon: '🧭',
|
|
127
|
+
aliases: ['tour', 'tutorial'],
|
|
128
|
+
handler: async (ctx) => {
|
|
129
|
+
const w = (0, theme_1.termWidth)();
|
|
130
|
+
ctx.display.write('\n');
|
|
131
|
+
ctx.display.write(' ' + (0, theme_1.bold)(theme_1.c.primary('Aiden walkthrough')) + '\n');
|
|
132
|
+
ctx.display.write(' ' + theme_1.c.muted('Five short screens. Each ends with a prompt you can copy-paste into chat.') + '\n');
|
|
133
|
+
for (let i = 0; i < SCREENS.length; i++) {
|
|
134
|
+
renderScreen(ctx, SCREENS[i], i, SCREENS.length);
|
|
135
|
+
}
|
|
136
|
+
ctx.display.write('\n ' + (0, theme_1.separator)(Math.min(w - 4, 64)) + '\n');
|
|
137
|
+
ctx.display.write(' ' + theme_1.c.muted('That\'s the tour. Type something — Aiden is listening.') + '\n\n');
|
|
138
|
+
return {};
|
|
139
|
+
},
|
|
140
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
4
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
5
|
+
*
|
|
6
|
+
* Aiden — local-first agent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* cli/v4/onboarding/disclaimer.ts — ONB1 slice 3.
|
|
10
|
+
*
|
|
11
|
+
* First screen of the redesigned first-run experience. Renders the
|
|
12
|
+
* framed AIDEN banner + tagline + credits + a single-paragraph
|
|
13
|
+
* disclaimer paragraph + Y/n prompt. Default Y. Typing 'n' or 'N'
|
|
14
|
+
* exits with a friendly goodbye line; ENTER (or 'y'/'Y') advances.
|
|
15
|
+
*
|
|
16
|
+
* Caller is responsible for branching on the returned `ok` boolean.
|
|
17
|
+
* This function NEVER calls process.exit — keeps it testable, and
|
|
18
|
+
* the parent (aidenCLI) controls the exit code centrally.
|
|
19
|
+
*
|
|
20
|
+
* TTY guard: non-TTY callers (systemd / launchd / CI / pipe) return
|
|
21
|
+
* `{ ok: true, skipped: true }` immediately so the wider boot path
|
|
22
|
+
* falls through to explore-mode wiring as today.
|
|
23
|
+
*/
|
|
24
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
27
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
28
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
29
|
+
}
|
|
30
|
+
Object.defineProperty(o, k2, desc);
|
|
31
|
+
}) : (function(o, m, k, k2) {
|
|
32
|
+
if (k2 === undefined) k2 = k;
|
|
33
|
+
o[k2] = m[k];
|
|
34
|
+
}));
|
|
35
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
36
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
37
|
+
}) : function(o, v) {
|
|
38
|
+
o["default"] = v;
|
|
39
|
+
});
|
|
40
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
41
|
+
var ownKeys = function(o) {
|
|
42
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
43
|
+
var ar = [];
|
|
44
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
45
|
+
return ar;
|
|
46
|
+
};
|
|
47
|
+
return ownKeys(o);
|
|
48
|
+
};
|
|
49
|
+
return function (mod) {
|
|
50
|
+
if (mod && mod.__esModule) return mod;
|
|
51
|
+
var result = {};
|
|
52
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
53
|
+
__setModuleDefault(result, mod);
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
})();
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
exports.showDisclaimer = showDisclaimer;
|
|
59
|
+
const readline = __importStar(require("node:readline"));
|
|
60
|
+
const banner_1 = require("../../../core/v4/ui/banner");
|
|
61
|
+
const theme_1 = require("../../../core/v4/ui/theme");
|
|
62
|
+
const version_1 = require("../../../core/version");
|
|
63
|
+
const DISCLAIMER_PARA = 'Aiden is a semi-autonomous AI agent that can touch your files, ' +
|
|
64
|
+
'browser, and shell. Open source — read the code if you want. ' +
|
|
65
|
+
'Started as a hobby project, built solo, still rough in spots.';
|
|
66
|
+
/**
|
|
67
|
+
* Word-wrap `text` to `width` columns. Preserves single spaces; does
|
|
68
|
+
* not handle ANSI codes (callers pass plain text here).
|
|
69
|
+
*/
|
|
70
|
+
function wrap(text, width) {
|
|
71
|
+
const words = text.split(/\s+/);
|
|
72
|
+
const lines = [];
|
|
73
|
+
let current = '';
|
|
74
|
+
for (const w of words) {
|
|
75
|
+
if (!current) {
|
|
76
|
+
current = w;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (current.length + 1 + w.length > width) {
|
|
80
|
+
lines.push(current);
|
|
81
|
+
current = w;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
current += ' ' + w;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (current)
|
|
88
|
+
lines.push(current);
|
|
89
|
+
return lines;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Clear the screen if stdout is a TTY. No-op otherwise.
|
|
93
|
+
*/
|
|
94
|
+
function clearScreen(out) {
|
|
95
|
+
if (!out.isTTY)
|
|
96
|
+
return;
|
|
97
|
+
out.write('\x1b[2J\x1b[H');
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Render the disclaimer body — banner + separator + wrapped paragraph.
|
|
101
|
+
* Pure renderer; caller owns the write.
|
|
102
|
+
*/
|
|
103
|
+
function renderDisclaimerBody(version) {
|
|
104
|
+
const w = (0, theme_1.termWidth)();
|
|
105
|
+
const body = [];
|
|
106
|
+
body.push((0, banner_1.renderBanner)({ version }));
|
|
107
|
+
body.push(' ' + (0, theme_1.separator)(Math.min(w - 4, 64)) + '\n');
|
|
108
|
+
body.push('\n');
|
|
109
|
+
const indent = ' ';
|
|
110
|
+
for (const line of wrap(DISCLAIMER_PARA, Math.min(w - 4, 70))) {
|
|
111
|
+
body.push(indent + theme_1.c.text(line) + '\n');
|
|
112
|
+
}
|
|
113
|
+
body.push('\n');
|
|
114
|
+
return body.join('');
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Read a single line from stdin. Resolves with the trimmed input.
|
|
118
|
+
* Rejects on close-without-input (typical when stdin is closed under us).
|
|
119
|
+
*/
|
|
120
|
+
function promptYesNo(inStream, outStream, question) {
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
const rl = readline.createInterface({ input: inStream, output: outStream });
|
|
123
|
+
rl.question(question, (answer) => {
|
|
124
|
+
rl.close();
|
|
125
|
+
resolve((answer ?? '').trim());
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Show the disclaimer screen and collect Y/n. ENTER == accept; 'n'/'N'
|
|
131
|
+
* == decline. Anything else also counts as accept (matches Claude
|
|
132
|
+
* Code / Codex defaults — users hammer enter and expect to advance).
|
|
133
|
+
*/
|
|
134
|
+
async function showDisclaimer(opts = {}) {
|
|
135
|
+
const out = opts.out ?? process.stdout;
|
|
136
|
+
const inStream = opts.in ?? process.stdin;
|
|
137
|
+
const version = opts.version ?? version_1.VERSION;
|
|
138
|
+
// Non-TTY: skip the prompt entirely. Caller falls through to whatever
|
|
139
|
+
// non-interactive path is appropriate (explore mode in aidenCLI today).
|
|
140
|
+
if (!out.isTTY || !inStream.isTTY) {
|
|
141
|
+
return { ok: true, skipped: true };
|
|
142
|
+
}
|
|
143
|
+
clearScreen(out);
|
|
144
|
+
out.write(renderDisclaimerBody(version));
|
|
145
|
+
const question = ' ' + theme_1.c.accent('Continue?') + ' ' + theme_1.c.muted('[Y/n] ');
|
|
146
|
+
const answer = await promptYesNo(inStream, out, question);
|
|
147
|
+
if (answer === 'n' || answer === 'N' || answer.toLowerCase() === 'no') {
|
|
148
|
+
out.write('\n ' + theme_1.c.muted('No problem — run `aiden` anytime to come back.') + '\n\n');
|
|
149
|
+
return { ok: false, reason: 'user-declined' };
|
|
150
|
+
}
|
|
151
|
+
return { ok: true };
|
|
152
|
+
}
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Direct invocation for visual smoke test:
|
|
155
|
+
// npx ts-node cli/v4/onboarding/disclaimer.ts
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
if (require.main === module) {
|
|
158
|
+
showDisclaimer().then((r) => {
|
|
159
|
+
process.stdout.write(`\n[result] ${JSON.stringify(r)}\n`);
|
|
160
|
+
process.exit(r.ok ? 0 : 1);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
4
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
5
|
+
*
|
|
6
|
+
* Aiden — local-first agent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* cli/v4/onboarding/loading.ts — ONB1 slice 4.
|
|
10
|
+
*
|
|
11
|
+
* Animated multi-step loading screen rendered AFTER the disclaimer
|
|
12
|
+
* accept and BEFORE the provider picker. Each step does real work
|
|
13
|
+
* (no sleep-only fakery); the spinner shows for at least 300ms per
|
|
14
|
+
* step so the user actually perceives motion. Spinner replaces with
|
|
15
|
+
* a green ✓ on success or red ✗ on failure; right-aligned status
|
|
16
|
+
* text gives the answer (e.g. "Node v22 · Windows 11", "74 loaded").
|
|
17
|
+
*
|
|
18
|
+
* Setting up Aiden...
|
|
19
|
+
*
|
|
20
|
+
* ✓ Checking system Node v22 · Windows 11 [180ms]
|
|
21
|
+
* ✓ Loading skills 74 loaded [640ms]
|
|
22
|
+
* ✓ Initializing tools 60 tools [310ms]
|
|
23
|
+
* ✓ Configuring memory ~/.aiden/ created [420ms]
|
|
24
|
+
*
|
|
25
|
+
* ────────────────────────────────────────────────────────────
|
|
26
|
+
*
|
|
27
|
+
* Step contract: each step is a sync-or-async function returning
|
|
28
|
+
* `{ status: string }` (the right-aligned answer). Throwing converts
|
|
29
|
+
* to ✗ with the error message as status; the runner continues to
|
|
30
|
+
* subsequent steps so a single failure doesn't strand the user.
|
|
31
|
+
*/
|
|
32
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
|
+
};
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runLoadingSequence = runLoadingSequence;
|
|
37
|
+
exports.defaultLoadingSteps = defaultLoadingSteps;
|
|
38
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
39
|
+
const node_fs_1 = require("node:fs");
|
|
40
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
41
|
+
const theme_1 = require("../../../core/v4/ui/theme");
|
|
42
|
+
const paths_1 = require("../../../core/v4/paths");
|
|
43
|
+
const MIN_SPINNER_MS = 300;
|
|
44
|
+
const SPIN_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
45
|
+
const SPIN_INTERVAL = 80;
|
|
46
|
+
function rowGeom(width) {
|
|
47
|
+
const inner = Math.min(width - 4, 70);
|
|
48
|
+
const labelCol = Math.floor(inner * 0.45);
|
|
49
|
+
const statusCol = inner - labelCol;
|
|
50
|
+
return { labelCol, statusCol };
|
|
51
|
+
}
|
|
52
|
+
function rpad(s, n) {
|
|
53
|
+
return s.length >= n ? s : s + ' '.repeat(n - s.length);
|
|
54
|
+
}
|
|
55
|
+
function lpad(s, n) {
|
|
56
|
+
return s.length >= n ? s : ' '.repeat(n - s.length) + s;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Run all steps sequentially and render the live multi-row pane.
|
|
60
|
+
* Non-TTY: emits one line per step on completion with no spinner.
|
|
61
|
+
*/
|
|
62
|
+
async function runLoadingSequence(steps, opts = {}) {
|
|
63
|
+
const out = opts.out ?? process.stdout;
|
|
64
|
+
const heading = opts.heading ?? 'Setting up Aiden...';
|
|
65
|
+
const w = (0, theme_1.termWidth)();
|
|
66
|
+
const { labelCol, statusCol } = rowGeom(w);
|
|
67
|
+
const isTty = !!out.isTTY;
|
|
68
|
+
const results = [];
|
|
69
|
+
if (!isTty) {
|
|
70
|
+
out.write(`${heading}\n`);
|
|
71
|
+
for (const step of steps) {
|
|
72
|
+
const t0 = Date.now();
|
|
73
|
+
try {
|
|
74
|
+
const r = await step.run();
|
|
75
|
+
const ms = Date.now() - t0;
|
|
76
|
+
results.push({ label: step.label, ok: true, status: r.status, ms });
|
|
77
|
+
out.write(` ok ${step.label} ${r.status} [${ms}ms]\n`);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
const ms = Date.now() - t0;
|
|
81
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
82
|
+
results.push({ label: step.label, ok: false, status: msg, ms });
|
|
83
|
+
out.write(` err ${step.label} ${msg} [${ms}ms]\n`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { ok: results.every((r) => r.ok), steps: results };
|
|
87
|
+
}
|
|
88
|
+
out.write('\n ' + theme_1.c.text(heading) + '\n\n');
|
|
89
|
+
// Pre-paint placeholder rows so the spinner overwrites in place.
|
|
90
|
+
for (const step of steps) {
|
|
91
|
+
const line = ' ' + theme_1.c.muted('·') + ' ' + rpad(step.label, labelCol) +
|
|
92
|
+
' ' + theme_1.c.muted(lpad('—', statusCol));
|
|
93
|
+
out.write(line + '\n');
|
|
94
|
+
}
|
|
95
|
+
// Walk back up to the top of the block.
|
|
96
|
+
out.write(`\x1b[${steps.length}A`);
|
|
97
|
+
for (let i = 0; i < steps.length; i++) {
|
|
98
|
+
const step = steps[i];
|
|
99
|
+
const t0 = Date.now();
|
|
100
|
+
let frame = 0;
|
|
101
|
+
const spinner = setInterval(() => {
|
|
102
|
+
const glyph = theme_1.c.primary(SPIN_FRAMES[frame % SPIN_FRAMES.length]);
|
|
103
|
+
out.write('\x1b[2K\r ' + glyph + ' ' + theme_1.c.text(rpad(step.label, labelCol)));
|
|
104
|
+
frame += 1;
|
|
105
|
+
}, SPIN_INTERVAL);
|
|
106
|
+
let ok = true;
|
|
107
|
+
let status = '';
|
|
108
|
+
try {
|
|
109
|
+
const r = await step.run();
|
|
110
|
+
status = r.status;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
ok = false;
|
|
114
|
+
status = err instanceof Error ? err.message : String(err);
|
|
115
|
+
}
|
|
116
|
+
// Enforce a minimum perceptible spinner duration.
|
|
117
|
+
const elapsed = Date.now() - t0;
|
|
118
|
+
if (elapsed < MIN_SPINNER_MS) {
|
|
119
|
+
await new Promise((r) => setTimeout(r, MIN_SPINNER_MS - elapsed));
|
|
120
|
+
}
|
|
121
|
+
clearInterval(spinner);
|
|
122
|
+
const ms = Date.now() - t0;
|
|
123
|
+
const glyph = ok ? theme_1.c.success('✓') : theme_1.c.error('✗');
|
|
124
|
+
const statusText = ok ? theme_1.c.muted(status) : theme_1.c.error(status);
|
|
125
|
+
const timing = theme_1.c.muted(`[${ms}ms]`);
|
|
126
|
+
const row = ' ' + glyph + ' ' + theme_1.c.text(rpad(step.label, labelCol)) +
|
|
127
|
+
' ' + lpad(statusText, statusCol) + ' ' + timing;
|
|
128
|
+
out.write('\x1b[2K\r' + row + '\n');
|
|
129
|
+
results.push({ label: step.label, ok, status, ms });
|
|
130
|
+
}
|
|
131
|
+
out.write('\n ' + (0, theme_1.separator)(Math.min(w - 4, 64)) + '\n');
|
|
132
|
+
return { ok: results.every((r) => r.ok), steps: results };
|
|
133
|
+
}
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Default step set — the real-work pipeline used by the wizard entry point.
|
|
136
|
+
// Exported so the wizard composer can extend / reorder; the smoke test
|
|
137
|
+
// also reuses these to verify the real numbers shown match disk state.
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
function defaultLoadingSteps(paths) {
|
|
140
|
+
const resolved = paths ?? (0, paths_1.resolveAidenPaths)();
|
|
141
|
+
return [
|
|
142
|
+
{
|
|
143
|
+
label: 'Checking system',
|
|
144
|
+
run: () => {
|
|
145
|
+
const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
|
|
146
|
+
if (!Number.isFinite(nodeMajor) || nodeMajor < 18) {
|
|
147
|
+
throw new Error(`Node ${process.versions.node} (need 18+)`);
|
|
148
|
+
}
|
|
149
|
+
const plat = process.platform === 'win32'
|
|
150
|
+
? `Windows ${node_os_1.default.release().split('.')[0]}`
|
|
151
|
+
: process.platform === 'darwin'
|
|
152
|
+
? `macOS ${node_os_1.default.release()}`
|
|
153
|
+
: `${process.platform} ${node_os_1.default.release()}`;
|
|
154
|
+
return { status: `Node v${nodeMajor} · ${plat}` };
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
label: 'Loading skills',
|
|
159
|
+
run: async () => {
|
|
160
|
+
// Walk skills/ at repo root + user dir; count SKILL.md files.
|
|
161
|
+
let count = 0;
|
|
162
|
+
const dirs = [
|
|
163
|
+
node_path_1.default.join(process.cwd(), 'skills'),
|
|
164
|
+
resolved.skillsDir,
|
|
165
|
+
];
|
|
166
|
+
for (const d of dirs) {
|
|
167
|
+
try {
|
|
168
|
+
const entries = await node_fs_1.promises.readdir(d, { withFileTypes: true });
|
|
169
|
+
for (const e of entries) {
|
|
170
|
+
if (!e.isDirectory())
|
|
171
|
+
continue;
|
|
172
|
+
try {
|
|
173
|
+
await node_fs_1.promises.access(node_path_1.default.join(d, e.name, 'SKILL.md'));
|
|
174
|
+
count += 1;
|
|
175
|
+
}
|
|
176
|
+
catch { /* not a skill — skip */ }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch { /* directory missing — skip */ }
|
|
180
|
+
}
|
|
181
|
+
return { status: `${count} available` };
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
label: 'Initializing tools',
|
|
186
|
+
run: async () => {
|
|
187
|
+
// Best-effort tool count: read the tools/v4/index source as a
|
|
188
|
+
// text count of registrations. Cheap and good enough for the
|
|
189
|
+
// "60 tools" status — avoids loading the tool registry itself.
|
|
190
|
+
try {
|
|
191
|
+
const src = await node_fs_1.promises.readFile(node_path_1.default.join(process.cwd(), 'tools', 'v4', 'index.ts'), 'utf8');
|
|
192
|
+
const matches = src.match(/registerTool\s*\(/g) ?? [];
|
|
193
|
+
return { status: `${matches.length || 0} registered` };
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return { status: 'registry ready' };
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
label: 'Configuring memory',
|
|
202
|
+
run: async () => {
|
|
203
|
+
await (0, paths_1.ensureAidenDirsExist)(resolved);
|
|
204
|
+
return { status: `${resolved.root} ready` };
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
];
|
|
208
|
+
}
|