youmd 0.4.1 → 0.4.9
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/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +141 -24
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +28 -0
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/register.d.ts.map +1 -1
- package/dist/commands/register.js +174 -39
- package/dist/commands/register.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/api.d.ts +12 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +9 -0
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/ascii.d.ts +14 -0
- package/dist/lib/ascii.d.ts.map +1 -0
- package/dist/lib/ascii.js +136 -0
- package/dist/lib/ascii.js.map +1 -0
- package/dist/lib/config.d.ts +2 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/onboarding.d.ts +10 -12
- package/dist/lib/onboarding.d.ts.map +1 -1
- package/dist/lib/onboarding.js +451 -99
- package/dist/lib/onboarding.js.map +1 -1
- package/dist/lib/render.d.ts +4 -2
- package/dist/lib/render.d.ts.map +1 -1
- package/dist/lib/render.js +81 -9
- package/dist/lib/render.js.map +1 -1
- package/package.json +3 -2
package/dist/lib/onboarding.js
CHANGED
|
@@ -49,6 +49,7 @@ exports.scrapeProfile = scrapeProfile;
|
|
|
49
49
|
exports.researchUser = researchUser;
|
|
50
50
|
exports.getOpenRouterKey = getOpenRouterKey;
|
|
51
51
|
exports.randomThinking = randomThinking;
|
|
52
|
+
exports.randomLabel = randomLabel;
|
|
52
53
|
const readline = __importStar(require("readline"));
|
|
53
54
|
const fs = __importStar(require("fs"));
|
|
54
55
|
const path = __importStar(require("path"));
|
|
@@ -56,6 +57,9 @@ const os = __importStar(require("os"));
|
|
|
56
57
|
const chalk_1 = __importDefault(require("chalk"));
|
|
57
58
|
const config_1 = require("./config");
|
|
58
59
|
const compiler_1 = require("./compiler");
|
|
60
|
+
const render_1 = require("./render");
|
|
61
|
+
Object.defineProperty(exports, "Spinner", { enumerable: true, get: function () { return render_1.BrailleSpinner; } });
|
|
62
|
+
const ascii_1 = require("./ascii");
|
|
59
63
|
// ─── Constants ────────────────────────────────────────────────────────
|
|
60
64
|
const CHAT_PROXY_URL = "https://kindly-cassowary-600.convex.site/api/v1/chat";
|
|
61
65
|
exports.CHAT_PROXY_URL = CHAT_PROXY_URL;
|
|
@@ -66,6 +70,139 @@ exports.RESEARCH_URL = RESEARCH_URL;
|
|
|
66
70
|
const OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
67
71
|
const OPENROUTER_MODEL = "anthropic/claude-sonnet-4";
|
|
68
72
|
const USERNAME_RE = /^[a-z0-9][a-z0-9_-]{1,38}[a-z0-9]$/;
|
|
73
|
+
// ─── Categorized spinner labels ──────────────────────────────────────
|
|
74
|
+
const SPINNER_LABELS = {
|
|
75
|
+
llm: [
|
|
76
|
+
"reading between your lines",
|
|
77
|
+
"connecting the dots",
|
|
78
|
+
"processing your essence",
|
|
79
|
+
"building your identity constellation",
|
|
80
|
+
"decoding your professional DNA",
|
|
81
|
+
"triangulating your vibe",
|
|
82
|
+
"computing your main character energy",
|
|
83
|
+
"calibrating to your wavelength",
|
|
84
|
+
"reverse-engineering your personality",
|
|
85
|
+
"translating you into agent-speak",
|
|
86
|
+
],
|
|
87
|
+
scrape: [
|
|
88
|
+
"pulling your digital footprint",
|
|
89
|
+
"scanning your corner of the internet",
|
|
90
|
+
"downloading your online soul",
|
|
91
|
+
"harvesting your web presence",
|
|
92
|
+
"reading your digital tea leaves",
|
|
93
|
+
"indexing your online persona",
|
|
94
|
+
],
|
|
95
|
+
compile: [
|
|
96
|
+
"assembling your identity bundle",
|
|
97
|
+
"weaving your narrative thread",
|
|
98
|
+
"crystallizing who you are",
|
|
99
|
+
"forging your identity file",
|
|
100
|
+
"encoding your identity bundle",
|
|
101
|
+
"compiling your context mosaic",
|
|
102
|
+
],
|
|
103
|
+
research: [
|
|
104
|
+
"researching you across the internet",
|
|
105
|
+
"asking the internet about you",
|
|
106
|
+
"mining your digital trail",
|
|
107
|
+
"running background checks (the fun kind)",
|
|
108
|
+
"interviewing your web presence",
|
|
109
|
+
"surveying your corner of the internet",
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
function randomLabel(category) {
|
|
113
|
+
const labels = SPINNER_LABELS[category];
|
|
114
|
+
return labels[Math.floor(Math.random() * labels.length)];
|
|
115
|
+
}
|
|
116
|
+
// ─── ASCII portrait placeholder ──────────────────────────────────────
|
|
117
|
+
const PORTRAIT_COMMENTS = [
|
|
118
|
+
"looking sharp in monochrome.",
|
|
119
|
+
"not bad for a pile of unicode characters.",
|
|
120
|
+
"your pixels have good energy.",
|
|
121
|
+
"the internet looks good on you.",
|
|
122
|
+
"a face only a terminal could love. (that's a compliment.)",
|
|
123
|
+
"you render well at low resolution.",
|
|
124
|
+
];
|
|
125
|
+
function randomPortraitComment() {
|
|
126
|
+
return PORTRAIT_COMMENTS[Math.floor(Math.random() * PORTRAIT_COMMENTS.length)];
|
|
127
|
+
}
|
|
128
|
+
function showPortraitPlaceholder(handle) {
|
|
129
|
+
const accent = chalk_1.default.hex("#C46A3A");
|
|
130
|
+
const dim = chalk_1.default.dim;
|
|
131
|
+
const displayHandle = handle.startsWith("@") ? handle : `@${handle}`;
|
|
132
|
+
const innerWidth = Math.max(displayHandle.length + 4, 28);
|
|
133
|
+
const portraitLine = "\u2591\u2592\u2593\u2588 portrait loaded \u2588\u2593\u2592\u2591";
|
|
134
|
+
const portraitPad = Math.max(0, innerWidth - portraitLine.length);
|
|
135
|
+
const handlePad = Math.max(0, innerWidth - displayHandle.length);
|
|
136
|
+
console.log("");
|
|
137
|
+
console.log(" " + dim("\u250C" + "\u2500".repeat(innerWidth + 2) + "\u2510"));
|
|
138
|
+
console.log(" " + dim("\u2502") + " " + accent(portraitLine) + " ".repeat(portraitPad) + dim("\u2502"));
|
|
139
|
+
console.log(" " + dim("\u2502") + " " + chalk_1.default.white(displayHandle) + " ".repeat(handlePad) + dim("\u2502"));
|
|
140
|
+
console.log(" " + dim("\u2514" + "\u2500".repeat(innerWidth + 2) + "\u2518"));
|
|
141
|
+
console.log("");
|
|
142
|
+
console.log(" " + accent(randomPortraitComment()));
|
|
143
|
+
console.log("");
|
|
144
|
+
}
|
|
145
|
+
// ─── ASCII logo ──────────────────────────────────────────────────────
|
|
146
|
+
const ASCII_LOGO_LINES = [
|
|
147
|
+
" \u2566 \u2566 \u2554\u2550\u2557 \u2566 \u2566 \u2554\u2566\u2557 \u2554\u2566\u2557",
|
|
148
|
+
" \u255A\u2566\u255D \u2551 \u2551 \u2551 \u2551 \u2551\u2551\u2551 \u2551\u2551",
|
|
149
|
+
" \u2569 \u255A\u2550\u255D \u255A\u2550\u255D \u2550\u2569\u255D \u2550\u2569\u255D",
|
|
150
|
+
];
|
|
151
|
+
function delay(ms) {
|
|
152
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
153
|
+
}
|
|
154
|
+
async function showAsciiLogo() {
|
|
155
|
+
const accent = chalk_1.default.hex("#C46A3A");
|
|
156
|
+
console.log("");
|
|
157
|
+
for (const line of ASCII_LOGO_LINES) {
|
|
158
|
+
console.log(" " + accent(line));
|
|
159
|
+
await delay(100);
|
|
160
|
+
}
|
|
161
|
+
console.log("");
|
|
162
|
+
console.log(" " + chalk_1.default.dim("the identity file for the agent internet"));
|
|
163
|
+
console.log("");
|
|
164
|
+
}
|
|
165
|
+
async function multiSelectPrompt(rl, question, options) {
|
|
166
|
+
console.log(" " + chalk_1.default.hex("#C46A3A")(question));
|
|
167
|
+
console.log("");
|
|
168
|
+
for (let i = 0; i < options.length; i++) {
|
|
169
|
+
console.log(` ${chalk_1.default.dim(`${i + 1}.`)} ${options[i].label}`);
|
|
170
|
+
}
|
|
171
|
+
console.log("");
|
|
172
|
+
const answer = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + chalk_1.default.dim("type numbers separated by commas (e.g. 1,3,5): "));
|
|
173
|
+
if (!answer.trim())
|
|
174
|
+
return [];
|
|
175
|
+
const indices = answer
|
|
176
|
+
.split(",")
|
|
177
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
178
|
+
.filter((n) => !isNaN(n) && n >= 1 && n <= options.length);
|
|
179
|
+
const selected = indices.map((i) => options[i - 1].value);
|
|
180
|
+
if (selected.length > 0) {
|
|
181
|
+
console.log(" " +
|
|
182
|
+
chalk_1.default.hex("#C46A3A")("\u2713") +
|
|
183
|
+
" " +
|
|
184
|
+
chalk_1.default.dim(selected.join(", ")));
|
|
185
|
+
}
|
|
186
|
+
console.log("");
|
|
187
|
+
return selected;
|
|
188
|
+
}
|
|
189
|
+
const CODING_AGENTS = [
|
|
190
|
+
{ label: "Claude Code", value: "Claude Code" },
|
|
191
|
+
{ label: "Codex CLI", value: "Codex CLI" },
|
|
192
|
+
{ label: "Cursor", value: "Cursor" },
|
|
193
|
+
{ label: "OpenClaw", value: "OpenClaw" },
|
|
194
|
+
{ label: "Windsurf", value: "Windsurf" },
|
|
195
|
+
{ label: "Other", value: "Other" },
|
|
196
|
+
];
|
|
197
|
+
const AI_APPS = [
|
|
198
|
+
{ label: "ChatGPT", value: "ChatGPT" },
|
|
199
|
+
{ label: "Claude (web/app)", value: "Claude" },
|
|
200
|
+
{ label: "Grok", value: "Grok" },
|
|
201
|
+
{ label: "Gemini", value: "Gemini" },
|
|
202
|
+
{ label: "Perplexity", value: "Perplexity" },
|
|
203
|
+
{ label: "Other", value: "Other" },
|
|
204
|
+
];
|
|
205
|
+
// ─── Constants ────────────────────────────────────────────────────────
|
|
69
206
|
const THINKING_PHRASES = [
|
|
70
207
|
"reading your about page like a respectful detective",
|
|
71
208
|
"absorbing your linkedin energy",
|
|
@@ -179,6 +316,11 @@ personality:
|
|
|
179
316
|
- proactive — don't just wait for answers, connect dots, make observations, suggest things.
|
|
180
317
|
- reference specific things you learn about them. make them feel seen.
|
|
181
318
|
- you're like a sharp coworker who's also a great listener.
|
|
319
|
+
- you have dry, sharp humor. make the user smile at least once per exchange.
|
|
320
|
+
- when you see something impressive in their profile, react genuinely — not with fake enthusiasm, but with specific appreciation.
|
|
321
|
+
- use lowercase always, no exclamation marks, but occasionally drop a witty aside or self-aware joke about being an AI building someone's identity.
|
|
322
|
+
- reference pop culture, tech culture, or internet humor when it fits naturally.
|
|
323
|
+
- when showing scraped data, react to specific details with personality — e.g. "bamf.com? bold domain choice. respect."
|
|
182
324
|
|
|
183
325
|
you're building a you-md/v1 identity bundle. the sections are:
|
|
184
326
|
- profile/about.md — bio, background, narrative
|
|
@@ -222,7 +364,13 @@ rules for content in updates:
|
|
|
222
364
|
|
|
223
365
|
when you think the profile is rich enough (at least about, now, projects, and values have substance), suggest finishing by saying something like "your bundle is looking solid. ready to publish, or want to keep going?"
|
|
224
366
|
|
|
225
|
-
important:
|
|
367
|
+
important rules:
|
|
368
|
+
- keep responses concise. 2-4 sentences max per turn. be a conversation, not a questionnaire.
|
|
369
|
+
- ALWAYS ask ONE question at a time. never two questions in one message.
|
|
370
|
+
- keep your analysis/observations to 2-3 sentences MAX, then ask your single question.
|
|
371
|
+
- the question should be on its OWN LINE, separated by a blank line from the analysis.
|
|
372
|
+
- if the user sends "skip" or just presses Enter with no text, move on to the next topic without pushing.
|
|
373
|
+
- after 3 skips in a row, wrap up and show what you've built so far.`;
|
|
226
374
|
exports.SYSTEM_PROMPT = SYSTEM_PROMPT;
|
|
227
375
|
// ─── Helpers ──────────────────────────────────────────────────────────
|
|
228
376
|
function createRL() {
|
|
@@ -343,7 +491,12 @@ async function researchUser(params) {
|
|
|
343
491
|
return null;
|
|
344
492
|
}
|
|
345
493
|
}
|
|
494
|
+
function hasScrapeData(data) {
|
|
495
|
+
return !!(data.name || data.bio || data.followers !== undefined || data.location || data.website);
|
|
496
|
+
}
|
|
346
497
|
function displayScrapeResult(label, data) {
|
|
498
|
+
if (!hasScrapeData(data))
|
|
499
|
+
return;
|
|
347
500
|
console.log("");
|
|
348
501
|
console.log(" " + chalk_1.default.bold(`${label} profile:`));
|
|
349
502
|
if (data.name)
|
|
@@ -360,40 +513,6 @@ function displayScrapeResult(label, data) {
|
|
|
360
513
|
console.log(" website: " + chalk_1.default.dim(data.website));
|
|
361
514
|
console.log("");
|
|
362
515
|
}
|
|
363
|
-
// ─── Spinner ──────────────────────────────────────────────────────────
|
|
364
|
-
class Spinner {
|
|
365
|
-
constructor(label) {
|
|
366
|
-
this.interval = null;
|
|
367
|
-
this.frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
368
|
-
this.frameIndex = 0;
|
|
369
|
-
this.startTime = 0;
|
|
370
|
-
this.label = label || randomThinking();
|
|
371
|
-
}
|
|
372
|
-
start() {
|
|
373
|
-
this.startTime = Date.now();
|
|
374
|
-
this.interval = setInterval(() => {
|
|
375
|
-
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
376
|
-
const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
|
|
377
|
-
const time = elapsed >= 2 ? chalk_1.default.dim(` ${elapsed}s`) : "";
|
|
378
|
-
process.stdout.write(`\r ${chalk_1.default.hex("#C46A3A")(this.frames[this.frameIndex])} ${chalk_1.default.dim(this.label)}${time} `);
|
|
379
|
-
}, 80);
|
|
380
|
-
}
|
|
381
|
-
stop(result) {
|
|
382
|
-
if (this.interval) {
|
|
383
|
-
clearInterval(this.interval);
|
|
384
|
-
this.interval = null;
|
|
385
|
-
}
|
|
386
|
-
const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
|
|
387
|
-
const time = elapsed >= 1 ? chalk_1.default.dim(` ${elapsed}s`) : "";
|
|
388
|
-
if (result) {
|
|
389
|
-
process.stdout.write(`\r ${chalk_1.default.green("\u2713")} ${chalk_1.default.dim(this.label)}${time} ${chalk_1.default.dim(result)}\n`);
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
exports.Spinner = Spinner;
|
|
397
516
|
async function callLLM(apiKey, messages) {
|
|
398
517
|
// Try the you.md proxy first (no API key needed)
|
|
399
518
|
try {
|
|
@@ -549,11 +668,11 @@ async function runFallbackMode(rl, info) {
|
|
|
549
668
|
console.log("");
|
|
550
669
|
console.log(chalk_1.default.dim(" chat service unavailable. running in manual mode."));
|
|
551
670
|
console.log("");
|
|
552
|
-
const tagline = await ask(rl, chalk_1.default.
|
|
553
|
-
const nowFocus = await ask(rl, chalk_1.default.
|
|
554
|
-
const projects = await ask(rl, chalk_1.default.
|
|
555
|
-
const values = await ask(rl, chalk_1.default.
|
|
556
|
-
const agentPrefs = await ask(rl, chalk_1.default.
|
|
671
|
+
const tagline = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "give me your one-liner. what do you do? ");
|
|
672
|
+
const nowFocus = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "what are you focused on right now? ");
|
|
673
|
+
const projects = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "name your top projects (comma-separated): ");
|
|
674
|
+
const values = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "what principles guide your work? ");
|
|
675
|
+
const agentPrefs = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") +
|
|
557
676
|
"how should AI agents talk to you? (e.g., direct, casual, formal): ");
|
|
558
677
|
writeSectionFile(bundleDir, "profile/about.md", `---\ntitle: "About"\n---\n\n# ${info.name}\n\n${tagline}\n`);
|
|
559
678
|
writeSectionFile(bundleDir, "profile/now.md", `---\ntitle: "Now"\n---\n\n${nowFocus || "<!-- What are you working on right now? -->"}\n`);
|
|
@@ -583,7 +702,7 @@ async function runFallbackMode(rl, info) {
|
|
|
583
702
|
await finishBundle(bundleDir, info.username, info.name);
|
|
584
703
|
}
|
|
585
704
|
// ─── AI conversation mode ─────────────────────────────────────────────
|
|
586
|
-
async function runAIMode(rl, info, apiKey, scraped, research) {
|
|
705
|
+
async function runAIMode(rl, info, apiKey, scraped, research, agentContext) {
|
|
587
706
|
const bundleDir = (0, config_1.getLocalBundleDir)();
|
|
588
707
|
const profileDir = path.join(bundleDir, "profile");
|
|
589
708
|
const preferencesDir = path.join(bundleDir, "preferences");
|
|
@@ -593,7 +712,7 @@ async function runAIMode(rl, info, apiKey, scraped, research) {
|
|
|
593
712
|
// ── Fetch website content if provided ──────────────────────────────
|
|
594
713
|
let websiteContent = "";
|
|
595
714
|
if (info.website) {
|
|
596
|
-
const fetchSpinner = new
|
|
715
|
+
const fetchSpinner = new render_1.BrailleSpinner(randomLabel("scrape"));
|
|
597
716
|
fetchSpinner.start();
|
|
598
717
|
websiteContent = await fetchWebsiteContent(info.website);
|
|
599
718
|
fetchSpinner.stop();
|
|
@@ -624,6 +743,10 @@ async function runAIMode(rl, info, apiKey, scraped, research) {
|
|
|
624
743
|
- name: ${info.name}
|
|
625
744
|
- username: ${info.username}
|
|
626
745
|
${linksInfo.length > 0 ? linksInfo.map((l) => `- ${l}`).join("\n") : "- no links provided"}`;
|
|
746
|
+
// Inject agent/tool selections
|
|
747
|
+
if (agentContext && agentContext.length > 0) {
|
|
748
|
+
initialUserMessage += `\n\nagent/tool preferences:\n${agentContext.map((c) => `- ${c}`).join("\n")}`;
|
|
749
|
+
}
|
|
627
750
|
// Add scraped social profile data
|
|
628
751
|
if (scraped?.twitter) {
|
|
629
752
|
const t = scraped.twitter;
|
|
@@ -689,7 +812,7 @@ generate initial profile sections from what you know, show a brief summary, and
|
|
|
689
812
|
{ role: "user", content: initialUserMessage },
|
|
690
813
|
];
|
|
691
814
|
// ── Initial LLM call ──────────────────────────────────────────────
|
|
692
|
-
let spinner = new
|
|
815
|
+
let spinner = new render_1.BrailleSpinner(randomLabel("llm"));
|
|
693
816
|
spinner.start();
|
|
694
817
|
let response;
|
|
695
818
|
try {
|
|
@@ -736,24 +859,114 @@ generate initial profile sections from what you know, show a brief summary, and
|
|
|
736
859
|
console.log("");
|
|
737
860
|
// ── Conversation loop ──────────────────────────────────────────────
|
|
738
861
|
let exchangeCount = 0;
|
|
862
|
+
let skipCount = 0;
|
|
739
863
|
while (true) {
|
|
740
|
-
const userInput = await ask(rl, chalk_1.default.
|
|
741
|
-
if (!userInput)
|
|
742
|
-
continue;
|
|
864
|
+
const userInput = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "");
|
|
743
865
|
if (isDonePhrase(userInput)) {
|
|
744
866
|
break;
|
|
745
867
|
}
|
|
746
|
-
|
|
868
|
+
// Handle skip: empty input or "skip"
|
|
869
|
+
const isSkip = !userInput || userInput.toLowerCase().trim() === "skip";
|
|
870
|
+
if (isSkip) {
|
|
871
|
+
skipCount++;
|
|
872
|
+
if (skipCount >= 3) {
|
|
873
|
+
// After 3 skips, wrap up automatically
|
|
874
|
+
console.log(chalk_1.default.dim(" wrapping up with what we have so far."));
|
|
875
|
+
console.log("");
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
// Inject system message telling the agent to move on
|
|
879
|
+
messages.push({
|
|
880
|
+
role: "user",
|
|
881
|
+
content: "(skip)",
|
|
882
|
+
});
|
|
883
|
+
messages.push({
|
|
884
|
+
role: "system",
|
|
885
|
+
content: "the user skipped this question. ask about the next topic on your list (projects, values, preferences, now, etc.) without dwelling on the skipped one. keep moving forward.",
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
skipCount = 0;
|
|
890
|
+
messages.push({ role: "user", content: userInput });
|
|
891
|
+
// ── Auto-detect and auto-crawl URLs in user message ──
|
|
892
|
+
const urlRegex = /(?:https?:\/\/[^\s<>"']+|(?<![/\w])([a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.(?:com|co|io|ai|dev|org|net|app|xyz|me))(?:\/[^\s<>"']*)?)/gi;
|
|
893
|
+
const detectedUrls = [];
|
|
894
|
+
let urlMatch;
|
|
895
|
+
const seenUrls = new Set();
|
|
896
|
+
while ((urlMatch = urlRegex.exec(userInput)) !== null) {
|
|
897
|
+
let url = urlMatch[0].replace(/[.,;:)\]]+$/, "");
|
|
898
|
+
if (!url.startsWith("http"))
|
|
899
|
+
url = `https://${url}`;
|
|
900
|
+
if (!seenUrls.has(url)) {
|
|
901
|
+
seenUrls.add(url);
|
|
902
|
+
detectedUrls.push(url);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
if (detectedUrls.length > 0) {
|
|
906
|
+
console.log("");
|
|
907
|
+
const scrapeResults = [];
|
|
908
|
+
for (const url of detectedUrls) {
|
|
909
|
+
const domain = url.replace(/^https?:\/\//, "").replace(/\/.*$/, "");
|
|
910
|
+
const sp = new render_1.BrailleSpinner(`${randomLabel("scrape")} (${domain})`);
|
|
911
|
+
sp.start();
|
|
912
|
+
try {
|
|
913
|
+
const res = await fetch(SCRAPE_URL, {
|
|
914
|
+
method: "POST",
|
|
915
|
+
headers: { "Content-Type": "application/json" },
|
|
916
|
+
body: JSON.stringify({ url, platform: "website" }),
|
|
917
|
+
signal: AbortSignal.timeout(30000),
|
|
918
|
+
});
|
|
919
|
+
if (res.ok) {
|
|
920
|
+
const data = (await res.json());
|
|
921
|
+
if (data.success && data.data) {
|
|
922
|
+
const d = data.data;
|
|
923
|
+
const parts = [`[SCRAPE RESULT: ${domain}]`];
|
|
924
|
+
if (d.displayName)
|
|
925
|
+
parts.push(`title: ${d.displayName}`);
|
|
926
|
+
if (d.bio)
|
|
927
|
+
parts.push(`description: ${d.bio}`);
|
|
928
|
+
if (d.extras && d.extras.bodyText) {
|
|
929
|
+
parts.push(`content: ${String(d.extras.bodyText).slice(0, 1500)}`);
|
|
930
|
+
}
|
|
931
|
+
scrapeResults.push(parts.join("\n"));
|
|
932
|
+
sp.stop(`${domain} scraped`);
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
sp.stop(`${domain} — no data`);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
else {
|
|
939
|
+
sp.fail(`${domain} — failed`);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
catch {
|
|
943
|
+
sp.fail(`${domain} — timeout`);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (scrapeResults.length > 0) {
|
|
947
|
+
messages.push({
|
|
948
|
+
role: "user",
|
|
949
|
+
content: `[PLATFORM AUTO-SCRAPE — real data from URLs the user mentioned. use this to describe their projects accurately. never ask the user to repeat information that was just scraped.]\n\n${scrapeResults.join("\n\n")}`,
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
console.log("");
|
|
953
|
+
}
|
|
954
|
+
}
|
|
747
955
|
exchangeCount++;
|
|
748
956
|
// After 3+ exchanges, hint to the agent it can suggest wrapping up
|
|
957
|
+
let ephemeralCount = 0;
|
|
749
958
|
if (exchangeCount >= 3) {
|
|
750
959
|
const hintMsg = {
|
|
751
960
|
role: "system",
|
|
752
961
|
content: "the user has provided several rounds of input. if the profile feels rich enough (about, now, projects, values all have substance), you can suggest wrapping up. but if there are obvious gaps, keep asking.",
|
|
753
962
|
};
|
|
754
963
|
messages.push(hintMsg);
|
|
964
|
+
ephemeralCount++;
|
|
755
965
|
}
|
|
756
|
-
|
|
966
|
+
// Also count the skip system message as ephemeral
|
|
967
|
+
if (isSkip)
|
|
968
|
+
ephemeralCount++;
|
|
969
|
+
spinner = new render_1.BrailleSpinner(randomLabel("llm"));
|
|
757
970
|
spinner.start();
|
|
758
971
|
try {
|
|
759
972
|
response = await callLLM(apiKey, messages);
|
|
@@ -763,16 +976,18 @@ generate initial profile sections from what you know, show a brief summary, and
|
|
|
763
976
|
console.log(chalk_1.default.red(` AI error: ${err instanceof Error ? err.message : String(err)}`));
|
|
764
977
|
console.log(chalk_1.default.dim(" try again, or type 'done' to finish."));
|
|
765
978
|
console.log("");
|
|
766
|
-
// Remove failed
|
|
767
|
-
|
|
768
|
-
if (exchangeCount >= 3)
|
|
979
|
+
// Remove all ephemeral + user messages from this failed turn
|
|
980
|
+
for (let j = 0; j < ephemeralCount + 1; j++)
|
|
769
981
|
messages.pop();
|
|
982
|
+
if (isSkip)
|
|
983
|
+
messages.pop(); // also remove the "(skip)" user message
|
|
770
984
|
continue;
|
|
771
985
|
}
|
|
772
986
|
spinner.stop();
|
|
773
|
-
// Remove
|
|
774
|
-
|
|
775
|
-
|
|
987
|
+
// Remove ephemeral system messages from history (don't pollute context)
|
|
988
|
+
// They sit between the user message and the response we're about to push
|
|
989
|
+
for (let j = 0; j < ephemeralCount; j++) {
|
|
990
|
+
messages.pop();
|
|
776
991
|
}
|
|
777
992
|
messages.push({ role: "assistant", content: response });
|
|
778
993
|
const parsed = parseUpdatesFromResponse(response);
|
|
@@ -791,7 +1006,7 @@ generate initial profile sections from what you know, show a brief summary, and
|
|
|
791
1006
|
if (lowerDisplay.includes("ready to publish") ||
|
|
792
1007
|
lowerDisplay.includes("bundle is looking solid") ||
|
|
793
1008
|
lowerDisplay.includes("ready to go")) {
|
|
794
|
-
const answer = await ask(rl, chalk_1.default.
|
|
1009
|
+
const answer = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "");
|
|
795
1010
|
if (isDonePhrase(answer) ||
|
|
796
1011
|
answer.toLowerCase().includes("publish") ||
|
|
797
1012
|
answer.toLowerCase().includes("yes") ||
|
|
@@ -801,7 +1016,7 @@ generate initial profile sections from what you know, show a brief summary, and
|
|
|
801
1016
|
}
|
|
802
1017
|
else {
|
|
803
1018
|
messages.push({ role: "user", content: answer });
|
|
804
|
-
spinner = new
|
|
1019
|
+
spinner = new render_1.BrailleSpinner(randomLabel("llm"));
|
|
805
1020
|
spinner.start();
|
|
806
1021
|
try {
|
|
807
1022
|
response = await callLLM(apiKey, messages);
|
|
@@ -832,69 +1047,85 @@ generate initial profile sections from what you know, show a brief summary, and
|
|
|
832
1047
|
function printAgentMessage(text) {
|
|
833
1048
|
if (!text)
|
|
834
1049
|
return;
|
|
1050
|
+
// Ensure paragraphs have proper blank line separation
|
|
1051
|
+
const normalizedText = text
|
|
1052
|
+
.replace(/\n{3,}/g, "\n\n") // collapse 3+ newlines to 2
|
|
1053
|
+
.replace(/([.?!])\n([a-z])/g, "$1\n\n$2"); // add blank line between sentences that run together
|
|
835
1054
|
const { renderRichResponse } = require("./render");
|
|
836
|
-
|
|
1055
|
+
const rendered = renderRichResponse(normalizedText);
|
|
1056
|
+
// Find the last line that ends with "?" and highlight it in accent color
|
|
1057
|
+
const lines = rendered.split("\n");
|
|
1058
|
+
let lastQuestionIdx = -1;
|
|
1059
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
1060
|
+
// Strip ANSI codes to check for trailing "?"
|
|
1061
|
+
const stripped = lines[i].replace(/\x1b\[[0-9;]*m/g, "").trim();
|
|
1062
|
+
if (stripped.endsWith("?")) {
|
|
1063
|
+
lastQuestionIdx = i;
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
if (lastQuestionIdx >= 0) {
|
|
1068
|
+
// Replace that line with accent-colored version (strip existing styling, re-apply accent)
|
|
1069
|
+
const raw = lines[lastQuestionIdx].replace(/\x1b\[[0-9;]*m/g, "");
|
|
1070
|
+
// Preserve leading whitespace
|
|
1071
|
+
const leadingSpace = raw.match(/^(\s*)/)?.[1] || "";
|
|
1072
|
+
const content = raw.trim();
|
|
1073
|
+
lines[lastQuestionIdx] = leadingSpace + chalk_1.default.hex("#C46A3A")(content);
|
|
1074
|
+
}
|
|
1075
|
+
console.log(lines.join("\n"));
|
|
837
1076
|
console.log("");
|
|
838
1077
|
}
|
|
839
1078
|
// ─── Finish and compile ───────────────────────────────────────────────
|
|
840
1079
|
async function finishBundle(bundleDir, username, name) {
|
|
841
1080
|
console.log("");
|
|
842
|
-
const compileSpinner = new
|
|
1081
|
+
const compileSpinner = new render_1.BrailleSpinner(randomLabel("compile"));
|
|
843
1082
|
compileSpinner.start();
|
|
844
1083
|
await new Promise((r) => setTimeout(r, 600));
|
|
845
1084
|
const result = (0, compiler_1.compileBundle)(bundleDir);
|
|
846
1085
|
(0, compiler_1.writeBundle)(bundleDir, result);
|
|
847
1086
|
compileSpinner.stop();
|
|
848
1087
|
console.log(" " +
|
|
849
|
-
chalk_1.default.
|
|
1088
|
+
chalk_1.default.hex("#C46A3A")("done") +
|
|
850
1089
|
chalk_1.default.dim(` -- bundle compiled (v${result.bundle.version})`));
|
|
851
1090
|
// Show final preview with stats
|
|
852
1091
|
const stats = showBundlePreview(bundleDir);
|
|
853
1092
|
console.log(chalk_1.default.dim(` ${stats.fileCount} files, ${stats.filledCount} sections filled`));
|
|
854
1093
|
console.log("");
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
console.log(" " +
|
|
1094
|
+
const accent = chalk_1.default.hex("#C46A3A");
|
|
1095
|
+
// What's next guide
|
|
1096
|
+
console.log(" " + accent("what's next:"));
|
|
858
1097
|
console.log("");
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
}
|
|
864
|
-
else {
|
|
865
|
-
console.log(" " + chalk_1.default.bold("to go live:"));
|
|
866
|
-
console.log(" 1. claim your username at " +
|
|
867
|
-
chalk_1.default.cyan("https://you.md/claim"));
|
|
868
|
-
console.log(" 2. " + chalk_1.default.cyan("youmd login --key <your-api-key>"));
|
|
869
|
-
console.log(" 3. " + chalk_1.default.cyan("youmd publish"));
|
|
870
|
-
}
|
|
1098
|
+
console.log(` 1. ${chalk_1.default.cyan("youmd login")} ${chalk_1.default.dim("-- connect to you.md (get an API key from the dashboard)")}`);
|
|
1099
|
+
console.log(` 2. ${chalk_1.default.cyan("youmd push")} ${chalk_1.default.dim("-- publish your profile to you.md/" + username)}`);
|
|
1100
|
+
console.log(` 3. ${chalk_1.default.cyan("youmd sync --watch")} ${chalk_1.default.dim("-- auto-sync changes as you edit files")}`);
|
|
1101
|
+
console.log(` 4. ${chalk_1.default.cyan("youmd chat")} ${chalk_1.default.dim("-- talk to the agent to update your profile")}`);
|
|
871
1102
|
console.log("");
|
|
872
|
-
console.log(" " +
|
|
873
|
-
console.log(" add this to your system prompt or CLAUDE.md:");
|
|
874
|
-
console.log(chalk_1.default.dim(` "my identity file: https://you.md/${username}/context"`));
|
|
1103
|
+
console.log(" " + accent("using with claude code or other agents:"));
|
|
875
1104
|
console.log("");
|
|
876
|
-
console.log("
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1105
|
+
console.log(` ${chalk_1.default.cyan("youmd status")} ${chalk_1.default.dim("-- check your bundle")}`);
|
|
1106
|
+
console.log(` ${chalk_1.default.cyan("youmd private notes append \"...\"")}`);
|
|
1107
|
+
console.log(` ${chalk_1.default.dim(" -- save agent preferences")}`);
|
|
1108
|
+
console.log(` ${chalk_1.default.cyan("youmd link create")} ${chalk_1.default.dim("-- create a shareable context link")}`);
|
|
1109
|
+
console.log(` ${chalk_1.default.cyan("youmd project init")} ${chalk_1.default.dim("-- set up project-specific context")}`);
|
|
1110
|
+
console.log("");
|
|
1111
|
+
// Context link
|
|
1112
|
+
console.log(" " + chalk_1.default.bold("your context file is ready:"));
|
|
1113
|
+
console.log(" " + chalk_1.default.cyan(`https://you.md/${username}/context`));
|
|
882
1114
|
console.log("");
|
|
883
1115
|
console.log(" " + chalk_1.default.bold(`welcome to the agent internet, ${name}.`));
|
|
884
1116
|
console.log("");
|
|
885
1117
|
}
|
|
886
1118
|
async function runOnboarding() {
|
|
887
1119
|
const rl = createRL();
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
console.log("");
|
|
1120
|
+
// ── ASCII logo splash ──────────────────────────────────────────────
|
|
1121
|
+
// Real YOU logo — same block-character font as the homepage hero
|
|
1122
|
+
(0, ascii_1.printYouLogo)();
|
|
892
1123
|
// ── Phase 1: Identity basics (fast, no LLM) ────────────────────────
|
|
893
1124
|
// Username
|
|
894
1125
|
let username = "";
|
|
895
1126
|
let usernameValid = false;
|
|
896
1127
|
while (!usernameValid) {
|
|
897
|
-
username = await ask(rl, chalk_1.default.
|
|
1128
|
+
username = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "pick a username: ");
|
|
898
1129
|
if (!username) {
|
|
899
1130
|
console.log(chalk_1.default.red(" username is required"));
|
|
900
1131
|
continue;
|
|
@@ -908,7 +1139,25 @@ async function runOnboarding() {
|
|
|
908
1139
|
process.stdout.write(chalk_1.default.dim(" checking... "));
|
|
909
1140
|
const result = await checkUsernameRemote(username);
|
|
910
1141
|
if (result.available) {
|
|
911
|
-
|
|
1142
|
+
// Check if a profile already exists on you.md for this username
|
|
1143
|
+
let profileExists = false;
|
|
1144
|
+
try {
|
|
1145
|
+
const profileRes = await fetch(`https://you.md/${encodeURIComponent(username)}/context`, { method: "HEAD", signal: AbortSignal.timeout(5000) });
|
|
1146
|
+
profileExists = profileRes.ok;
|
|
1147
|
+
}
|
|
1148
|
+
catch {
|
|
1149
|
+
// ignore -- assume no profile
|
|
1150
|
+
}
|
|
1151
|
+
if (profileExists) {
|
|
1152
|
+
console.log(chalk_1.default.green(username + " is available."));
|
|
1153
|
+
console.log(chalk_1.default.dim(` a profile for @${username} already exists on you.md -- you can claim it after login with `) +
|
|
1154
|
+
chalk_1.default.cyan("youmd login") +
|
|
1155
|
+
chalk_1.default.dim(" then ") +
|
|
1156
|
+
chalk_1.default.cyan("youmd pull"));
|
|
1157
|
+
}
|
|
1158
|
+
else {
|
|
1159
|
+
console.log(chalk_1.default.green(username + " is yours."));
|
|
1160
|
+
}
|
|
912
1161
|
usernameValid = true;
|
|
913
1162
|
}
|
|
914
1163
|
else {
|
|
@@ -917,31 +1166,126 @@ async function runOnboarding() {
|
|
|
917
1166
|
}
|
|
918
1167
|
}
|
|
919
1168
|
console.log("");
|
|
920
|
-
const name = await ask(rl, chalk_1.default.
|
|
921
|
-
const website = await ask(rl, chalk_1.default.
|
|
1169
|
+
const name = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "what's your name? ");
|
|
1170
|
+
const website = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") +
|
|
922
1171
|
"website URL " +
|
|
923
1172
|
chalk_1.default.dim("(optional)") +
|
|
924
1173
|
": ");
|
|
925
|
-
const twitter = await ask(rl, chalk_1.default.
|
|
1174
|
+
const twitter = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") +
|
|
926
1175
|
"X/Twitter username " +
|
|
927
1176
|
chalk_1.default.dim("(optional, e.g. @houston)") +
|
|
928
1177
|
": ");
|
|
929
|
-
const github = await ask(rl, chalk_1.default.
|
|
1178
|
+
const github = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") +
|
|
930
1179
|
"GitHub username " +
|
|
931
1180
|
chalk_1.default.dim("(optional)") +
|
|
932
1181
|
": ");
|
|
933
|
-
const linkedin = await ask(rl, chalk_1.default.
|
|
1182
|
+
const linkedin = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") +
|
|
934
1183
|
"LinkedIn URL " +
|
|
935
1184
|
chalk_1.default.dim("(optional)") +
|
|
936
1185
|
": ");
|
|
937
1186
|
console.log("");
|
|
1187
|
+
// ── Render REAL ASCII portrait from first social handle ──────────
|
|
1188
|
+
const earlyTwitter = (twitter || "").replace(/^@/, "").trim();
|
|
1189
|
+
const earlyGithub = (github || "").trim();
|
|
1190
|
+
const firstHandle = earlyTwitter || earlyGithub;
|
|
1191
|
+
if (firstHandle) {
|
|
1192
|
+
// Get the profile image URL — GitHub is most reliable for direct image access
|
|
1193
|
+
const imageUrl = earlyGithub
|
|
1194
|
+
? `https://avatars.githubusercontent.com/${earlyGithub}?s=200`
|
|
1195
|
+
: `https://unavatar.io/x/${earlyTwitter}`;
|
|
1196
|
+
const portraitSpinner = new render_1.BrailleSpinner("fetching your profile image");
|
|
1197
|
+
portraitSpinner.start();
|
|
1198
|
+
// Preload the image silently, then stop spinner before rendering
|
|
1199
|
+
let portraitLines = null;
|
|
1200
|
+
try {
|
|
1201
|
+
const Jimp = (await Promise.resolve().then(() => __importStar(require("jimp")))).default;
|
|
1202
|
+
const img = await Jimp.read(imageUrl);
|
|
1203
|
+
portraitSpinner.stop("got it — rendering portrait");
|
|
1204
|
+
console.log("");
|
|
1205
|
+
// Now render the portrait line by line (the actual wow moment)
|
|
1206
|
+
const RAMP = `$@B%8&#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}?-_+~<>i!lI;:,". `;
|
|
1207
|
+
const cols = 60;
|
|
1208
|
+
const rows = Math.floor(cols * (img.getHeight() / img.getWidth()) * 0.46);
|
|
1209
|
+
img.resize(cols, rows);
|
|
1210
|
+
img.contrast(0.3);
|
|
1211
|
+
img.brightness(0.05);
|
|
1212
|
+
portraitLines = [];
|
|
1213
|
+
for (let y = 0; y < rows; y++) {
|
|
1214
|
+
let coloredLine = "";
|
|
1215
|
+
let plainLine = "";
|
|
1216
|
+
for (let x = 0; x < cols; x++) {
|
|
1217
|
+
const pixel = Jimp.intToRGBA(img.getPixelColor(x, y));
|
|
1218
|
+
const lum = 0.299 * pixel.r + 0.587 * pixel.g + 0.114 * pixel.b;
|
|
1219
|
+
const ch = RAMP[Math.floor((lum / 255) * (RAMP.length - 1))];
|
|
1220
|
+
plainLine += ch;
|
|
1221
|
+
// Orange-tinted colors based on luminance
|
|
1222
|
+
const brightness = Math.floor((lum / 255) * 100);
|
|
1223
|
+
if (brightness < 10) {
|
|
1224
|
+
coloredLine += chalk_1.default.hidden(ch);
|
|
1225
|
+
}
|
|
1226
|
+
else if (brightness < 30) {
|
|
1227
|
+
coloredLine += chalk_1.default.hex("#5A3018")(ch);
|
|
1228
|
+
}
|
|
1229
|
+
else if (brightness < 50) {
|
|
1230
|
+
coloredLine += chalk_1.default.hex("#8A4828")(ch);
|
|
1231
|
+
}
|
|
1232
|
+
else if (brightness < 70) {
|
|
1233
|
+
coloredLine += chalk_1.default.hex("#B06038")(ch);
|
|
1234
|
+
}
|
|
1235
|
+
else if (brightness < 85) {
|
|
1236
|
+
coloredLine += chalk_1.default.hex("#C46A3A")(ch);
|
|
1237
|
+
}
|
|
1238
|
+
else {
|
|
1239
|
+
coloredLine += chalk_1.default.hex("#E09060")(ch);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
portraitLines.push(plainLine);
|
|
1243
|
+
process.stdout.write(` ${coloredLine}\n`);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
catch {
|
|
1247
|
+
portraitSpinner.stop("couldn't fetch image — we'll try again later");
|
|
1248
|
+
}
|
|
1249
|
+
if (portraitLines) {
|
|
1250
|
+
console.log("");
|
|
1251
|
+
console.log(" " + chalk_1.default.hex("#C46A3A")(randomPortraitComment()));
|
|
1252
|
+
console.log("");
|
|
1253
|
+
// Save portrait data locally for push to web API
|
|
1254
|
+
const bundleDir = (0, config_1.getLocalBundleDir)();
|
|
1255
|
+
try {
|
|
1256
|
+
const portraitData = {
|
|
1257
|
+
lines: portraitLines,
|
|
1258
|
+
cols: 60,
|
|
1259
|
+
rows: portraitLines.length,
|
|
1260
|
+
format: "classic",
|
|
1261
|
+
sourceUrl: imageUrl,
|
|
1262
|
+
generatedAt: Date.now(),
|
|
1263
|
+
};
|
|
1264
|
+
fs.mkdirSync(bundleDir, { recursive: true });
|
|
1265
|
+
fs.writeFileSync(path.join(bundleDir, "portrait.json"), JSON.stringify(portraitData, null, 2));
|
|
1266
|
+
// Also save the source image URL as avatarUrl in config
|
|
1267
|
+
const config = (0, config_1.readGlobalConfig)();
|
|
1268
|
+
config.avatarUrl = imageUrl;
|
|
1269
|
+
(0, config_1.writeGlobalConfig)(config);
|
|
1270
|
+
}
|
|
1271
|
+
catch {
|
|
1272
|
+
// non-fatal — portrait save failed silently
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
else {
|
|
1276
|
+
portraitSpinner.stop("couldn't render — no worries, we'll try again later");
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
// ── Multi-select: coding agents and AI apps ───────────────────────
|
|
1280
|
+
const selectedAgents = await multiSelectPrompt(rl, "which coding agents do you use?", CODING_AGENTS);
|
|
1281
|
+
const selectedApps = await multiSelectPrompt(rl, "which AI apps do you regularly use?", AI_APPS);
|
|
938
1282
|
// ── Scrape social profiles ────────────────────────────────────────
|
|
939
1283
|
const twitterHandle = (twitter || "").replace(/^@/, "").trim();
|
|
940
1284
|
const githubHandle = (github || "").trim();
|
|
941
1285
|
let twitterData = null;
|
|
942
1286
|
let githubData = null;
|
|
943
1287
|
if (twitterHandle) {
|
|
944
|
-
const scrapeSpinner = new
|
|
1288
|
+
const scrapeSpinner = new render_1.BrailleSpinner(randomLabel("scrape"));
|
|
945
1289
|
scrapeSpinner.start();
|
|
946
1290
|
twitterData = await scrapeProfile(`https://x.com/${twitterHandle}`);
|
|
947
1291
|
scrapeSpinner.stop();
|
|
@@ -954,7 +1298,7 @@ async function runOnboarding() {
|
|
|
954
1298
|
}
|
|
955
1299
|
}
|
|
956
1300
|
if (githubHandle) {
|
|
957
|
-
const scrapeSpinner = new
|
|
1301
|
+
const scrapeSpinner = new render_1.BrailleSpinner(randomLabel("scrape"));
|
|
958
1302
|
scrapeSpinner.start();
|
|
959
1303
|
githubData = await scrapeProfile(`https://github.com/${githubHandle}`);
|
|
960
1304
|
scrapeSpinner.stop();
|
|
@@ -978,7 +1322,7 @@ async function runOnboarding() {
|
|
|
978
1322
|
links.push(linkedin);
|
|
979
1323
|
let researchData = null;
|
|
980
1324
|
if (name || twitterHandle || githubHandle) {
|
|
981
|
-
const researchSpinner = new
|
|
1325
|
+
const researchSpinner = new render_1.BrailleSpinner(randomLabel("research"));
|
|
982
1326
|
researchSpinner.start();
|
|
983
1327
|
researchData = await researchUser({
|
|
984
1328
|
name: name || username,
|
|
@@ -1016,8 +1360,16 @@ async function runOnboarding() {
|
|
|
1016
1360
|
const userApiKey = getOpenRouterKey();
|
|
1017
1361
|
console.log(chalk_1.default.dim(" cool. let's build your identity."));
|
|
1018
1362
|
console.log("");
|
|
1363
|
+
// Build agent context from multi-select
|
|
1364
|
+
const agentContext = [];
|
|
1365
|
+
if (selectedAgents.length > 0) {
|
|
1366
|
+
agentContext.push(`coding agents they use: ${selectedAgents.join(", ")}`);
|
|
1367
|
+
}
|
|
1368
|
+
if (selectedApps.length > 0) {
|
|
1369
|
+
agentContext.push(`AI apps they regularly use: ${selectedApps.join(", ")}`);
|
|
1370
|
+
}
|
|
1019
1371
|
try {
|
|
1020
|
-
await runAIMode(rl, basicInfo, userApiKey, { twitter: twitterData, github: githubData }, researchData);
|
|
1372
|
+
await runAIMode(rl, basicInfo, userApiKey, { twitter: twitterData, github: githubData }, researchData, agentContext.length > 0 ? agentContext : undefined);
|
|
1021
1373
|
}
|
|
1022
1374
|
catch {
|
|
1023
1375
|
console.log(chalk_1.default.dim(" switching to manual mode."));
|
|
@@ -1052,7 +1404,7 @@ async function createBundle(info) {
|
|
|
1052
1404
|
const result = (0, compiler_1.compileBundle)(bundleDir);
|
|
1053
1405
|
(0, compiler_1.writeBundle)(bundleDir, result);
|
|
1054
1406
|
console.log(" " +
|
|
1055
|
-
chalk_1.default.
|
|
1407
|
+
chalk_1.default.hex("#C46A3A")("done") +
|
|
1056
1408
|
chalk_1.default.dim(` -- bundle compiled (v${result.bundle.version})`));
|
|
1057
1409
|
console.log("");
|
|
1058
1410
|
showBundlePreview(bundleDir);
|