jobarbiter 0.3.1 → 0.3.3
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/lib/config.d.ts +2 -0
- package/dist/lib/detect-tools.d.ts +46 -0
- package/dist/lib/detect-tools.js +473 -0
- package/dist/lib/observe.d.ts +6 -2
- package/dist/lib/observe.js +111 -129
- package/dist/lib/onboard.js +211 -127
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/lib/config.ts +2 -0
- package/src/lib/detect-tools.ts +526 -0
- package/src/lib/observe.ts +116 -131
- package/src/lib/onboard.ts +229 -143
package/src/lib/onboard.ts
CHANGED
|
@@ -11,7 +11,15 @@
|
|
|
11
11
|
import * as readline from "node:readline";
|
|
12
12
|
import { loadConfig, saveConfig, getConfigPath, type Config } from "./config.js";
|
|
13
13
|
import { apiUnauthenticated, api, ApiError } from "./api.js";
|
|
14
|
-
import {
|
|
14
|
+
import { installObservers } from "./observe.js";
|
|
15
|
+
import {
|
|
16
|
+
detectAllTools,
|
|
17
|
+
getInstalledTools,
|
|
18
|
+
getToolsNeedingObserver,
|
|
19
|
+
formatToolDisplay,
|
|
20
|
+
type DetectedTool,
|
|
21
|
+
type ToolCategory,
|
|
22
|
+
} from "./detect-tools.js";
|
|
15
23
|
|
|
16
24
|
// ── ANSI Colors ────────────────────────────────────────────────────────
|
|
17
25
|
|
|
@@ -131,13 +139,44 @@ interface OnboardState {
|
|
|
131
139
|
export async function runOnboardWizard(opts: { force?: boolean; baseUrl?: string }): Promise<void> {
|
|
132
140
|
const baseUrl = opts.baseUrl || "https://jobarbiter-api-production.up.railway.app";
|
|
133
141
|
|
|
134
|
-
// Check for existing config
|
|
142
|
+
// Check for existing config — resume if onboarding incomplete
|
|
135
143
|
const existingConfig = loadConfig();
|
|
136
144
|
if (existingConfig && !opts.force) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
145
|
+
if (existingConfig.onboardingComplete) {
|
|
146
|
+
console.log(`\n${sym.check} ${c.success("You're already onboarded!")}`);
|
|
147
|
+
console.log(`\n Run ${c.highlight("jobarbiter status")} to check your account.`);
|
|
148
|
+
console.log(` Run ${c.highlight("jobarbiter onboard --force")} to start fresh.\n`);
|
|
149
|
+
process.exit(0);
|
|
150
|
+
}
|
|
151
|
+
// Onboarding incomplete — resume
|
|
152
|
+
const resumeStep = (existingConfig.onboardingStep ?? 1) + 1;
|
|
153
|
+
console.log(`\n${sym.rocket} ${c.bold("Resuming onboarding")} from step ${resumeStep}/6\n`);
|
|
154
|
+
console.log(c.dim(` Account: ${existingConfig.userType} | API key configured`));
|
|
155
|
+
console.log(c.dim(` Run ${c.highlight("jobarbiter onboard --force")} to start over.\n`));
|
|
156
|
+
|
|
157
|
+
const prompt = new Prompt();
|
|
158
|
+
const state: Partial<OnboardState> = {
|
|
159
|
+
baseUrl,
|
|
160
|
+
apiKey: existingConfig.apiKey,
|
|
161
|
+
userType: existingConfig.userType as "worker" | "employer",
|
|
162
|
+
userId: "",
|
|
163
|
+
email: "",
|
|
164
|
+
};
|
|
165
|
+
try {
|
|
166
|
+
if (existingConfig.userType === "worker" || existingConfig.userType === "seeker") {
|
|
167
|
+
await runWorkerFlow(prompt, state as OnboardState, resumeStep);
|
|
168
|
+
} else {
|
|
169
|
+
await runEmployerFlow(prompt, state as OnboardState);
|
|
170
|
+
}
|
|
171
|
+
prompt.close();
|
|
172
|
+
} catch (err) {
|
|
173
|
+
prompt.close();
|
|
174
|
+
if (err instanceof Error) {
|
|
175
|
+
console.log(`\n${sym.cross} ${c.error(err.message)}`);
|
|
176
|
+
}
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
141
180
|
}
|
|
142
181
|
|
|
143
182
|
const prompt = new Prompt();
|
|
@@ -155,11 +194,13 @@ export async function runOnboardWizard(opts: { force?: boolean; baseUrl?: string
|
|
|
155
194
|
state.apiKey = apiKey;
|
|
156
195
|
state.userId = userId;
|
|
157
196
|
|
|
158
|
-
// Save config immediately after verification
|
|
197
|
+
// Save config immediately after verification (with step progress)
|
|
159
198
|
saveConfig({
|
|
160
199
|
apiKey,
|
|
161
200
|
baseUrl,
|
|
162
201
|
userType,
|
|
202
|
+
onboardingStep: 1,
|
|
203
|
+
onboardingComplete: false,
|
|
163
204
|
});
|
|
164
205
|
|
|
165
206
|
if (userType === "worker") {
|
|
@@ -216,7 +257,9 @@ async function handleEmailVerification(
|
|
|
216
257
|
baseUrl: string,
|
|
217
258
|
userType: "worker" | "employer"
|
|
218
259
|
): Promise<{ email: string; apiKey: string; userId: string }> {
|
|
219
|
-
|
|
260
|
+
// Workers: 1) Account, 2) Tool Detection, 3) Domains, 4) GitHub, 5) LinkedIn, 6) Done
|
|
261
|
+
// Employers: 1) Account, 2) (skip verification), 3) Company, 4) Domain, 5) What You Need, 6) Done
|
|
262
|
+
const totalSteps = 6;
|
|
220
263
|
|
|
221
264
|
console.log(`\n${sym.email} ${c.bold(`Step 1/${totalSteps} — Create Your Account`)}\n`);
|
|
222
265
|
|
|
@@ -296,189 +339,232 @@ async function handleEmailVerification(
|
|
|
296
339
|
|
|
297
340
|
// ── Worker Flow ────────────────────────────────────────────────────────
|
|
298
341
|
|
|
299
|
-
async function runWorkerFlow(prompt: Prompt, state: OnboardState): Promise<void> {
|
|
342
|
+
async function runWorkerFlow(prompt: Prompt, state: OnboardState, startStep = 2): Promise<void> {
|
|
300
343
|
const config: Config = {
|
|
301
344
|
apiKey: state.apiKey,
|
|
302
345
|
baseUrl: state.baseUrl,
|
|
303
346
|
userType: "worker",
|
|
304
347
|
};
|
|
305
348
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
console.log(`What AI tools do you use? ${c.dim("(comma-separated)")}`);
|
|
310
|
-
console.log(c.dim("Examples: Claude Code, Cursor, OpenClaw, ChatGPT, Copilot, Midjourney\n"));
|
|
311
|
-
const toolsInput = await prompt.question(`${sym.arrow} `);
|
|
312
|
-
const tools = toolsInput.split(",").map(s => s.trim()).filter(Boolean);
|
|
313
|
-
state.tools = tools;
|
|
314
|
-
|
|
315
|
-
console.log(`\nWhat domains do you work in? ${c.dim("(comma-separated)")}`);
|
|
316
|
-
console.log(c.dim("Examples: full-stack dev, data engineering, trading, content creation\n"));
|
|
317
|
-
const domainsInput = await prompt.question(`${sym.arrow} `);
|
|
318
|
-
const domains = domainsInput.split(",").map(s => s.trim()).filter(Boolean);
|
|
319
|
-
state.domains = domains;
|
|
320
|
-
|
|
321
|
-
// Create/update profile
|
|
322
|
-
console.log(c.dim("\nSaving profile..."));
|
|
349
|
+
const saveProgress = (step: number) => {
|
|
350
|
+
saveConfig({ ...config, onboardingStep: step });
|
|
351
|
+
};
|
|
323
352
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
},
|
|
330
|
-
});
|
|
331
|
-
console.log(`${sym.check} Profile saved\n`);
|
|
332
|
-
} catch (err) {
|
|
333
|
-
console.log(`${sym.warning} ${c.warning("Could not save profile details — you can update later with 'jobarbiter profile create'")}\n`);
|
|
353
|
+
// Step 2: Auto-detect AI Tools
|
|
354
|
+
if (startStep <= 2) {
|
|
355
|
+
const detectedToolsResult = await runToolDetectionStep(prompt, config);
|
|
356
|
+
state.tools = detectedToolsResult.tools;
|
|
357
|
+
saveProgress(2);
|
|
334
358
|
}
|
|
335
359
|
|
|
336
|
-
// Step 3:
|
|
337
|
-
|
|
360
|
+
// Step 3: Domains
|
|
361
|
+
if (startStep <= 3) {
|
|
362
|
+
console.log(`${sym.target} ${c.bold("Step 3/6 — Your Domains")}\n`);
|
|
363
|
+
console.log(`What domains do you work in? ${c.dim("(comma-separated)")}`);
|
|
364
|
+
console.log(c.dim("Examples: full-stack dev, data engineering, trading, content creation\n"));
|
|
365
|
+
const domainsInput = await prompt.question(`${sym.arrow} `);
|
|
366
|
+
const domains = domainsInput.split(",").map(s => s.trim()).filter(Boolean);
|
|
367
|
+
state.domains = domains;
|
|
338
368
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
console.log(`Connecting your GitHub lets us analyze your AI-assisted work patterns.`);
|
|
342
|
-
console.log(`This significantly boosts your proficiency score.\n`);
|
|
369
|
+
// Create/update profile
|
|
370
|
+
console.log(c.dim("\nSaving profile..."));
|
|
343
371
|
|
|
344
|
-
const githubUsername = await prompt.question(`GitHub username ${c.dim("(press Enter to skip)")}: `);
|
|
345
|
-
|
|
346
|
-
if (githubUsername) {
|
|
347
|
-
console.log(c.dim("\nConnecting GitHub..."));
|
|
348
372
|
try {
|
|
349
|
-
await api(config, "POST", "/v1/
|
|
350
|
-
|
|
351
|
-
|
|
373
|
+
await api(config, "POST", "/v1/profile", {
|
|
374
|
+
domains,
|
|
375
|
+
tools: {
|
|
376
|
+
primary: state.tools,
|
|
377
|
+
},
|
|
352
378
|
});
|
|
353
|
-
console.log(`${sym.check}
|
|
354
|
-
state.githubUsername = githubUsername;
|
|
379
|
+
console.log(`${sym.check} Profile saved\n`);
|
|
355
380
|
} catch (err) {
|
|
356
|
-
console.log(`${sym.warning} ${c.warning("Could not
|
|
381
|
+
console.log(`${sym.warning} ${c.warning("Could not save profile details — you can update later with 'jobarbiter profile create'")}\n`);
|
|
357
382
|
}
|
|
358
|
-
|
|
359
|
-
|
|
383
|
+
saveProgress(3);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Step 4: Connect GitHub (optional)
|
|
387
|
+
if (startStep <= 4) {
|
|
388
|
+
console.log(`${sym.link} ${c.bold("Step 4/6 — Connect GitHub")} ${c.dim("(optional)")}\n`);
|
|
389
|
+
console.log(`Connecting your GitHub lets us analyze your AI-assisted work patterns.`);
|
|
390
|
+
console.log(`This significantly boosts your proficiency score.\n`);
|
|
391
|
+
|
|
392
|
+
const githubUsername = await prompt.question(`GitHub username ${c.dim("(press Enter to skip)")}: `);
|
|
393
|
+
|
|
394
|
+
if (githubUsername) {
|
|
395
|
+
console.log(c.dim("\nConnecting GitHub..."));
|
|
396
|
+
try {
|
|
397
|
+
await api(config, "POST", "/v1/attestations/git/connect", {
|
|
398
|
+
provider: "github",
|
|
399
|
+
username: githubUsername,
|
|
400
|
+
});
|
|
401
|
+
console.log(`${sym.check} GitHub connected: ${c.highlight(githubUsername)}\n`);
|
|
402
|
+
state.githubUsername = githubUsername;
|
|
403
|
+
} catch (err) {
|
|
404
|
+
console.log(`${sym.warning} ${c.warning("Could not connect GitHub — you can try later with 'jobarbiter git connect'")}\n`);
|
|
405
|
+
}
|
|
406
|
+
} else {
|
|
407
|
+
console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter git connect'")}\n`);
|
|
408
|
+
}
|
|
409
|
+
saveProgress(4);
|
|
360
410
|
}
|
|
361
411
|
|
|
362
412
|
// Step 5: Connect LinkedIn (optional)
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
413
|
+
if (startStep <= 5) {
|
|
414
|
+
console.log(`${sym.link} ${c.bold("Step 5/6 — Connect LinkedIn")} ${c.dim("(optional)")}\n`);
|
|
415
|
+
console.log(`Your LinkedIn profile strengthens identity verification.`);
|
|
416
|
+
console.log(c.dim("We never post on your behalf or access your connections.\n"));
|
|
366
417
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
418
|
+
const linkedinUrl = await prompt.question(`LinkedIn URL ${c.dim("(press Enter to skip)")}: `);
|
|
419
|
+
|
|
420
|
+
if (linkedinUrl) {
|
|
421
|
+
console.log(c.dim("\nSubmitting for verification..."));
|
|
422
|
+
try {
|
|
423
|
+
await api(config, "POST", "/v1/verification/linkedin", {
|
|
424
|
+
linkedinUrl: linkedinUrl.trim(),
|
|
425
|
+
});
|
|
426
|
+
console.log(`${sym.check} LinkedIn submitted for verification\n`);
|
|
427
|
+
} catch (err) {
|
|
428
|
+
console.log(`${sym.warning} ${c.warning("Could not submit LinkedIn — you can try later with 'jobarbiter identity linkedin <url>'")}\n`);
|
|
429
|
+
}
|
|
430
|
+
} else {
|
|
431
|
+
console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter identity linkedin <url>'")}\n`);
|
|
378
432
|
}
|
|
379
|
-
|
|
380
|
-
console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter identity linkedin <url>'")}\n`);
|
|
433
|
+
saveProgress(5);
|
|
381
434
|
}
|
|
382
435
|
|
|
383
436
|
// Step 6: Done!
|
|
437
|
+
saveConfig({ ...config, onboardingComplete: true, onboardingStep: 6 });
|
|
384
438
|
showWorkerCompletion(state);
|
|
385
439
|
}
|
|
386
440
|
|
|
387
|
-
// ──
|
|
441
|
+
// ── Tool Detection Step ────────────────────────────────────────────────
|
|
388
442
|
|
|
389
|
-
async function
|
|
443
|
+
async function runToolDetectionStep(
|
|
390
444
|
prompt: Prompt,
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
445
|
+
config: Config,
|
|
446
|
+
): Promise<{ tools: string[] }> {
|
|
447
|
+
console.log(`🔍 ${c.bold("Step 2/6 — Detecting AI Tools")}\n`);
|
|
448
|
+
console.log(c.dim(" Scanning your machine...\n"));
|
|
449
|
+
|
|
450
|
+
const allTools = detectAllTools();
|
|
451
|
+
const installed = allTools.filter((t) => t.installed);
|
|
452
|
+
const notInstalled = allTools.filter((t) => !t.installed && t.category === "coding-agent");
|
|
453
|
+
|
|
454
|
+
// Group by category
|
|
455
|
+
const codingAgents = installed.filter((t) => t.category === "coding-agent");
|
|
456
|
+
const chatTools = installed.filter((t) => t.category === "chat");
|
|
457
|
+
const orchestration = installed.filter((t) => t.category === "orchestration");
|
|
458
|
+
const apiProviders = installed.filter((t) => t.category === "api-provider");
|
|
459
|
+
|
|
460
|
+
// Display found tools
|
|
461
|
+
if (installed.length === 0) {
|
|
462
|
+
console.log(` ${c.dim("No AI tools detected on this system.")}\n`);
|
|
463
|
+
console.log(c.dim(" You can add tools later with 'jobarbiter observe install'.\n"));
|
|
464
|
+
return { tools: [] };
|
|
407
465
|
}
|
|
408
466
|
|
|
409
|
-
console.log(` Found
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
467
|
+
console.log(` ${c.bold("Found:")}`);
|
|
468
|
+
|
|
469
|
+
// Show coding agents with observer status
|
|
470
|
+
for (const tool of codingAgents) {
|
|
471
|
+
const display = formatToolDisplay(tool);
|
|
472
|
+
if (tool.observerAvailable) {
|
|
473
|
+
if (tool.observerActive) {
|
|
474
|
+
console.log(` ${sym.check} ${display} ${c.dim("(observer active)")}`);
|
|
475
|
+
} else {
|
|
476
|
+
console.log(` ${sym.check} ${display} ${c.success("(observer available)")}`);
|
|
477
|
+
}
|
|
413
478
|
} else {
|
|
414
|
-
console.log(` ${c.
|
|
479
|
+
console.log(` ${sym.check} ${display} ${c.dim("(detected)")}`);
|
|
415
480
|
}
|
|
416
481
|
}
|
|
417
|
-
|
|
418
|
-
|
|
482
|
+
|
|
483
|
+
// Show other tools
|
|
484
|
+
for (const tool of chatTools) {
|
|
485
|
+
console.log(` ${sym.check} ${formatToolDisplay(tool)} ${c.dim("(detected)")}`);
|
|
486
|
+
}
|
|
487
|
+
for (const tool of orchestration) {
|
|
488
|
+
console.log(` ${sym.check} ${formatToolDisplay(tool)} ${c.dim("(detected)")}`);
|
|
489
|
+
}
|
|
490
|
+
for (const tool of apiProviders) {
|
|
491
|
+
console.log(` ${sym.check} ${tool.name} ${c.dim("configured")}`);
|
|
419
492
|
}
|
|
420
493
|
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
494
|
+
// Show not-detected coding agents
|
|
495
|
+
if (notInstalled.length > 0) {
|
|
496
|
+
console.log(`\n ${c.dim("Not detected (install to track):")}`);
|
|
497
|
+
for (const tool of notInstalled.slice(0, 5)) {
|
|
498
|
+
console.log(` ${c.dim("⬚")} ${tool.name}`);
|
|
499
|
+
}
|
|
500
|
+
if (notInstalled.length > 5) {
|
|
501
|
+
console.log(` ${c.dim(`... and ${notInstalled.length - 5} more`)}`);
|
|
502
|
+
}
|
|
427
503
|
}
|
|
428
504
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
console.log(` only aggregate scores (tool usage, session counts, token volume).\n`);
|
|
432
|
-
console.log(c.dim(` Data stored locally: ~/.config/jobarbiter/observer/observations.json`));
|
|
433
|
-
console.log(c.dim(` Review anytime: jobarbiter observe status\n`));
|
|
505
|
+
// Collect tool names for profile
|
|
506
|
+
const toolNames = installed.map((t) => t.name);
|
|
434
507
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
);
|
|
508
|
+
// Observer installation for coding agents
|
|
509
|
+
const needsObserver = codingAgents.filter((t) => t.observerAvailable && !t.observerActive);
|
|
438
510
|
|
|
439
|
-
|
|
511
|
+
if (needsObserver.length > 0) {
|
|
512
|
+
console.log(`\n ${c.bold("Observers")}`);
|
|
513
|
+
console.log(` JobArbiter observes your coding sessions to build your`);
|
|
514
|
+
console.log(` proficiency profile. ${c.bold("No code or prompts leave your machine")} —`);
|
|
515
|
+
console.log(` only aggregate scores (tool usage, session counts, token volume).\n`);
|
|
516
|
+
console.log(c.dim(` Data stored locally: ~/.config/jobarbiter/observer/observations.json`));
|
|
517
|
+
console.log(c.dim(` Review anytime: jobarbiter observe status\n`));
|
|
440
518
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
console.log(`\n Select which agents to observe:\n`);
|
|
446
|
-
const selections: Record<string, boolean> = {};
|
|
447
|
-
|
|
448
|
-
for (const agent of needsInstall) {
|
|
449
|
-
selections[agent.id] = await prompt.confirm(` ${agent.name}?`, true);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
toInstall = Object.entries(selections)
|
|
453
|
-
.filter(([, v]) => v)
|
|
454
|
-
.map(([k]) => k);
|
|
519
|
+
const observerNames = needsObserver.map((t) => t.name).join(", ");
|
|
520
|
+
const installAll = await prompt.confirm(
|
|
521
|
+
` Install observers for detected tools? (${observerNames})`,
|
|
522
|
+
);
|
|
455
523
|
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
}
|
|
524
|
+
if (installAll) {
|
|
525
|
+
const toInstall = needsObserver.map((t) => t.id);
|
|
526
|
+
console.log(c.dim("\n Installing observers..."));
|
|
527
|
+
const result = installObservers(toInstall);
|
|
461
528
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
529
|
+
for (const name of result.installed) {
|
|
530
|
+
console.log(` ${sym.check} ${name}`);
|
|
531
|
+
}
|
|
532
|
+
for (const name of result.skipped) {
|
|
533
|
+
console.log(` ${c.dim("—")} ${name} ${c.dim("(already installed)")}`);
|
|
534
|
+
}
|
|
535
|
+
for (const { agent, error: errMsg } of result.errors) {
|
|
536
|
+
console.log(` ${sym.cross} ${agent}: ${c.error(errMsg)}`);
|
|
537
|
+
}
|
|
465
538
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
539
|
+
if (result.installed.length > 0) {
|
|
540
|
+
console.log(`\n ${sym.check} ${c.success(`${result.installed.length} observer${result.installed.length > 1 ? "s" : ""} installed!`)}`);
|
|
541
|
+
console.log(c.dim(` Your proficiency profile will start building automatically.\n`));
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
console.log(c.dim("\n Skipped — you can install observers later with 'jobarbiter observe install'.\n"));
|
|
545
|
+
}
|
|
546
|
+
} else if (codingAgents.length > 0) {
|
|
547
|
+
const hasActiveObservers = codingAgents.some((t) => t.observerActive);
|
|
548
|
+
if (hasActiveObservers) {
|
|
549
|
+
console.log(`\n ${c.dim("All detected agents already have observers installed.")}\n`);
|
|
550
|
+
} else {
|
|
551
|
+
console.log();
|
|
552
|
+
}
|
|
474
553
|
}
|
|
475
554
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
555
|
+
// "Did we miss anything?" prompt
|
|
556
|
+
console.log(` ${c.dim("Did we miss anything?")}`);
|
|
557
|
+
const additionalTools = await prompt.question(` Other AI tools you use ${c.dim("(comma-separated, or press Enter)")}: `);
|
|
558
|
+
|
|
559
|
+
if (additionalTools.trim()) {
|
|
560
|
+
const additional = additionalTools.split(",").map((s) => s.trim()).filter(Boolean);
|
|
561
|
+
toolNames.push(...additional);
|
|
562
|
+
console.log(` ${sym.check} Added: ${additional.join(", ")}\n`);
|
|
479
563
|
} else {
|
|
480
564
|
console.log();
|
|
481
565
|
}
|
|
566
|
+
|
|
567
|
+
return { tools: toolNames };
|
|
482
568
|
}
|
|
483
569
|
|
|
484
570
|
function showWorkerCompletion(state: OnboardState): void {
|