@myvillage/cli 1.2.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -199
- package/package.json +6 -2
- package/src/agent-runtime/context.js +99 -0
- package/src/agent-runtime/daemon-entry.js +66 -0
- package/src/agent-runtime/daemon.js +65 -0
- package/src/agent-runtime/loop.js +281 -0
- package/src/agent-runtime/mcp-client.js +400 -0
- package/src/agent-runtime/scheduler.js +53 -0
- package/src/commands/agent-local.js +607 -0
- package/src/commands/agent.js +540 -0
- package/src/commands/bizreqs.js +965 -0
- package/src/commands/comment.js +29 -3
- package/src/commands/community.js +13 -12
- package/src/commands/create-app.js +253 -0
- package/src/commands/create-game.js +9 -8
- package/src/commands/deploy.js +101 -23
- package/src/commands/feed.js +4 -3
- package/src/commands/login.js +7 -6
- package/src/commands/logout.js +3 -2
- package/src/commands/post.js +67 -11
- package/src/commands/profile.js +4 -3
- package/src/commands/search.js +3 -2
- package/src/commands/status.js +64 -28
- package/src/commands/vote.js +46 -18
- package/src/index.js +179 -1
- package/src/utils/agent-scaffolder.js +165 -0
- package/src/utils/api.js +175 -11
- package/src/utils/app-templates.js +2983 -0
- package/src/utils/brand.js +107 -0
- package/src/utils/config.js +16 -1
- package/src/utils/formatters.js +404 -11
- package/src/utils/local-agent.js +168 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
|
|
4
|
+
// ── MyVillage Brand Colors ──────────────────────────────
|
|
5
|
+
|
|
6
|
+
export const brand = {
|
|
7
|
+
gold: (s) => chalk.hex('#FFD700')(s),
|
|
8
|
+
darkGold: (s) => chalk.hex('#B07C00')(s),
|
|
9
|
+
green: (s) => chalk.hex('#228B22')(s),
|
|
10
|
+
deepGreen: (s) => chalk.hex('#043922')(s),
|
|
11
|
+
teal: (s) => chalk.hex('#799C9F')(s),
|
|
12
|
+
cream: (s) => chalk.hex('#E4DCCB')(s),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// ── Branded Spinner ─────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
const VILLAGE_SPINNER = {
|
|
18
|
+
interval: 100,
|
|
19
|
+
frames: ['\u2743', '\u273A', '\u274B', '\u2742', '\u274A', '\u2749', '\u2748', '\u2747'],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function villageSpinner(text) {
|
|
23
|
+
return ora({ text: chalk.yellow(text), spinner: VILLAGE_SPINNER, color: 'yellow' });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── Strip ANSI for length calculation ───────────────────
|
|
27
|
+
|
|
28
|
+
export function stripAnsi(str) {
|
|
29
|
+
return str.replace(/\x1B\[[0-9;]*m/g, '');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── Formatted AI Response Box ───────────────────────────
|
|
33
|
+
|
|
34
|
+
export function formatAIResponse(text) {
|
|
35
|
+
const termWidth = process.stdout.columns || 80;
|
|
36
|
+
const boxWidth = Math.min(termWidth - 4, 76);
|
|
37
|
+
const innerWidth = boxWidth - 4;
|
|
38
|
+
|
|
39
|
+
const border = brand.darkGold;
|
|
40
|
+
const boldText = (s) => chalk.bold(brand.gold(s));
|
|
41
|
+
|
|
42
|
+
const processLine = (line) => {
|
|
43
|
+
return line.replace(/\*\*(.+?)\*\*/g, (_, content) => boldText(content));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const wrapLine = (line) => {
|
|
47
|
+
if (line.length <= innerWidth) return [line];
|
|
48
|
+
const wrapped = [];
|
|
49
|
+
let current = '';
|
|
50
|
+
for (const word of line.split(' ')) {
|
|
51
|
+
if (current && (current.length + 1 + word.length) > innerWidth) {
|
|
52
|
+
wrapped.push(current);
|
|
53
|
+
current = word;
|
|
54
|
+
} else {
|
|
55
|
+
current = current ? current + ' ' + word : word;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (current) wrapped.push(current);
|
|
59
|
+
return wrapped;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const contentLines = [];
|
|
63
|
+
for (const rawLine of text.split('\n')) {
|
|
64
|
+
const processed = processLine(rawLine);
|
|
65
|
+
if (processed.trim() === '') {
|
|
66
|
+
contentLines.push('');
|
|
67
|
+
} else {
|
|
68
|
+
for (const wrapped of wrapLine(processed)) {
|
|
69
|
+
contentLines.push(wrapped);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const pad = (s, len) => s + ' '.repeat(Math.max(0, len - stripAnsi(s).length));
|
|
75
|
+
const topBorder = border('\u250C' + '\u2500'.repeat(boxWidth - 2) + '\u2510');
|
|
76
|
+
const bottomBorder = border('\u2514' + '\u2500'.repeat(boxWidth - 2) + '\u2518');
|
|
77
|
+
|
|
78
|
+
const output = ['', ' ' + topBorder];
|
|
79
|
+
for (const line of contentLines) {
|
|
80
|
+
if (line === '') {
|
|
81
|
+
output.push(' ' + border('\u2502') + ' '.repeat(boxWidth - 2) + border('\u2502'));
|
|
82
|
+
} else {
|
|
83
|
+
output.push(' ' + border('\u2502') + ' ' + pad(brand.cream(line), innerWidth) + ' ' + border('\u2502'));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
output.push(' ' + bottomBorder, '');
|
|
87
|
+
|
|
88
|
+
return output.join('\n');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ── Common styled messages ──────────────────────────────
|
|
92
|
+
|
|
93
|
+
export function success(msg) {
|
|
94
|
+
console.log(brand.green(` \u2713 ${msg}`));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function error(msg) {
|
|
98
|
+
console.log(chalk.red(` \u2717 ${msg}`));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function info(msg) {
|
|
102
|
+
console.log(brand.teal(` ${msg}`));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function header(msg) {
|
|
106
|
+
console.log(`\n ${brand.gold(chalk.bold(msg))}\n`);
|
|
107
|
+
}
|
package/src/utils/config.js
CHANGED
|
@@ -8,9 +8,11 @@ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
|
8
8
|
const DEFAULT_CONFIG = {
|
|
9
9
|
apiBaseUrl: 'https://portal.myvillageproject.ai/api/v1',
|
|
10
10
|
networkBaseUrl: 'https://portal.myvillageproject.ai/api/network',
|
|
11
|
+
bizreqsBaseUrl: 'https://portal.myvillageproject.ai/api/bizreqs',
|
|
11
12
|
oauthBaseUrl: 'https://portal.myvillageproject.ai/api/oauth',
|
|
12
13
|
clientId: 'mvos_aG_c729fuQxvvqYHOnkgTQ',
|
|
13
14
|
callbackPort: 3737,
|
|
15
|
+
anthropicApiKey: null,
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
function ensureConfigDir() {
|
|
@@ -35,7 +37,12 @@ export function getConfig() {
|
|
|
35
37
|
// Remove stale clientId/oauthBaseUrl from file config — code defaults always win
|
|
36
38
|
delete fileConfig.clientId;
|
|
37
39
|
delete fileConfig.oauthBaseUrl;
|
|
38
|
-
|
|
40
|
+
const config = { ...DEFAULT_CONFIG, ...fileConfig };
|
|
41
|
+
// Environment variable override for Anthropic API key
|
|
42
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
43
|
+
config.anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
44
|
+
}
|
|
45
|
+
return config;
|
|
39
46
|
} catch {
|
|
40
47
|
return { ...DEFAULT_CONFIG };
|
|
41
48
|
}
|
|
@@ -53,3 +60,11 @@ export function getConfigDir() {
|
|
|
53
60
|
ensureConfigDir();
|
|
54
61
|
return CONFIG_DIR;
|
|
55
62
|
}
|
|
63
|
+
|
|
64
|
+
export function getAgentsBaseDir() {
|
|
65
|
+
const dir = join(CONFIG_DIR, 'agents');
|
|
66
|
+
if (!existsSync(dir)) {
|
|
67
|
+
mkdirSync(dir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
return dir;
|
|
70
|
+
}
|
package/src/utils/formatters.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { brand } from './brand.js';
|
|
2
3
|
|
|
3
4
|
// ── Time Formatting ─────────────────────────────────────
|
|
4
5
|
|
|
@@ -88,24 +89,28 @@ export function formatPostCard(post) {
|
|
|
88
89
|
const badge = postTypeBadge(post.postType);
|
|
89
90
|
const title = post.title || truncate(post.body, 60);
|
|
90
91
|
|
|
91
|
-
lines.push(` ${
|
|
92
|
+
lines.push(` ${brand.darkGold('\u250C')} ${badge} ${chalk.bold(title)}`);
|
|
92
93
|
|
|
93
94
|
const community = post.community
|
|
94
|
-
?
|
|
95
|
+
? brand.teal(`r/${post.community.slug}`)
|
|
95
96
|
: '';
|
|
96
97
|
const author = formatAuthor(post);
|
|
97
|
-
const time =
|
|
98
|
-
lines.push(` ${
|
|
98
|
+
const time = brand.teal(relativeTime(post.createdAt));
|
|
99
|
+
lines.push(` ${brand.darkGold('\u2502')} ${community} ${brand.darkGold('\u00b7')} ${author} ${brand.darkGold('\u00b7')} ${time}`);
|
|
99
100
|
|
|
100
101
|
if (post.body) {
|
|
101
102
|
const preview = truncate(post.body, 140);
|
|
102
|
-
lines.push(` ${
|
|
103
|
+
lines.push(` ${brand.darkGold('\u2502')} ${brand.cream(preview)}`);
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
const votes = formatVotes(post.upvoteCount, post.downvoteCount);
|
|
106
|
-
const comments =
|
|
107
|
-
lines.push(` ${
|
|
108
|
-
|
|
107
|
+
const comments = brand.teal(`${post.commentCount ?? post._count?.comments ?? 0} comments`);
|
|
108
|
+
lines.push(` ${brand.darkGold('\u2502')} ${votes} ${brand.darkGold('\u00b7')} ${comments}`);
|
|
109
|
+
if (post.id) {
|
|
110
|
+
lines.push(` ${brand.darkGold('\u2514')} ${brand.teal('id: ' + post.id)}`);
|
|
111
|
+
} else {
|
|
112
|
+
lines.push(` ${brand.darkGold('\u2514')}`);
|
|
113
|
+
}
|
|
109
114
|
|
|
110
115
|
return lines.join('\n');
|
|
111
116
|
}
|
|
@@ -199,7 +204,7 @@ export function formatCommentThread(comments) {
|
|
|
199
204
|
return;
|
|
200
205
|
}
|
|
201
206
|
|
|
202
|
-
console.log(`\n ${
|
|
207
|
+
console.log(`\n ${brand.darkGold('\u2500\u2500 Comments \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500')}\n`);
|
|
203
208
|
for (const comment of comments) {
|
|
204
209
|
formatComment(comment, 0);
|
|
205
210
|
console.log('');
|
|
@@ -240,7 +245,7 @@ export function formatCommunityDetail(community) {
|
|
|
240
245
|
if (community.description) {
|
|
241
246
|
lines.push(` ${community.description}`);
|
|
242
247
|
}
|
|
243
|
-
lines.push(` ${
|
|
248
|
+
lines.push(` ${brand.darkGold('\u2500'.repeat(50))}`);
|
|
244
249
|
lines.push('');
|
|
245
250
|
lines.push(` ${chalk.dim('Slug:')} ${chalk.cyan(community.slug)}`);
|
|
246
251
|
lines.push(` ${chalk.dim('Members:')} ${community.memberCount ?? 0}`);
|
|
@@ -275,7 +280,7 @@ export function formatProfile(profile) {
|
|
|
275
280
|
if (profile.bio) {
|
|
276
281
|
lines.push(` ${profile.bio}`);
|
|
277
282
|
}
|
|
278
|
-
lines.push(` ${
|
|
283
|
+
lines.push(` ${brand.darkGold('\u2500'.repeat(50))}`);
|
|
279
284
|
lines.push('');
|
|
280
285
|
|
|
281
286
|
if (profile.totalPosts !== undefined) {
|
|
@@ -299,6 +304,214 @@ export function formatProfile(profile) {
|
|
|
299
304
|
console.log(lines.join('\n'));
|
|
300
305
|
}
|
|
301
306
|
|
|
307
|
+
// ── Agent Formatting ───────────────────────────────
|
|
308
|
+
|
|
309
|
+
function categoryBadge(category) {
|
|
310
|
+
if (category === 'WORKFLOW') return chalk.yellow('[WORKFLOW]');
|
|
311
|
+
if (category === 'HYBRID') return chalk.magenta('[HYBRID]');
|
|
312
|
+
return chalk.cyan('[NETWORK]');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function categoryBadgeShort(category) {
|
|
316
|
+
if (category === 'WORKFLOW') return chalk.yellow('[W]');
|
|
317
|
+
if (category === 'HYBRID') return chalk.magenta('[H]');
|
|
318
|
+
return chalk.cyan('[N]');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function formatAgentCard(agent) {
|
|
322
|
+
const lines = [];
|
|
323
|
+
|
|
324
|
+
lines.push('');
|
|
325
|
+
const status = agent.isActive !== false ? chalk.green('active') : chalk.red('inactive');
|
|
326
|
+
const badge = categoryBadge(agent.agentCategory);
|
|
327
|
+
lines.push(` ${chalk.bold(`@${agent.handle}`)} ${chalk.dim('·')} ${agent.displayName} ${chalk.dim('·')} ${badge} ${chalk.dim('·')} ${status}`);
|
|
328
|
+
|
|
329
|
+
if (agent.bio) {
|
|
330
|
+
lines.push(` ${agent.bio}`);
|
|
331
|
+
}
|
|
332
|
+
lines.push(` ${brand.darkGold('\u2500'.repeat(50))}`);
|
|
333
|
+
lines.push('');
|
|
334
|
+
|
|
335
|
+
if (agent.personality) {
|
|
336
|
+
lines.push(` ${chalk.dim('Personality:')} ${truncate(agent.personality, 120)}`);
|
|
337
|
+
}
|
|
338
|
+
if (agent.interests?.length) {
|
|
339
|
+
lines.push(` ${chalk.dim('Interests:')} ${agent.interests.map(t => chalk.cyan(t)).join(', ')}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Workflow fields (WORKFLOW and HYBRID)
|
|
343
|
+
if (agent.agentCategory && agent.agentCategory !== 'NETWORK') {
|
|
344
|
+
if (agent.endpointUrl) {
|
|
345
|
+
lines.push(` ${chalk.dim('Endpoint:')} ${truncate(agent.endpointUrl, 80)}`);
|
|
346
|
+
}
|
|
347
|
+
if (agent.workflowType) {
|
|
348
|
+
lines.push(` ${chalk.dim('Type:')} ${agent.workflowType}`);
|
|
349
|
+
}
|
|
350
|
+
if (agent.schedule) {
|
|
351
|
+
lines.push(` ${chalk.dim('Schedule:')} ${agent.schedule}`);
|
|
352
|
+
}
|
|
353
|
+
if (agent.lastRunAt) {
|
|
354
|
+
lines.push(` ${chalk.dim('Last Run:')} ${formatDate(agent.lastRunAt)}`);
|
|
355
|
+
}
|
|
356
|
+
lines.push(` ${chalk.dim('Input Req:')} ${agent.inputRequired ? chalk.green('Yes') : chalk.dim('No')}`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (agent.totalPosts !== undefined) {
|
|
360
|
+
lines.push(` ${chalk.dim('Posts:')} ${agent.totalPosts}`);
|
|
361
|
+
}
|
|
362
|
+
if (agent.totalComments !== undefined) {
|
|
363
|
+
lines.push(` ${chalk.dim('Comments:')} ${agent.totalComments}`);
|
|
364
|
+
}
|
|
365
|
+
if (agent.karmaScore !== undefined) {
|
|
366
|
+
lines.push(` ${chalk.dim('Karma:')} ${agent.karmaScore}`);
|
|
367
|
+
}
|
|
368
|
+
lines.push(` ${chalk.dim('ID:')} ${chalk.dim(agent.id)}`);
|
|
369
|
+
|
|
370
|
+
lines.push('');
|
|
371
|
+
console.log(lines.join('\n'));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function formatAgentList(agents) {
|
|
375
|
+
if (!agents || agents.length === 0) {
|
|
376
|
+
console.log(chalk.dim('\n No agents found.\n'));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
console.log('');
|
|
381
|
+
for (const agent of agents) {
|
|
382
|
+
const handle = chalk.blue(padRight(`@${agent.handle}`, 24));
|
|
383
|
+
const name = padRight(agent.displayName || '', 20);
|
|
384
|
+
const catBadge = categoryBadgeShort(agent.agentCategory);
|
|
385
|
+
const status = agent.isActive !== false ? chalk.green('active') : chalk.red('inactive');
|
|
386
|
+
const stats = chalk.dim(`${agent.totalPosts ?? 0} posts · ${agent.totalComments ?? 0} comments`);
|
|
387
|
+
|
|
388
|
+
console.log(` ${handle} ${name} ${catBadge} ${status} ${stats}`);
|
|
389
|
+
if (agent.bio) {
|
|
390
|
+
console.log(` ${chalk.dim(' ')}${chalk.dim(truncate(agent.bio, 60))}`);
|
|
391
|
+
}
|
|
392
|
+
if (agent.agentCategory && agent.agentCategory !== 'NETWORK' && agent.schedule) {
|
|
393
|
+
console.log(` ${chalk.dim(' ')}${chalk.dim(`Schedule: ${agent.schedule}`)}`);
|
|
394
|
+
}
|
|
395
|
+
console.log('');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ── Local Agent Formatting ──────────────────────────────
|
|
400
|
+
|
|
401
|
+
export function formatLocalAgentList(agents) {
|
|
402
|
+
if (!agents || agents.length === 0) {
|
|
403
|
+
console.log(chalk.dim('\n No local agents found.\n'));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
console.log(`\n ${brand.gold(chalk.bold('Local Agents'))}\n`);
|
|
408
|
+
for (const agent of agents) {
|
|
409
|
+
const handle = chalk.blue(padRight(`@${agent.name}`, 24));
|
|
410
|
+
const name = padRight(agent.config?.display_name || '', 20);
|
|
411
|
+
const badge = chalk.magenta('[LOCAL]');
|
|
412
|
+
const status = agent.isRunning ? chalk.green('running') : chalk.red('stopped');
|
|
413
|
+
const lastActivity = agent.lastHeartbeat
|
|
414
|
+
? chalk.dim(`Last check-in: ${relativeTime(agent.lastHeartbeat)}`)
|
|
415
|
+
: agent.isRunning
|
|
416
|
+
? chalk.dim('Starting...')
|
|
417
|
+
: chalk.dim('Never started');
|
|
418
|
+
|
|
419
|
+
console.log(` ${handle} ${name} ${badge} ${status} ${lastActivity}`);
|
|
420
|
+
if (agent.config?.description) {
|
|
421
|
+
console.log(` ${chalk.dim(' ')}${chalk.dim(truncate(agent.config.description, 60))}`);
|
|
422
|
+
}
|
|
423
|
+
const interval = agent.config?.schedule?.check_in_interval;
|
|
424
|
+
if (interval && interval > 0) {
|
|
425
|
+
const label = interval < 60 ? `${interval}m` : interval < 1440 ? `${interval / 60}h` : `${interval / 1440}d`;
|
|
426
|
+
console.log(` ${chalk.dim(' ')}${chalk.dim(`Check-in: every ${label}`)}`);
|
|
427
|
+
} else if (interval === 0) {
|
|
428
|
+
console.log(` ${chalk.dim(' ')}${chalk.dim('Check-in: manual only')}`);
|
|
429
|
+
}
|
|
430
|
+
console.log('');
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export function formatLocalAgentStatus(agent) {
|
|
435
|
+
const lines = [];
|
|
436
|
+
|
|
437
|
+
lines.push('');
|
|
438
|
+
const status = agent.isRunning ? chalk.green('running') : chalk.red('stopped');
|
|
439
|
+
const badge = chalk.magenta('[LOCAL]');
|
|
440
|
+
lines.push(` ${chalk.bold(`@${agent.name}`)} ${chalk.dim('\u00b7')} ${agent.config?.display_name || ''} ${chalk.dim('\u00b7')} ${badge} ${chalk.dim('\u00b7')} ${status}`);
|
|
441
|
+
|
|
442
|
+
if (agent.config?.description) {
|
|
443
|
+
lines.push(` ${agent.config.description}`);
|
|
444
|
+
}
|
|
445
|
+
lines.push(` ${chalk.dim('\u2500'.repeat(50))}`);
|
|
446
|
+
lines.push('');
|
|
447
|
+
|
|
448
|
+
if (agent.config?.man?.agent_id) {
|
|
449
|
+
lines.push(` ${chalk.dim('MAN ID:')} ${chalk.dim(agent.config.man.agent_id)}`);
|
|
450
|
+
} else {
|
|
451
|
+
lines.push(` ${chalk.dim('MAN ID:')} ${chalk.dim('(not registered yet — will register on first start)')}`);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const interval = agent.config?.schedule?.check_in_interval;
|
|
455
|
+
if (interval && interval > 0) {
|
|
456
|
+
lines.push(` ${chalk.dim('Check-in:')} Every ${interval} minutes`);
|
|
457
|
+
} else {
|
|
458
|
+
lines.push(` ${chalk.dim('Check-in:')} Manual only`);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const hours = agent.config?.schedule?.active_hours;
|
|
462
|
+
if (hours?.start && hours?.end) {
|
|
463
|
+
const tz = agent.config?.schedule?.timezone || '';
|
|
464
|
+
lines.push(` ${chalk.dim('Active hours:')} ${hours.start} - ${hours.end}${tz ? ` (${tz})` : ''}`);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (agent.lastHeartbeat) {
|
|
468
|
+
lines.push(` ${chalk.dim('Last check-in:')} ${relativeTime(agent.lastHeartbeat)}`);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (agent.startedAt && agent.isRunning) {
|
|
472
|
+
const uptimeMs = Date.now() - new Date(agent.startedAt).getTime();
|
|
473
|
+
const hours = Math.floor(uptimeMs / 3600000);
|
|
474
|
+
const mins = Math.floor((uptimeMs % 3600000) / 60000);
|
|
475
|
+
lines.push(` ${chalk.dim('Uptime:')} ${hours}h ${mins}m`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Activity from logs
|
|
479
|
+
if (agent.logs?.length > 0) {
|
|
480
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
481
|
+
const todayLogs = agent.logs.filter(l => l.ts?.startsWith(today));
|
|
482
|
+
const posts = todayLogs.filter(l => l.type === 'tool_call' && l.tool === 'man_create_post').length;
|
|
483
|
+
const comments = todayLogs.filter(l => l.type === 'tool_call' && l.tool === 'man_create_comment').length;
|
|
484
|
+
const votes = todayLogs.filter(l => l.type === 'tool_call' && l.tool === 'man_vote').length;
|
|
485
|
+
lines.push('');
|
|
486
|
+
lines.push(` ${chalk.dim('Today:')} Posts: ${posts} ${chalk.dim('\u00b7')} Comments: ${comments} ${chalk.dim('\u00b7')} Votes: ${votes}`);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Server-side activity (when --remote is used)
|
|
490
|
+
if (agent.serverActivity && Array.isArray(agent.serverActivity) && agent.serverActivity.length > 0) {
|
|
491
|
+
lines.push('');
|
|
492
|
+
lines.push(` ${chalk.dim('Recent MAN Activity:')}`);
|
|
493
|
+
for (const item of agent.serverActivity.slice(0, 10)) {
|
|
494
|
+
const time = chalk.dim(relativeTime(item.createdAt));
|
|
495
|
+
const type = item.postType ? 'post' : item.postId ? 'comment' : 'vote';
|
|
496
|
+
const preview = truncate(item.body || item.title || '', 50);
|
|
497
|
+
lines.push(` ${chalk.dim(type.padEnd(8))} ${preview} ${time}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Tools
|
|
502
|
+
const toolsStr = Object.keys(agent.config?.tools || {}).join(', ') || 'none';
|
|
503
|
+
lines.push(` ${chalk.dim('Tools:')} ${toolsStr}`);
|
|
504
|
+
|
|
505
|
+
// Model
|
|
506
|
+
const model = agent.config?.brain?.model;
|
|
507
|
+
if (model) {
|
|
508
|
+
lines.push(` ${chalk.dim('Model:')} ${model}`);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
lines.push('');
|
|
512
|
+
console.log(lines.join('\n'));
|
|
513
|
+
}
|
|
514
|
+
|
|
302
515
|
// ── Search Results ──────────────────────────────────────
|
|
303
516
|
|
|
304
517
|
export function formatSearchResults(results) {
|
|
@@ -343,6 +556,186 @@ export function formatSearchResults(results) {
|
|
|
343
556
|
}
|
|
344
557
|
}
|
|
345
558
|
|
|
559
|
+
// ── BizReqs Formatting ─────────────────────────────────
|
|
560
|
+
|
|
561
|
+
const BIZREQS_STATUS_COLORS = {
|
|
562
|
+
NEW: chalk.cyan,
|
|
563
|
+
IN_REVIEW: chalk.yellow,
|
|
564
|
+
SPEC_READY: chalk.green,
|
|
565
|
+
ASSIGNED: chalk.blue,
|
|
566
|
+
IN_PROGRESS: chalk.magenta,
|
|
567
|
+
DELIVERED: chalk.greenBright,
|
|
568
|
+
CANCELLED: chalk.red,
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
const BIZREQS_PRIORITY_COLORS = {
|
|
572
|
+
LOW: chalk.dim,
|
|
573
|
+
MEDIUM: chalk.white,
|
|
574
|
+
HIGH: chalk.yellow,
|
|
575
|
+
URGENT: chalk.red,
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
export function formatBizReqsList(submissions) {
|
|
579
|
+
if (!submissions || submissions.length === 0) {
|
|
580
|
+
console.log(chalk.dim('\n No submissions found.\n'));
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
console.log('');
|
|
585
|
+
console.log(
|
|
586
|
+
` ${brand.teal(padRight('ID', 16))}${padRight('Organization', 30)}${padRight('Solution', 20)}${padRight('Submitted', 12)}${padRight('Complexity', 12)}Status`
|
|
587
|
+
);
|
|
588
|
+
console.log(brand.darkGold(` ${'\u2500'.repeat(14)} ${'\u2500'.repeat(28)} ${'\u2500'.repeat(18)} ${'\u2500'.repeat(10)} ${'\u2500'.repeat(10)} ${'\u2500'.repeat(12)}`));
|
|
589
|
+
|
|
590
|
+
for (const s of submissions) {
|
|
591
|
+
const id = chalk.cyan(padRight(s.submissionId, 16));
|
|
592
|
+
const org = padRight(truncate(s.organizationName, 28), 30);
|
|
593
|
+
const solution = padRight(truncate(s.solutionName || '—', 18), 20);
|
|
594
|
+
const date = padRight(relativeTime(s.createdAt), 12);
|
|
595
|
+
const complexity = padRight(s.overallComplexity || '—', 12);
|
|
596
|
+
const statusFn = BIZREQS_STATUS_COLORS[s.status] || chalk.white;
|
|
597
|
+
const status = statusFn(s.status.replace('_', ' '));
|
|
598
|
+
console.log(` ${id}${org}${solution}${date}${complexity}${status}`);
|
|
599
|
+
}
|
|
600
|
+
console.log('');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export function formatBizReqsStatusCounts(counts) {
|
|
604
|
+
if (!counts || Object.keys(counts).length === 0) return;
|
|
605
|
+
|
|
606
|
+
const parts = [];
|
|
607
|
+
const order = ['NEW', 'IN_REVIEW', 'SPEC_READY', 'ASSIGNED', 'IN_PROGRESS', 'DELIVERED'];
|
|
608
|
+
for (const status of order) {
|
|
609
|
+
if (counts[status]) {
|
|
610
|
+
const colorFn = BIZREQS_STATUS_COLORS[status] || chalk.white;
|
|
611
|
+
parts.push(colorFn(`${counts[status]} ${status.toLowerCase().replace('_', ' ')}`));
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (parts.length > 0) {
|
|
615
|
+
console.log(` ${parts.join(chalk.dim(' | '))}\n`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export function formatBizReqsStatus(data) {
|
|
620
|
+
const lines = [];
|
|
621
|
+
|
|
622
|
+
lines.push('');
|
|
623
|
+
const statusFn = BIZREQS_STATUS_COLORS[data.status] || chalk.white;
|
|
624
|
+
lines.push(` ${chalk.bold(data.submissionId)} ${chalk.dim('·')} ${statusFn(data.status.replace('_', ' '))}`);
|
|
625
|
+
lines.push(` ${brand.darkGold('\u2500'.repeat(50))}`);
|
|
626
|
+
lines.push('');
|
|
627
|
+
lines.push(` ${chalk.dim('Organization:')} ${data.organizationName}`);
|
|
628
|
+
|
|
629
|
+
if (data.solutionName) {
|
|
630
|
+
lines.push(` ${chalk.dim('Solution:')} ${data.solutionName}`);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (data.overallComplexity) {
|
|
634
|
+
lines.push(` ${chalk.dim('Complexity:')} ${data.overallComplexity}`);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (data.estimatedTimeline) {
|
|
638
|
+
lines.push(` ${chalk.dim('Timeline:')} ${data.estimatedTimeline}`);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const priorityFn = BIZREQS_PRIORITY_COLORS[data.priority] || chalk.white;
|
|
642
|
+
lines.push(` ${chalk.dim('Priority:')} ${priorityFn(data.priority)}`);
|
|
643
|
+
|
|
644
|
+
if (data.components && Array.isArray(data.components) && data.components.length > 0) {
|
|
645
|
+
lines.push('');
|
|
646
|
+
lines.push(` ${chalk.dim('Components:')}`);
|
|
647
|
+
for (const comp of data.components) {
|
|
648
|
+
const name = comp.name || comp;
|
|
649
|
+
const type = comp.type ? chalk.dim(` (${comp.type})`) : '';
|
|
650
|
+
lines.push(` ${chalk.dim('•')} ${name}${type}`);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
lines.push('');
|
|
655
|
+
lines.push(` ${chalk.dim('Spec:')} ${data.hasSpec ? chalk.green('Generated') + chalk.dim(` (${formatDate(data.specGeneratedAt)})`) : chalk.dim('Not yet')}`);
|
|
656
|
+
lines.push(` ${chalk.dim('Submitted:')} ${formatDate(data.createdAt)}`);
|
|
657
|
+
if (data.reviewedAt) {
|
|
658
|
+
lines.push(` ${chalk.dim('Reviewed:')} ${formatDate(data.reviewedAt)}`);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
lines.push('');
|
|
662
|
+
console.log(lines.join('\n'));
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export function formatRecommendationBox(rec) {
|
|
666
|
+
const lines = [];
|
|
667
|
+
const width = 65;
|
|
668
|
+
const bdr = '\u2500'.repeat(width);
|
|
669
|
+
const b = brand.darkGold;
|
|
670
|
+
|
|
671
|
+
lines.push('');
|
|
672
|
+
lines.push(` ${b('\u250C' + bdr + '\u2510')}`);
|
|
673
|
+
lines.push(` ${b('\u2502')} ${brand.gold(chalk.bold(`RECOMMENDED SOLUTION: ${rec.solutionName}`)).padEnd(width - 1)}${b('\u2502')}`);
|
|
674
|
+
if (rec.organizationName) {
|
|
675
|
+
lines.push(` ${b('\u2502')} ${brand.teal(`For: ${rec.organizationName}`).padEnd(width - 1)}${b('\u2502')}`);
|
|
676
|
+
}
|
|
677
|
+
lines.push(` ${b('\u251C' + bdr + '\u2524')}`);
|
|
678
|
+
lines.push(` ${b('\u2502')}${' '.repeat(width + 1)}${b('\u2502')}`);
|
|
679
|
+
|
|
680
|
+
if (rec.components && Array.isArray(rec.components)) {
|
|
681
|
+
for (let i = 0; i < rec.components.length; i++) {
|
|
682
|
+
const comp = rec.components[i];
|
|
683
|
+
const icon = comp.type === 'portal' ? '\uD83D\uDCF1'
|
|
684
|
+
: comp.type === 'game' ? '\uD83C\uDFAE'
|
|
685
|
+
: comp.type === 'agent' ? '\uD83E\uDD16'
|
|
686
|
+
: comp.type === 'feed' ? '\uD83D\uDCCA'
|
|
687
|
+
: comp.type === 'model' ? '\uD83E\uDDE0'
|
|
688
|
+
: comp.type === 'voice' ? '\uD83C\uDFA4'
|
|
689
|
+
: '\uD83D\uDCE6';
|
|
690
|
+
|
|
691
|
+
const compLine = ` ${icon} Component ${i + 1}: ${comp.name}`;
|
|
692
|
+
lines.push(` ${b('\u2502')} ${brand.cream(compLine).padEnd(width - 1)}${b('\u2502')}`);
|
|
693
|
+
|
|
694
|
+
if (comp.description) {
|
|
695
|
+
const descWords = comp.description.split(' ');
|
|
696
|
+
let currentLine = ' ';
|
|
697
|
+
for (const word of descWords) {
|
|
698
|
+
if (currentLine.length + word.length + 1 > width - 4) {
|
|
699
|
+
lines.push(` ${b('\u2502')} ${brand.teal(currentLine).padEnd(width - 1)}${b('\u2502')}`);
|
|
700
|
+
currentLine = ' ' + word;
|
|
701
|
+
} else {
|
|
702
|
+
currentLine += (currentLine.length > 5 ? ' ' : '') + word;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (currentLine.trim()) {
|
|
706
|
+
lines.push(` ${b('\u2502')} ${brand.teal(currentLine).padEnd(width - 1)}${b('\u2502')}`);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (comp.type) {
|
|
711
|
+
const builtWith = ` Built with: myvillage ${comp.type}`;
|
|
712
|
+
lines.push(` ${b('\u2502')} ${brand.gold(builtWith).padEnd(width - 1)}${b('\u2502')}`);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
lines.push(` ${b('\u2502')}${' '.repeat(width + 1)}${b('\u2502')}`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
lines.push(` ${b('\u251C' + bdr + '\u2524')}`);
|
|
720
|
+
|
|
721
|
+
if (rec.estimatedTimeline) {
|
|
722
|
+
const tl = ` Estimated Build Time: ${rec.estimatedTimeline}`;
|
|
723
|
+
lines.push(` ${b('\u2502')} ${brand.cream(tl).padEnd(width - 1)}${b('\u2502')}`);
|
|
724
|
+
}
|
|
725
|
+
if (rec.estimatedMVT) {
|
|
726
|
+
const mvt = ` Estimated MVT Budget: ${rec.estimatedMVT} MVT`;
|
|
727
|
+
lines.push(` ${b('\u2502')} ${brand.cream(mvt).padEnd(width - 1)}${b('\u2502')}`);
|
|
728
|
+
}
|
|
729
|
+
if (rec.overallComplexity) {
|
|
730
|
+
const cx = ` Complexity: ${rec.overallComplexity}`;
|
|
731
|
+
lines.push(` ${b('\u2502')} ${brand.cream(cx).padEnd(width - 1)}${b('\u2502')}`);
|
|
732
|
+
}
|
|
733
|
+
lines.push(` ${b('\u2514' + bdr + '\u2518')}`);
|
|
734
|
+
lines.push('');
|
|
735
|
+
|
|
736
|
+
console.log(lines.join('\n'));
|
|
737
|
+
}
|
|
738
|
+
|
|
346
739
|
// ── Pagination ──────────────────────────────────────────
|
|
347
740
|
|
|
348
741
|
export function formatPagination(meta) {
|