@myvillage/cli 1.10.2 → 1.18.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/package.json +1 -1
- package/src/agent-runtime/loop.js +215 -6
- package/src/commands/agent-client.js +435 -0
- package/src/commands/agent-grant.js +131 -0
- package/src/commands/agent-local.js +395 -1
- package/src/commands/create-app.js +61 -1
- package/src/commands/media.js +185 -187
- package/src/commands/wisdom.js +185 -0
- package/src/index.js +212 -0
- package/src/utils/agent-scaffolder.js +8 -0
- package/src/utils/agentic-templates.js +10 -2
- package/src/utils/api.js +179 -0
- package/src/utils/formatters.js +72 -0
- package/src/utils/wisdom.js +102 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { join, basename } from 'path';
|
|
4
|
+
import { isAuthenticated } from '../utils/auth.js';
|
|
5
|
+
import { brand, villageSpinner } from '../utils/brand.js';
|
|
6
|
+
import { agentExists } from '../utils/local-agent.js';
|
|
7
|
+
import {
|
|
8
|
+
listVillageBooks,
|
|
9
|
+
exportVillageBook,
|
|
10
|
+
importVillageBook,
|
|
11
|
+
} from '../utils/api.js';
|
|
12
|
+
import {
|
|
13
|
+
parseWisdom,
|
|
14
|
+
serializeWisdom,
|
|
15
|
+
slugify,
|
|
16
|
+
getAgentWisdomDir,
|
|
17
|
+
ensureWisdomDir,
|
|
18
|
+
readAgentWisdom,
|
|
19
|
+
writeWisdomFile,
|
|
20
|
+
} from '../utils/wisdom.js';
|
|
21
|
+
|
|
22
|
+
function requireAuth() {
|
|
23
|
+
if (!isAuthenticated()) {
|
|
24
|
+
console.log(chalk.red(' ✗ Authentication required. Run \'myvillage login\' first.'));
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveAgentName(name, action) {
|
|
31
|
+
if (!name) {
|
|
32
|
+
console.log(chalk.red(` ✗ Specify an agent: myvillage wisdom ${action} --agent <name>\n`));
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (!agentExists(name)) {
|
|
36
|
+
console.log(chalk.red(` ✗ Agent "${name}" not found.\n`));
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return name;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── list ────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export async function wisdomListCommand(options = {}) {
|
|
45
|
+
if (options.remote) {
|
|
46
|
+
if (!requireAuth()) return;
|
|
47
|
+
try {
|
|
48
|
+
const result = await listVillageBooks({ bookTypeId: 'wisdom' });
|
|
49
|
+
// The API can return either an array directly or wrapped in { data }
|
|
50
|
+
const books = Array.isArray(result) ? result : result?.data || [];
|
|
51
|
+
const wisdom = books.filter(b => b.bookType?.name === 'wisdom' || !b.bookType);
|
|
52
|
+
if (wisdom.length === 0) {
|
|
53
|
+
console.log(brand.teal(' No community wisdom files published yet.\n'));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
console.log(brand.teal(` ${wisdom.length} wisdom file(s) on the network:\n`));
|
|
57
|
+
for (const w of wisdom) {
|
|
58
|
+
console.log(` ${brand.gold(w.id)} ${chalk.bold(w.name)}`);
|
|
59
|
+
if (w.description) console.log(` ${chalk.dim(w.description)}`);
|
|
60
|
+
}
|
|
61
|
+
console.log('');
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const msg = err.response?.data?.error || err.message;
|
|
64
|
+
console.log(chalk.red(` ✗ Failed to list remote wisdom: ${msg}\n`));
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Local mode: list every wisdom file across every agent on disk
|
|
70
|
+
if (!options.agent) {
|
|
71
|
+
console.log(chalk.yellow(' Hint: pass --agent <name> to list one agent\'s wisdom, or --remote to list community-published wisdom.\n'));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (!resolveAgentName(options.agent, 'list')) return;
|
|
75
|
+
const files = readAgentWisdom(options.agent);
|
|
76
|
+
if (files.length === 0) {
|
|
77
|
+
console.log(brand.teal(` Agent "${options.agent}" has no wisdom files yet.\n`));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
console.log(brand.teal(` ${files.length} local wisdom file(s) for "${options.agent}":\n`));
|
|
81
|
+
for (const w of files) {
|
|
82
|
+
const linked = w.villageBookId ? brand.gold(` (synced ${w.villageBookId})`) : chalk.dim(' (local-only)');
|
|
83
|
+
console.log(` ${chalk.bold(w.name)}${linked}`);
|
|
84
|
+
if (w.description) console.log(` ${chalk.dim(w.description)}`);
|
|
85
|
+
console.log(` ${chalk.dim(w.path)}`);
|
|
86
|
+
}
|
|
87
|
+
console.log('');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── pull ────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
export async function wisdomPullCommand(id, options = {}) {
|
|
93
|
+
if (!requireAuth()) return;
|
|
94
|
+
if (!id) {
|
|
95
|
+
console.log(chalk.red(' ✗ Specify a village book id: myvillage wisdom pull <id> --into <agent>\n'));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (!options.into) {
|
|
99
|
+
console.log(chalk.red(' ✗ Specify a target agent: myvillage wisdom pull <id> --into <agent>\n'));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!resolveAgentName(options.into, 'pull')) return;
|
|
103
|
+
|
|
104
|
+
const spinner = villageSpinner('Fetching wisdom from the network...').start();
|
|
105
|
+
try {
|
|
106
|
+
const text = await exportVillageBook(id);
|
|
107
|
+
const { frontmatter } = parseWisdom(text);
|
|
108
|
+
const slug = slugify(frontmatter.name || id);
|
|
109
|
+
const dir = ensureWisdomDir(options.into);
|
|
110
|
+
const filePath = join(dir, `${slug}.wisdom`);
|
|
111
|
+
writeWisdomFile(filePath, parseWisdom(text)); // re-serialize for consistent formatting
|
|
112
|
+
spinner.succeed(`Wisdom "${frontmatter.name || id}" saved to ${filePath}`);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
const msg = err.response?.data?.error || err.message;
|
|
115
|
+
spinner.fail(`Failed to pull wisdom: ${msg}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── push ────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
export async function wisdomPushCommand(filePath) {
|
|
122
|
+
if (!requireAuth()) return;
|
|
123
|
+
if (!filePath) {
|
|
124
|
+
console.log(chalk.red(' ✗ Specify a .wisdom file: myvillage wisdom push <file>\n'));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!existsSync(filePath)) {
|
|
128
|
+
console.log(chalk.red(` ✗ File not found: ${filePath}\n`));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
133
|
+
const spinner = villageSpinner('Publishing wisdom to the network...').start();
|
|
134
|
+
try {
|
|
135
|
+
const result = await importVillageBook({ content });
|
|
136
|
+
const book = result?.data || result;
|
|
137
|
+
if (book?.id) {
|
|
138
|
+
// Save the assigned id back into the local file so future pushes update
|
|
139
|
+
// the same row instead of creating duplicates.
|
|
140
|
+
const parsed = parseWisdom(content);
|
|
141
|
+
parsed.frontmatter.villageBookId = book.id;
|
|
142
|
+
writeWisdomFile(filePath, parsed);
|
|
143
|
+
spinner.succeed(`Published as ${book.id}`);
|
|
144
|
+
} else {
|
|
145
|
+
spinner.succeed('Published.');
|
|
146
|
+
}
|
|
147
|
+
} catch (err) {
|
|
148
|
+
const msg = err.response?.data?.error || err.message;
|
|
149
|
+
spinner.fail(`Failed to push wisdom: ${msg}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── new ─────────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
export async function wisdomNewCommand(name, options = {}) {
|
|
156
|
+
if (!name) {
|
|
157
|
+
console.log(chalk.red(' ✗ Specify a name: myvillage wisdom new <name> --agent <agent>\n'));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (!options.agent) {
|
|
161
|
+
console.log(chalk.red(' ✗ Specify a target agent: myvillage wisdom new <name> --agent <agent>\n'));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (!resolveAgentName(options.agent, 'new')) return;
|
|
165
|
+
|
|
166
|
+
const slug = slugify(name);
|
|
167
|
+
const dir = ensureWisdomDir(options.agent);
|
|
168
|
+
const filePath = join(dir, `${slug}.wisdom`);
|
|
169
|
+
if (existsSync(filePath)) {
|
|
170
|
+
console.log(chalk.yellow(` ${filePath} already exists; edit it instead.\n`));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const template = serializeWisdom({
|
|
175
|
+
frontmatter: {
|
|
176
|
+
name: slug,
|
|
177
|
+
description: options.description || 'Describe what this skill does and when to apply it.',
|
|
178
|
+
trigger: options.trigger || 'Describe the condition that should activate this skill.',
|
|
179
|
+
},
|
|
180
|
+
body: '<!-- The body of this wisdom file. Write it as a short skill: tone, structure, examples. The agent loads it into its system prompt every iteration. -->\n',
|
|
181
|
+
});
|
|
182
|
+
writeWisdomFile(filePath, parseWisdom(template));
|
|
183
|
+
console.log(brand.green(` ✓ Wisdom scaffolded at ${filePath}`));
|
|
184
|
+
console.log(brand.teal(` Edit the body, then publish with: myvillage wisdom push ${filePath}\n`));
|
|
185
|
+
}
|
package/src/index.js
CHANGED
|
@@ -2,6 +2,12 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import updateNotifier from 'update-notifier';
|
|
4
4
|
import { loginCommand } from './commands/login.js';
|
|
5
|
+
import {
|
|
6
|
+
mediaDraftCreateCommand,
|
|
7
|
+
mediaDraftEditCommand,
|
|
8
|
+
mediaDraftListCommand,
|
|
9
|
+
mediaDraftStatusCommand,
|
|
10
|
+
} from './commands/media.js';
|
|
5
11
|
import { logoutCommand } from './commands/logout.js';
|
|
6
12
|
import { createGameCommand } from './commands/create-game.js';
|
|
7
13
|
import { createCommand } from './commands/create-app.js';
|
|
@@ -45,7 +51,27 @@ import {
|
|
|
45
51
|
agentLogsCommand,
|
|
46
52
|
agentAddToolCommand,
|
|
47
53
|
agentRemoveToolCommand,
|
|
54
|
+
agentTaskListCommand,
|
|
55
|
+
agentTaskAssignCommand,
|
|
56
|
+
agentTaskRetryCommand,
|
|
57
|
+
agentTaskRetryFailedCommand,
|
|
58
|
+
agentMemoryCommand,
|
|
59
|
+
agentRecallCommand,
|
|
60
|
+
agentRememberCommand,
|
|
48
61
|
} from './commands/agent-local.js';
|
|
62
|
+
import {
|
|
63
|
+
agentGrantCommand,
|
|
64
|
+
agentRevokeCommand,
|
|
65
|
+
agentGrantsCommand,
|
|
66
|
+
} from './commands/agent-grant.js';
|
|
67
|
+
import {
|
|
68
|
+
agentRegisterClientCommand,
|
|
69
|
+
agentListClientsCommand,
|
|
70
|
+
agentViewClientCommand,
|
|
71
|
+
agentEditClientCommand,
|
|
72
|
+
agentDeactivateClientCommand,
|
|
73
|
+
agentRotateClientKeyCommand,
|
|
74
|
+
} from './commands/agent-client.js';
|
|
49
75
|
import {
|
|
50
76
|
bizreqsNewCommand,
|
|
51
77
|
bizreqsSpecCommand,
|
|
@@ -69,6 +95,12 @@ import { checkinCommand } from './commands/checkin.js';
|
|
|
69
95
|
import { discoverCommand } from './commands/discover.js';
|
|
70
96
|
import { logCommand } from './commands/log.js';
|
|
71
97
|
import { storyCommand } from './commands/story.js';
|
|
98
|
+
import {
|
|
99
|
+
wisdomListCommand,
|
|
100
|
+
wisdomPullCommand,
|
|
101
|
+
wisdomPushCommand,
|
|
102
|
+
wisdomNewCommand,
|
|
103
|
+
} from './commands/wisdom.js';
|
|
72
104
|
import {
|
|
73
105
|
soulprintInitCommand,
|
|
74
106
|
soulprintIngestCommand,
|
|
@@ -408,6 +440,109 @@ export function run() {
|
|
|
408
440
|
.description('Remove an MCP server tool from a local agent')
|
|
409
441
|
.action(agentRemoveToolCommand);
|
|
410
442
|
|
|
443
|
+
// Task queue commands — assign and inspect work for a developer's agent
|
|
444
|
+
agentCmd
|
|
445
|
+
.command('task-list <name>')
|
|
446
|
+
.description('List tasks queued for a local agent')
|
|
447
|
+
.option('--status <status>', 'Filter by status (PENDING|IN_PROGRESS|COMPLETED|FAILED|CANCELLED)')
|
|
448
|
+
.option('--limit <n>', 'Max number of tasks to show', '20')
|
|
449
|
+
.action(agentTaskListCommand);
|
|
450
|
+
|
|
451
|
+
agentCmd
|
|
452
|
+
.command('task-assign <name>')
|
|
453
|
+
.description('Assign a task to a local agent (it will pick it up on next poll)')
|
|
454
|
+
.option('--type <type>', 'Task type (e.g., CLIENT_TASK, GENERATE_POST, SHARE_KNOWLEDGE)')
|
|
455
|
+
.option('--instruction <text>', 'Free-text instruction (required for CLIENT_TASK)')
|
|
456
|
+
.option('--input <json>', 'JSON-encoded structured input payload')
|
|
457
|
+
.option('--priority <n>', 'Priority 1-10 (lower runs first)', '5')
|
|
458
|
+
.action(agentTaskAssignCommand);
|
|
459
|
+
|
|
460
|
+
agentCmd
|
|
461
|
+
.command('task-retry <name> <taskId>')
|
|
462
|
+
.description('Reset a FAILED or CANCELLED task back to PENDING so the agent retries it')
|
|
463
|
+
.action(agentTaskRetryCommand);
|
|
464
|
+
|
|
465
|
+
agentCmd
|
|
466
|
+
.command('task-retry-failed <name>')
|
|
467
|
+
.description('Bulk-reset every FAILED task for an agent back to PENDING')
|
|
468
|
+
.option('--filter <text>', 'Only retry tasks whose errorMessage contains this substring')
|
|
469
|
+
.action(agentTaskRetryFailedCommand);
|
|
470
|
+
|
|
471
|
+
// Agent memory (short-term KV state) and recall (long-term searchable memory)
|
|
472
|
+
agentCmd
|
|
473
|
+
.command('memory <name> <action> [args...]')
|
|
474
|
+
.description('Read/write an agent\'s short-term memory: list | get <key> | set <key> <value> | delete <key>')
|
|
475
|
+
.action((name, action, args = []) => agentMemoryCommand(name, action, ...args));
|
|
476
|
+
|
|
477
|
+
agentCmd
|
|
478
|
+
.command('recall <name> <query>')
|
|
479
|
+
.description('Search this agent\'s long-term memory (Knowledge submissions it authored)')
|
|
480
|
+
.option('--limit <n>', 'Max results to show', '10')
|
|
481
|
+
.action(agentRecallCommand);
|
|
482
|
+
|
|
483
|
+
agentCmd
|
|
484
|
+
.command('remember <name> <text>')
|
|
485
|
+
.description('Save a memory to this agent\'s long-term store (Knowledge submission with source=AI_AGENT)')
|
|
486
|
+
.option('--summary <text>', 'Short summary of the memory')
|
|
487
|
+
.option('--themes <list>', 'Comma-separated tags')
|
|
488
|
+
.option('--sharing <option>', 'PRIVATE | VILLAGE_ONLY | PUBLIC', 'PRIVATE')
|
|
489
|
+
.action(agentRememberCommand);
|
|
490
|
+
|
|
491
|
+
// Per-agent OAuth credential grants
|
|
492
|
+
agentCmd
|
|
493
|
+
.command('grants <name>')
|
|
494
|
+
.description('List active OAuth credential grants for a local agent')
|
|
495
|
+
.action(agentGrantsCommand);
|
|
496
|
+
|
|
497
|
+
agentCmd
|
|
498
|
+
.command('grant <name> <provider>')
|
|
499
|
+
.description('Grant the agent access to a connected OAuth provider (google|microsoft|zoom)')
|
|
500
|
+
.action(agentGrantCommand);
|
|
501
|
+
|
|
502
|
+
agentCmd
|
|
503
|
+
.command('revoke <name> <provider>')
|
|
504
|
+
.description('Revoke an OAuth provider grant from the agent')
|
|
505
|
+
.action(agentRevokeCommand);
|
|
506
|
+
|
|
507
|
+
// Client agent registration commands
|
|
508
|
+
agentCmd
|
|
509
|
+
.command('register-client')
|
|
510
|
+
.description('Register a client application for agent automation')
|
|
511
|
+
.option('--agent <handle>', 'Agent handle')
|
|
512
|
+
.option('--client-id <id>', 'Client identifier (lowercase)')
|
|
513
|
+
.option('--name <name>', 'Client display name')
|
|
514
|
+
.option('--url <url>', 'Client base URL')
|
|
515
|
+
.option('--workflow <type>', 'Workflow type (e.g., submission_processor)')
|
|
516
|
+
.option('--schedule <cron>', 'Cron schedule expression')
|
|
517
|
+
.option('--timezone <tz>', 'Timezone (default: America/Chicago)')
|
|
518
|
+
.option('--env-file <path>', 'Write API key to this .env file')
|
|
519
|
+
.action(agentRegisterClientCommand);
|
|
520
|
+
|
|
521
|
+
agentCmd
|
|
522
|
+
.command('list-clients')
|
|
523
|
+
.description('List all registered client agent configs')
|
|
524
|
+
.action(agentListClientsCommand);
|
|
525
|
+
|
|
526
|
+
agentCmd
|
|
527
|
+
.command('view-client <configId>')
|
|
528
|
+
.description('View a client agent config')
|
|
529
|
+
.action(agentViewClientCommand);
|
|
530
|
+
|
|
531
|
+
agentCmd
|
|
532
|
+
.command('edit-client <configId>')
|
|
533
|
+
.description('Edit a client agent config')
|
|
534
|
+
.action(agentEditClientCommand);
|
|
535
|
+
|
|
536
|
+
agentCmd
|
|
537
|
+
.command('deactivate-client <configId>')
|
|
538
|
+
.description('Deactivate a client agent config')
|
|
539
|
+
.action(agentDeactivateClientCommand);
|
|
540
|
+
|
|
541
|
+
agentCmd
|
|
542
|
+
.command('rotate-client-key <configId>')
|
|
543
|
+
.description('Rotate the API key for a client agent config')
|
|
544
|
+
.action(agentRotateClientKeyCommand);
|
|
545
|
+
|
|
411
546
|
// ── Village Content Pipeline ───────────────────────────
|
|
412
547
|
|
|
413
548
|
program
|
|
@@ -432,6 +567,49 @@ export function run() {
|
|
|
432
567
|
.description('Share a story or wisdom with the network')
|
|
433
568
|
.action(storyCommand);
|
|
434
569
|
|
|
570
|
+
// ── Media: Soulprint Reels (mobile-app posts) ──────────
|
|
571
|
+
|
|
572
|
+
const mediaCmd = program
|
|
573
|
+
.command('media')
|
|
574
|
+
.description('Manage Soulprint Reels (internal social posts shown in the mobile app)');
|
|
575
|
+
|
|
576
|
+
const mediaDraftCmd = mediaCmd
|
|
577
|
+
.command('draft')
|
|
578
|
+
.description('Reel draft commands');
|
|
579
|
+
|
|
580
|
+
mediaDraftCmd
|
|
581
|
+
.command('create')
|
|
582
|
+
.description('Create a new reel draft (video, photo, carousel, audio, text)')
|
|
583
|
+
.option('-f, --file <path>', 'Attach a media file (repeatable)', (v, prev) => prev ? [...prev, v] : [v])
|
|
584
|
+
.option('-a, --asset-type <type>', 'Asset type when --file is given (IMAGE, VIDEO, THUMBNAIL, DOCUMENT)')
|
|
585
|
+
.option('--submit', 'Auto-submit the draft for review after creation')
|
|
586
|
+
.action(mediaDraftCreateCommand);
|
|
587
|
+
|
|
588
|
+
mediaDraftCmd
|
|
589
|
+
.command('edit <id>')
|
|
590
|
+
.description('Edit a DRAFT or REJECTED reel draft')
|
|
591
|
+
.action(mediaDraftEditCommand);
|
|
592
|
+
|
|
593
|
+
mediaDraftCmd
|
|
594
|
+
.command('list')
|
|
595
|
+
.description('List reel drafts')
|
|
596
|
+
.option('--status <status>', 'Filter by status (DRAFT, SUBMITTED, IN_REVIEW, APPROVED, REJECTED, PUBLISHED)')
|
|
597
|
+
.option('--content-type <type>', 'Filter by content type (VIDEO, PHOTO, CAROUSEL, AUDIO, TEXT)')
|
|
598
|
+
.option('--platform <platform>', '[Deprecated] Legacy platform alias — maps forward to content type')
|
|
599
|
+
.option('--visibility <visibility>', 'Filter by visibility (PUBLIC, COMMUNITY, PRIVATE)')
|
|
600
|
+
.option('--community-id <id>', 'Filter by community')
|
|
601
|
+
.option('--search <query>', 'Search captions / titles / quotes')
|
|
602
|
+
.option('-n, --limit <number>', 'Number of drafts', '20')
|
|
603
|
+
.option('--offset <number>', 'Pagination offset', '0')
|
|
604
|
+
.option('--sort <sort>', 'Sort: newest, oldest, publish_date', 'newest')
|
|
605
|
+
.option('--json', 'Output raw JSON')
|
|
606
|
+
.action(mediaDraftListCommand);
|
|
607
|
+
|
|
608
|
+
mediaDraftCmd
|
|
609
|
+
.command('status <id>')
|
|
610
|
+
.description('Show status of a reel draft')
|
|
611
|
+
.action(mediaDraftStatusCommand);
|
|
612
|
+
|
|
435
613
|
// ── BizReqs: Business Requirements Pipeline ───────────
|
|
436
614
|
|
|
437
615
|
const bizreqsCmd = program
|
|
@@ -483,6 +661,40 @@ export function run() {
|
|
|
483
661
|
|
|
484
662
|
// ── SoulPrint Studio: Model Training Pipeline ───────────
|
|
485
663
|
|
|
664
|
+
// \u2500\u2500 Wisdom: agent skill packs (Books of Wisdom) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
665
|
+
|
|
666
|
+
const wisdomCmd = program
|
|
667
|
+
.command('wisdom')
|
|
668
|
+
.description('Manage .wisdom files \u2014 agent skill packs backed by the network\'s Books of Wisdom');
|
|
669
|
+
|
|
670
|
+
wisdomCmd
|
|
671
|
+
.command('list')
|
|
672
|
+
.description('List wisdom files (use --remote for community-published, --agent for local)')
|
|
673
|
+
.option('--remote', 'List community-published wisdom on the network')
|
|
674
|
+
.option('--agent <name>', 'List a local agent\'s wisdom files')
|
|
675
|
+
.action(wisdomListCommand);
|
|
676
|
+
|
|
677
|
+
wisdomCmd
|
|
678
|
+
.command('pull <id>')
|
|
679
|
+
.description('Fetch a community wisdom file and save it into a local agent')
|
|
680
|
+
.requiredOption('--into <agent>', 'Local agent to save the wisdom file into')
|
|
681
|
+
.action(wisdomPullCommand);
|
|
682
|
+
|
|
683
|
+
wisdomCmd
|
|
684
|
+
.command('push <file>')
|
|
685
|
+
.description('Publish a .wisdom file to the network so other agents can use it')
|
|
686
|
+
.action(wisdomPushCommand);
|
|
687
|
+
|
|
688
|
+
wisdomCmd
|
|
689
|
+
.command('new <name>')
|
|
690
|
+
.description('Scaffold a new local .wisdom file for an agent')
|
|
691
|
+
.requiredOption('--agent <name>', 'Local agent to add the wisdom file to')
|
|
692
|
+
.option('--description <text>', 'Short description shown in the agent\'s skill list')
|
|
693
|
+
.option('--trigger <text>', 'Condition that should activate this skill')
|
|
694
|
+
.action(wisdomNewCommand);
|
|
695
|
+
|
|
696
|
+
// \u2500\u2500 SoulPrint Studio (legacy): training pipeline \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
697
|
+
|
|
486
698
|
const soulprintCmd = program
|
|
487
699
|
.command('soulprint')
|
|
488
700
|
.description('SoulPrint Studio \u2014 datasets, training, and model publishing');
|
|
@@ -138,6 +138,14 @@ ${description}
|
|
|
138
138
|
- Speaks casually but clearly
|
|
139
139
|
- Concise in responses
|
|
140
140
|
|
|
141
|
+
## Handling tasks
|
|
142
|
+
When you receive a TASK in your context, follow these rules:
|
|
143
|
+
|
|
144
|
+
- **Use the values from the task input verbatim.** If the task input is JSON like \`{"communitySlug":"general"}\`, call the tool with \`communitySlug: "general"\` exactly. Do not substitute, translate, or invent slugs, IDs, or names.
|
|
145
|
+
- **If a required value is missing, do NOT guess.** Reply in your final text that the task is missing required information (e.g. "Task is missing a communitySlug — cannot proceed"). Don't pick a community at random.
|
|
146
|
+
- **If a tool call fails, do NOT claim success.** Report what failed and why in your final text. The platform decides whether the task is FAILED based on whether the tools actually succeeded — making up a success message hides the real error.
|
|
147
|
+
- **Use the platform's communities you already belong to.** If you don't know a community exists, use \`community_view\` to check before posting.
|
|
148
|
+
|
|
141
149
|
## Boundaries
|
|
142
150
|
- Never share personal files or private data to the feed
|
|
143
151
|
- Ask before posting anything longer than 2 sentences
|
|
@@ -40,6 +40,7 @@ export function createAgenticAppProject(targetDir, options) {
|
|
|
40
40
|
includeRestApi = true,
|
|
41
41
|
mcpToolGroups = [],
|
|
42
42
|
oauthCredentials = null,
|
|
43
|
+
agentConfig = null,
|
|
43
44
|
} = options;
|
|
44
45
|
|
|
45
46
|
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
|
|
@@ -88,7 +89,7 @@ export function createAgenticAppProject(targetDir, options) {
|
|
|
88
89
|
writeFileSync(join(targetDir, 'package.json'), generatePackageJson(slug, description, includeMcp));
|
|
89
90
|
writeFileSync(join(targetDir, 'next.config.mjs'), generateNextConfig());
|
|
90
91
|
writeFileSync(join(targetDir, '.gitignore'), generateGitignore());
|
|
91
|
-
writeFileSync(join(targetDir, '.env.local'), generateEnv(oauthCredentials, hasOAuth, includeMcp));
|
|
92
|
+
writeFileSync(join(targetDir, '.env.local'), generateEnv(oauthCredentials, hasOAuth, includeMcp, agentConfig));
|
|
92
93
|
writeFileSync(join(targetDir, '.env.example'), generateEnvExample(hasOAuth, includeMcp));
|
|
93
94
|
writeFileSync(join(targetDir, 'README.md'), generateReadme(name, description, hasOAuth, includeMcp, includeRestApi, features));
|
|
94
95
|
writeFileSync(join(targetDir, 'jsconfig.json'), generateJsConfig());
|
|
@@ -214,7 +215,7 @@ out/
|
|
|
214
215
|
`;
|
|
215
216
|
}
|
|
216
217
|
|
|
217
|
-
function generateEnv(oauthCredentials, hasOAuth, includeMcp) {
|
|
218
|
+
function generateEnv(oauthCredentials, hasOAuth, includeMcp, agentConfig = null) {
|
|
218
219
|
const lines = [];
|
|
219
220
|
|
|
220
221
|
if (hasOAuth) {
|
|
@@ -233,6 +234,11 @@ function generateEnv(oauthCredentials, hasOAuth, includeMcp) {
|
|
|
233
234
|
lines.push('MYVILLAGEOS_MCP_URL=https://mcp.myvillageproject.ai');
|
|
234
235
|
}
|
|
235
236
|
|
|
237
|
+
if (agentConfig?.apiKey) {
|
|
238
|
+
lines.push(`MYVILLAGE_AGENT_API_KEY=${agentConfig.apiKey}`);
|
|
239
|
+
lines.push(`MYVILLAGE_AGENT_CLIENT_ID=${agentConfig.clientId}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
236
242
|
lines.push('ANTHROPIC_API_KEY=');
|
|
237
243
|
|
|
238
244
|
return lines.join('\n') + '\n';
|
|
@@ -254,6 +260,8 @@ function generateEnvExample(hasOAuth, includeMcp) {
|
|
|
254
260
|
lines.push('MYVILLAGEOS_MCP_URL=https://mcp.myvillageproject.ai');
|
|
255
261
|
}
|
|
256
262
|
|
|
263
|
+
lines.push('MYVILLAGE_AGENT_API_KEY=');
|
|
264
|
+
lines.push('MYVILLAGE_AGENT_CLIENT_ID=');
|
|
257
265
|
lines.push('ANTHROPIC_API_KEY=');
|
|
258
266
|
|
|
259
267
|
return lines.join('\n') + '\n';
|
package/src/utils/api.js
CHANGED
|
@@ -418,6 +418,44 @@ export async function listPostsByFilters(params = {}) {
|
|
|
418
418
|
return response.data;
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
+
// ── Client Agent Configs API (/api/client-agents/configs) ──
|
|
422
|
+
|
|
423
|
+
export async function registerClientAgent(data) {
|
|
424
|
+
const client = getPlatformClient();
|
|
425
|
+
const response = await client.post('/client-agents/configs', data);
|
|
426
|
+
return response.data;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export async function listClientAgentConfigs() {
|
|
430
|
+
const client = getPlatformClient();
|
|
431
|
+
const response = await client.get('/client-agents/configs');
|
|
432
|
+
return response.data;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export async function getClientAgentConfig(configId) {
|
|
436
|
+
const client = getPlatformClient();
|
|
437
|
+
const response = await client.get(`/client-agents/configs/${encodeURIComponent(configId)}`);
|
|
438
|
+
return response.data;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export async function updateClientAgentConfig(configId, data) {
|
|
442
|
+
const client = getPlatformClient();
|
|
443
|
+
const response = await client.patch(`/client-agents/configs/${encodeURIComponent(configId)}`, data);
|
|
444
|
+
return response.data;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export async function deactivateClientAgent(configId) {
|
|
448
|
+
const client = getPlatformClient();
|
|
449
|
+
const response = await client.delete(`/client-agents/configs/${encodeURIComponent(configId)}`);
|
|
450
|
+
return response.data;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export async function rotateClientAgentKey(configId) {
|
|
454
|
+
const client = getPlatformClient();
|
|
455
|
+
const response = await client.post(`/client-agents/configs/${encodeURIComponent(configId)}/rotate-key`);
|
|
456
|
+
return response.data;
|
|
457
|
+
}
|
|
458
|
+
|
|
421
459
|
// ── BizReqs API Client (/api/bizreqs) ───────────────────
|
|
422
460
|
|
|
423
461
|
export function getBizReqsClient() {
|
|
@@ -564,3 +602,144 @@ export async function submitGameForReview(gameId) {
|
|
|
564
602
|
const response = await client.put(`/games/${encodeURIComponent(gameId)}`, { status: 'SUBMITTED' });
|
|
565
603
|
return response.data;
|
|
566
604
|
}
|
|
605
|
+
|
|
606
|
+
// ── Village Agents (developer agents) ───────────────────
|
|
607
|
+
|
|
608
|
+
// AgentProfiles owned by the caller that aren't yet linked to a VillageAgent.
|
|
609
|
+
// Used by the CLI "attach existing network identity" picker.
|
|
610
|
+
export async function listMyUnlinkedAgentProfiles() {
|
|
611
|
+
const client = getNetworkClient();
|
|
612
|
+
const response = await client.get('/agents/my', { params: { unlinked: 'true' } });
|
|
613
|
+
return response.data;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// Create a VillageAgent. Optionally either:
|
|
617
|
+
// - link to an existing AgentProfile via `agentProfileId`
|
|
618
|
+
// - auto-create a fresh AgentProfile via `createNetworkIdentity: true`
|
|
619
|
+
export async function createVillageAgent(data) {
|
|
620
|
+
const client = getPlatformClient();
|
|
621
|
+
const response = await client.post('/village-agents', data);
|
|
622
|
+
return response.data;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export async function listVillageAgents() {
|
|
626
|
+
const client = getPlatformClient();
|
|
627
|
+
const response = await client.get('/village-agents');
|
|
628
|
+
return response.data;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
export async function getVillageAgent(id) {
|
|
632
|
+
const client = getPlatformClient();
|
|
633
|
+
const response = await client.get(`/village-agents/${encodeURIComponent(id)}`);
|
|
634
|
+
return response.data;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
export async function updateVillageAgent(id, data) {
|
|
638
|
+
const client = getPlatformClient();
|
|
639
|
+
const response = await client.patch(`/village-agents/${encodeURIComponent(id)}`, data);
|
|
640
|
+
return response.data;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// ── Agent Task Queue ────────────────────────────────────
|
|
644
|
+
|
|
645
|
+
export async function listAgentTasks(villageAgentId, params = {}) {
|
|
646
|
+
const client = getPlatformClient();
|
|
647
|
+
const response = await client.get(
|
|
648
|
+
`/village-agents/${encodeURIComponent(villageAgentId)}/tasks`,
|
|
649
|
+
{ params },
|
|
650
|
+
);
|
|
651
|
+
return response.data;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
export async function assignAgentTask(villageAgentId, data) {
|
|
655
|
+
const client = getPlatformClient();
|
|
656
|
+
const response = await client.post(
|
|
657
|
+
`/village-agents/${encodeURIComponent(villageAgentId)}/tasks`,
|
|
658
|
+
data,
|
|
659
|
+
);
|
|
660
|
+
return response.data;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
export async function claimAgentTask(villageAgentId, taskId) {
|
|
664
|
+
const client = getPlatformClient();
|
|
665
|
+
const response = await client.post(
|
|
666
|
+
`/village-agents/${encodeURIComponent(villageAgentId)}/tasks/${encodeURIComponent(taskId)}/claim`,
|
|
667
|
+
);
|
|
668
|
+
return response.data;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// completeAgentTask is also used for "fail" — pass `errorMessage` to mark FAILED,
|
|
672
|
+
// otherwise the task is marked COMPLETED with the given `output`.
|
|
673
|
+
export async function completeAgentTask(villageAgentId, taskId, data = {}) {
|
|
674
|
+
const client = getPlatformClient();
|
|
675
|
+
const response = await client.post(
|
|
676
|
+
`/village-agents/${encodeURIComponent(villageAgentId)}/tasks/${encodeURIComponent(taskId)}/complete`,
|
|
677
|
+
data,
|
|
678
|
+
);
|
|
679
|
+
return response.data;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Retry a single FAILED or CANCELLED task: resets it to PENDING so the
|
|
683
|
+
// agent daemon re-claims it on the next polling iteration.
|
|
684
|
+
export async function retryAgentTask(villageAgentId, taskId) {
|
|
685
|
+
const client = getPlatformClient();
|
|
686
|
+
const response = await client.post(
|
|
687
|
+
`/village-agents/${encodeURIComponent(villageAgentId)}/tasks/${encodeURIComponent(taskId)}/retry`,
|
|
688
|
+
);
|
|
689
|
+
return response.data;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Bulk-retry every FAILED task for an agent. Optional `errorPattern`
|
|
693
|
+
// filters by errorMessage substring (case-insensitive).
|
|
694
|
+
export async function retryFailedAgentTasks(villageAgentId, errorPattern) {
|
|
695
|
+
const client = getPlatformClient();
|
|
696
|
+
const body = errorPattern ? { errorPattern } : {};
|
|
697
|
+
const response = await client.post(
|
|
698
|
+
`/village-agents/${encodeURIComponent(villageAgentId)}/tasks/retry-failed`,
|
|
699
|
+
body,
|
|
700
|
+
);
|
|
701
|
+
return response.data;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// ── Wisdom (VillageBooks repurposed as agent skill packs) ──────────
|
|
705
|
+
|
|
706
|
+
export async function listVillageBooks(params = {}) {
|
|
707
|
+
const client = getPlatformClient();
|
|
708
|
+
const response = await client.get('/village-books', { params });
|
|
709
|
+
return response.data;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Returns the raw .wisdom file text (YAML frontmatter + markdown body).
|
|
713
|
+
export async function exportVillageBook(id) {
|
|
714
|
+
const client = getPlatformClient();
|
|
715
|
+
const response = await client.get(
|
|
716
|
+
`/village-books/${encodeURIComponent(id)}/export`,
|
|
717
|
+
{ responseType: 'text', transformResponse: (data) => data },
|
|
718
|
+
);
|
|
719
|
+
return response.data;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
export async function importVillageBook(payload) {
|
|
723
|
+
const client = getPlatformClient();
|
|
724
|
+
const response = await client.post('/village-books/import', payload);
|
|
725
|
+
return response.data;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// ── Knowledge (long-term agent memory) ──────────────────
|
|
729
|
+
|
|
730
|
+
// Filter / search knowledge submissions. Pass `createdByAgentId` to narrow
|
|
731
|
+
// to a specific agent's own memories.
|
|
732
|
+
export async function listKnowledgeFiltered(params = {}) {
|
|
733
|
+
const client = getPlatformClient();
|
|
734
|
+
const response = await client.get('/knowledge/filtered', { params });
|
|
735
|
+
return response.data;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Write a memory by submitting a Knowledge entry attributed to the agent.
|
|
739
|
+
// Backed by /api/agent-tools/share-knowledge — the agent_profile_id override
|
|
740
|
+
// makes the entry appear in this specific agent's recall results.
|
|
741
|
+
export async function shareKnowledgeAsAgent(data) {
|
|
742
|
+
const client = getPlatformClient();
|
|
743
|
+
const response = await client.post('/agent-tools/share-knowledge', data);
|
|
744
|
+
return response.data;
|
|
745
|
+
}
|