@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
|
@@ -12,6 +12,20 @@ import {
|
|
|
12
12
|
agentJoinCommunity as apiAgentJoinCommunity,
|
|
13
13
|
listCommunities,
|
|
14
14
|
getAgentActivity,
|
|
15
|
+
listMyUnlinkedAgentProfiles,
|
|
16
|
+
createVillageAgent as apiCreateVillageAgent,
|
|
17
|
+
listAgentTasks,
|
|
18
|
+
assignAgentTask,
|
|
19
|
+
claimAgentTask,
|
|
20
|
+
completeAgentTask,
|
|
21
|
+
upsertAgentMemory,
|
|
22
|
+
listAgentMemory,
|
|
23
|
+
getAgentMemoryEntry,
|
|
24
|
+
deleteAgentMemoryEntry,
|
|
25
|
+
listKnowledgeFiltered,
|
|
26
|
+
shareKnowledgeAsAgent,
|
|
27
|
+
retryAgentTask,
|
|
28
|
+
retryFailedAgentTasks,
|
|
15
29
|
} from '../utils/api.js';
|
|
16
30
|
import {
|
|
17
31
|
getAgentDir,
|
|
@@ -39,6 +53,36 @@ export async function agentCreateLocalCommand() {
|
|
|
39
53
|
}
|
|
40
54
|
|
|
41
55
|
try {
|
|
56
|
+
// Optional: let the developer reuse an existing AgentProfile (network identity)
|
|
57
|
+
// they already own and haven't yet linked to a VillageAgent.
|
|
58
|
+
let preselectedProfile = null;
|
|
59
|
+
try {
|
|
60
|
+
const result = await listMyUnlinkedAgentProfiles();
|
|
61
|
+
const profiles = Array.isArray(result?.data) ? result.data : [];
|
|
62
|
+
if (profiles.length > 0) {
|
|
63
|
+
const { useExisting } = await inquirer.prompt([{
|
|
64
|
+
type: 'confirm',
|
|
65
|
+
name: 'useExisting',
|
|
66
|
+
message: `You have ${profiles.length} unlinked network identity(ies). Reuse one for this agent?`,
|
|
67
|
+
default: false,
|
|
68
|
+
}]);
|
|
69
|
+
if (useExisting) {
|
|
70
|
+
const { profileId } = await inquirer.prompt([{
|
|
71
|
+
type: 'list',
|
|
72
|
+
name: 'profileId',
|
|
73
|
+
message: 'Pick a network identity:',
|
|
74
|
+
choices: profiles.map(p => ({
|
|
75
|
+
name: `@${p.handle} — ${p.displayName}`,
|
|
76
|
+
value: p.id,
|
|
77
|
+
})),
|
|
78
|
+
}]);
|
|
79
|
+
preselectedProfile = profiles.find(p => p.id === profileId) || null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// listMyUnlinkedAgentProfiles can fail if the user has no villager — that's fine, just skip
|
|
84
|
+
}
|
|
85
|
+
|
|
42
86
|
const answers = await inquirer.prompt([
|
|
43
87
|
{
|
|
44
88
|
type: 'input',
|
|
@@ -151,6 +195,16 @@ export async function agentCreateLocalCommand() {
|
|
|
151
195
|
model: answers.model,
|
|
152
196
|
});
|
|
153
197
|
|
|
198
|
+
// If the developer picked an existing AgentProfile, pre-fill man.agent_id
|
|
199
|
+
// so `start` skips the lazy-create step and links the VillageAgent shim to it instead.
|
|
200
|
+
if (preselectedProfile?.id) {
|
|
201
|
+
const cfg = readAgentConfig(answers.name);
|
|
202
|
+
cfg.man = cfg.man || {};
|
|
203
|
+
cfg.man.agent_id = preselectedProfile.id;
|
|
204
|
+
cfg.man.handle = preselectedProfile.handle;
|
|
205
|
+
writeAgentConfig(answers.name, cfg);
|
|
206
|
+
}
|
|
207
|
+
|
|
154
208
|
spinner.succeed('Agent scaffolded!');
|
|
155
209
|
|
|
156
210
|
console.log(brand.green(` \u2713 Agent created at ~/.myvillage/agents/${answers.name}/\n`));
|
|
@@ -231,7 +285,7 @@ export async function agentStartCommand(name) {
|
|
|
231
285
|
}
|
|
232
286
|
}
|
|
233
287
|
|
|
234
|
-
// Register on MAN if first start
|
|
288
|
+
// Register on MAN if first start (skipped when `create` preselected an existing AgentProfile)
|
|
235
289
|
if (!agentConfig.man?.agent_id) {
|
|
236
290
|
const regSpinner = villageSpinner('Registering agent on the MAN network...').start();
|
|
237
291
|
try {
|
|
@@ -251,6 +305,32 @@ export async function agentStartCommand(name) {
|
|
|
251
305
|
|
|
252
306
|
regSpinner.succeed(`Registered as @${agent.handle || agentConfig.name} on MAN.`);
|
|
253
307
|
|
|
308
|
+
// Also create the VillageAgent shim so this agent has a task queue
|
|
309
|
+
// and shared autonomous-engagement controls. This is what `loop.js`
|
|
310
|
+
// polls against. Failure is non-fatal — the agent can still run locally.
|
|
311
|
+
try {
|
|
312
|
+
const shimResult = await apiCreateVillageAgent({
|
|
313
|
+
name: agentConfig.display_name || agentConfig.name,
|
|
314
|
+
title: 'Developer Agent',
|
|
315
|
+
description: agentConfig.description || '',
|
|
316
|
+
avatar: agent.avatarUrl || '',
|
|
317
|
+
archetypes: [],
|
|
318
|
+
greeting: '',
|
|
319
|
+
starterPrompts: [],
|
|
320
|
+
associatedVillages: [],
|
|
321
|
+
specialInstructions: null,
|
|
322
|
+
agentProfileId: agent.id,
|
|
323
|
+
});
|
|
324
|
+
const shim = shimResult.agent || shimResult;
|
|
325
|
+
if (shim?.id) {
|
|
326
|
+
agentConfig.man.village_agent_id = shim.id;
|
|
327
|
+
writeAgentConfig(name, agentConfig);
|
|
328
|
+
}
|
|
329
|
+
} catch (shimErr) {
|
|
330
|
+
const shimMsg = shimErr.response?.data?.error || shimErr.message;
|
|
331
|
+
console.log(brand.teal(` Note: task queue not available (${shimMsg}). Agent will run in feed-monitoring mode.`));
|
|
332
|
+
}
|
|
333
|
+
|
|
254
334
|
// Auto-join default communities so the agent can post
|
|
255
335
|
try {
|
|
256
336
|
const commResult = await listCommunities({ pageSize: 50 });
|
|
@@ -284,6 +364,34 @@ export async function agentStartCommand(name) {
|
|
|
284
364
|
}
|
|
285
365
|
}
|
|
286
366
|
|
|
367
|
+
// If we have an AgentProfile but no VillageAgent shim yet (e.g., the developer
|
|
368
|
+
// preselected an existing AgentProfile via `create`), create it now.
|
|
369
|
+
if (agentConfig.man?.agent_id && !agentConfig.man?.village_agent_id) {
|
|
370
|
+
try {
|
|
371
|
+
const shimResult = await apiCreateVillageAgent({
|
|
372
|
+
name: agentConfig.display_name || agentConfig.name,
|
|
373
|
+
title: 'Developer Agent',
|
|
374
|
+
description: agentConfig.description || '',
|
|
375
|
+
avatar: '',
|
|
376
|
+
archetypes: [],
|
|
377
|
+
greeting: '',
|
|
378
|
+
starterPrompts: [],
|
|
379
|
+
associatedVillages: [],
|
|
380
|
+
specialInstructions: null,
|
|
381
|
+
agentProfileId: agentConfig.man.agent_id,
|
|
382
|
+
});
|
|
383
|
+
const shim = shimResult.agent || shimResult;
|
|
384
|
+
if (shim?.id) {
|
|
385
|
+
agentConfig.man.village_agent_id = shim.id;
|
|
386
|
+
writeAgentConfig(name, agentConfig);
|
|
387
|
+
console.log(brand.teal(` Task queue ready: ${shim.id}`));
|
|
388
|
+
}
|
|
389
|
+
} catch (shimErr) {
|
|
390
|
+
const shimMsg = shimErr.response?.data?.error || shimErr.message;
|
|
391
|
+
console.log(brand.teal(` Note: task queue not available (${shimMsg}).`));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
287
395
|
// Migrate tools.yaml: replace man-feed with myvillage MCP server
|
|
288
396
|
const toolsConfig = readToolsYaml(name);
|
|
289
397
|
if (toolsConfig.servers?.['man-feed'] && !toolsConfig.servers?.['myvillage']) {
|
|
@@ -300,6 +408,9 @@ export async function agentStartCommand(name) {
|
|
|
300
408
|
if (agentConfig.man?.agent_id) {
|
|
301
409
|
process.env.MYVILLAGE_AGENT_ID = agentConfig.man.agent_id;
|
|
302
410
|
}
|
|
411
|
+
if (agentConfig.man?.village_agent_id) {
|
|
412
|
+
process.env.MYVILLAGE_VILLAGE_AGENT_ID = agentConfig.man.village_agent_id;
|
|
413
|
+
}
|
|
303
414
|
|
|
304
415
|
// Fork the daemon process
|
|
305
416
|
const spinner = villageSpinner(`Starting agent "${name}"...`).start();
|
|
@@ -670,3 +781,286 @@ export async function agentDeleteLocalCommand(name) {
|
|
|
670
781
|
console.log(chalk.red(` \u2717 Failed to delete agent: ${err.message}\n`));
|
|
671
782
|
}
|
|
672
783
|
}
|
|
784
|
+
|
|
785
|
+
// \u2500\u2500 Tasks \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\u2500
|
|
786
|
+
|
|
787
|
+
function resolveVillageAgentId(name) {
|
|
788
|
+
if (!agentExists(name)) {
|
|
789
|
+
console.log(chalk.red(` \u2717 Agent "${name}" not found.\n`));
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
const config = readAgentConfig(name);
|
|
793
|
+
const id = config?.man?.village_agent_id;
|
|
794
|
+
if (!id) {
|
|
795
|
+
console.log(chalk.yellow(` Agent "${name}" has no task queue yet \u2014 start it once so the VillageAgent shim is created.\n`));
|
|
796
|
+
return null;
|
|
797
|
+
}
|
|
798
|
+
return id;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
export async function agentTaskListCommand(name, options = {}) {
|
|
802
|
+
if (!isAuthenticated()) {
|
|
803
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
const villageAgentId = resolveVillageAgentId(name);
|
|
807
|
+
if (!villageAgentId) return;
|
|
808
|
+
|
|
809
|
+
try {
|
|
810
|
+
const result = await listAgentTasks(villageAgentId, {
|
|
811
|
+
status: options.status,
|
|
812
|
+
limit: options.limit ? Number(options.limit) : undefined,
|
|
813
|
+
});
|
|
814
|
+
const tasks = result.tasks || [];
|
|
815
|
+
if (tasks.length === 0) {
|
|
816
|
+
console.log(brand.teal(` No tasks${options.status ? ` with status ${options.status}` : ''}.\n`));
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
console.log(brand.teal(` ${tasks.length} task(s):\n`));
|
|
820
|
+
for (const t of tasks) {
|
|
821
|
+
console.log(` ${brand.gold(t.id)} ${chalk.bold(t.taskType)} ${t.status} priority=${t.priority}`);
|
|
822
|
+
if (t.instruction) console.log(` ${chalk.dim(t.instruction)}`);
|
|
823
|
+
}
|
|
824
|
+
console.log('');
|
|
825
|
+
} catch (err) {
|
|
826
|
+
const msg = err.response?.data?.error || err.message;
|
|
827
|
+
console.log(chalk.red(` \u2717 Failed to list tasks: ${msg}\n`));
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
export async function agentTaskAssignCommand(name, options = {}) {
|
|
832
|
+
if (!isAuthenticated()) {
|
|
833
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
const villageAgentId = resolveVillageAgentId(name);
|
|
837
|
+
if (!villageAgentId) return;
|
|
838
|
+
|
|
839
|
+
if (!options.type) {
|
|
840
|
+
console.log(chalk.red(' \u2717 --type is required (e.g., CLIENT_TASK, GENERATE_POST, SHARE_KNOWLEDGE)\n'));
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
let inputObj = {};
|
|
845
|
+
if (options.input) {
|
|
846
|
+
try { inputObj = JSON.parse(options.input); }
|
|
847
|
+
catch { console.log(chalk.red(' \u2717 --input must be valid JSON\n')); return; }
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
const result = await assignAgentTask(villageAgentId, {
|
|
852
|
+
taskType: options.type,
|
|
853
|
+
instruction: options.instruction,
|
|
854
|
+
input: inputObj,
|
|
855
|
+
priority: options.priority ? Number(options.priority) : 5,
|
|
856
|
+
});
|
|
857
|
+
const task = result.task || result;
|
|
858
|
+
console.log(brand.green(` \u2713 Task ${task.id} assigned (${task.taskType}, status=${task.status}).\n`));
|
|
859
|
+
} catch (err) {
|
|
860
|
+
const msg = err.response?.data?.error || err.message;
|
|
861
|
+
console.log(chalk.red(` \u2717 Failed to assign task: ${msg}\n`));
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Exported for use by the runtime loop, not registered as a CLI command directly.
|
|
866
|
+
export async function pollAndClaimNextTask(villageAgentId) {
|
|
867
|
+
const result = await listAgentTasks(villageAgentId, { status: 'PENDING', limit: 5 });
|
|
868
|
+
const pending = result.tasks || [];
|
|
869
|
+
if (pending.length === 0) return null;
|
|
870
|
+
for (const task of pending) {
|
|
871
|
+
try {
|
|
872
|
+
const claim = await claimAgentTask(villageAgentId, task.id);
|
|
873
|
+
return claim.data || claim;
|
|
874
|
+
} catch {
|
|
875
|
+
// Race lost \u2014 try the next task
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
export async function reportTaskOutcome(villageAgentId, taskId, outcome) {
|
|
882
|
+
return completeAgentTask(villageAgentId, taskId, outcome);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
export async function agentTaskRetryCommand(name, taskId) {
|
|
886
|
+
if (!isAuthenticated()) {
|
|
887
|
+
console.log(chalk.red(' ✗ Authentication required. Run \'myvillage login\' first.'));
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const villageAgentId = resolveVillageAgentId(name);
|
|
891
|
+
if (!villageAgentId) return;
|
|
892
|
+
if (!taskId) {
|
|
893
|
+
console.log(chalk.red(' ✗ Usage: myvillage agent task-retry <name> <taskId>\n'));
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
try {
|
|
898
|
+
await retryAgentTask(villageAgentId, taskId);
|
|
899
|
+
console.log(brand.green(` ✓ Task ${taskId} reset to PENDING. Agent will re-claim on next poll.\n`));
|
|
900
|
+
} catch (err) {
|
|
901
|
+
const msg = err.response?.data?.error || err.message;
|
|
902
|
+
console.log(chalk.red(` ✗ Retry failed: ${msg}\n`));
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
export async function agentTaskRetryFailedCommand(name, options = {}) {
|
|
907
|
+
if (!isAuthenticated()) {
|
|
908
|
+
console.log(chalk.red(' ✗ Authentication required. Run \'myvillage login\' first.'));
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
const villageAgentId = resolveVillageAgentId(name);
|
|
912
|
+
if (!villageAgentId) return;
|
|
913
|
+
|
|
914
|
+
try {
|
|
915
|
+
const result = await retryFailedAgentTasks(villageAgentId, options.filter);
|
|
916
|
+
const filterNote = options.filter ? chalk.dim(` (filter: ${options.filter})`) : '';
|
|
917
|
+
console.log(brand.green(` ✓ ${result.retried} task(s) reset to PENDING${filterNote}.\n`));
|
|
918
|
+
} catch (err) {
|
|
919
|
+
const msg = err.response?.data?.error || err.message;
|
|
920
|
+
console.log(chalk.red(` ✗ Bulk retry failed: ${msg}\n`));
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// ── Memory (short-term KV) and Recall (long-term searchable) ────
|
|
925
|
+
|
|
926
|
+
function resolveAgentProfileId(name) {
|
|
927
|
+
if (!agentExists(name)) {
|
|
928
|
+
console.log(chalk.red(` ✗ Agent "${name}" not found.\n`));
|
|
929
|
+
return null;
|
|
930
|
+
}
|
|
931
|
+
const config = readAgentConfig(name);
|
|
932
|
+
const id = config?.man?.agent_id;
|
|
933
|
+
if (!id) {
|
|
934
|
+
console.log(chalk.yellow(` Agent "${name}" hasn't registered on the network yet — start it once so an AgentProfile is created.\n`));
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
return id;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
export async function agentMemoryCommand(name, action, ...args) {
|
|
941
|
+
if (!isAuthenticated()) {
|
|
942
|
+
console.log(chalk.red(' ✗ Authentication required. Run \'myvillage login\' first.'));
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
const agentProfileId = resolveAgentProfileId(name);
|
|
946
|
+
if (!agentProfileId) return;
|
|
947
|
+
|
|
948
|
+
try {
|
|
949
|
+
if (action === 'list') {
|
|
950
|
+
const result = await listAgentMemory(agentProfileId);
|
|
951
|
+
const items = result.data || result.memories || result;
|
|
952
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
953
|
+
console.log(brand.teal(' No memory entries.\n'));
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
console.log(brand.teal(` ${items.length} memory entr${items.length === 1 ? 'y' : 'ies'}:\n`));
|
|
957
|
+
for (const m of items) {
|
|
958
|
+
console.log(` ${chalk.bold(m.key)}${m.category ? chalk.dim(` [${m.category}]`) : ''}`);
|
|
959
|
+
const v = typeof m.value === 'string' ? m.value : JSON.stringify(m.value);
|
|
960
|
+
console.log(` ${chalk.dim(v.slice(0, 200))}`);
|
|
961
|
+
}
|
|
962
|
+
console.log('');
|
|
963
|
+
} else if (action === 'get') {
|
|
964
|
+
const key = args[0];
|
|
965
|
+
if (!key) {
|
|
966
|
+
console.log(chalk.red(' ✗ Usage: myvillage agent memory <name> get <key>\n'));
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
const result = await getAgentMemoryEntry(agentProfileId, key);
|
|
970
|
+
console.log(JSON.stringify(result.data || result, null, 2));
|
|
971
|
+
} else if (action === 'set') {
|
|
972
|
+
const [key, ...rest] = args;
|
|
973
|
+
const value = rest.join(' ');
|
|
974
|
+
if (!key || !value) {
|
|
975
|
+
console.log(chalk.red(' ✗ Usage: myvillage agent memory <name> set <key> <value>\n'));
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
// Try JSON first so { "foo": 1 } stays structured; fall back to string.
|
|
979
|
+
let parsed;
|
|
980
|
+
try { parsed = JSON.parse(value); } catch { parsed = value; }
|
|
981
|
+
await upsertAgentMemory(agentProfileId, { key, value: parsed });
|
|
982
|
+
console.log(brand.green(` ✓ ${key} set.\n`));
|
|
983
|
+
} else if (action === 'delete') {
|
|
984
|
+
const key = args[0];
|
|
985
|
+
if (!key) {
|
|
986
|
+
console.log(chalk.red(' ✗ Usage: myvillage agent memory <name> delete <key>\n'));
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
await deleteAgentMemoryEntry(agentProfileId, key);
|
|
990
|
+
console.log(brand.green(` ✓ ${key} deleted.\n`));
|
|
991
|
+
} else {
|
|
992
|
+
console.log(chalk.red(` ✗ Unknown memory action: ${action}. Use list|get|set|delete.\n`));
|
|
993
|
+
}
|
|
994
|
+
} catch (err) {
|
|
995
|
+
const msg = err.response?.data?.error || err.message;
|
|
996
|
+
console.log(chalk.red(` ✗ Memory ${action} failed: ${msg}\n`));
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
export async function agentRecallCommand(name, query, options = {}) {
|
|
1001
|
+
if (!isAuthenticated()) {
|
|
1002
|
+
console.log(chalk.red(' ✗ Authentication required. Run \'myvillage login\' first.'));
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
const agentProfileId = resolveAgentProfileId(name);
|
|
1006
|
+
if (!agentProfileId) return;
|
|
1007
|
+
if (!query) {
|
|
1008
|
+
console.log(chalk.red(' ✗ Usage: myvillage agent recall <name> <query>\n'));
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
try {
|
|
1013
|
+
const result = await listKnowledgeFiltered({
|
|
1014
|
+
createdByAgentId: agentProfileId,
|
|
1015
|
+
search: query,
|
|
1016
|
+
limit: options.limit ? Number(options.limit) : 10,
|
|
1017
|
+
});
|
|
1018
|
+
const items = result.data || result.knowledge || result.knowledgeSubmissions || [];
|
|
1019
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
1020
|
+
console.log(brand.teal(` No memories matched "${query}".\n`));
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
console.log(brand.teal(` ${items.length} matching memor${items.length === 1 ? 'y' : 'ies'}:\n`));
|
|
1024
|
+
for (const k of items) {
|
|
1025
|
+
const text = k.originalText || k.text || '';
|
|
1026
|
+
console.log(` ${brand.gold(k.id)} ${chalk.dim(new Date(k.createdAt).toLocaleString())}`);
|
|
1027
|
+
console.log(` ${text.slice(0, 280)}${text.length > 280 ? '…' : ''}`);
|
|
1028
|
+
}
|
|
1029
|
+
console.log('');
|
|
1030
|
+
} catch (err) {
|
|
1031
|
+
const msg = err.response?.data?.error || err.message;
|
|
1032
|
+
console.log(chalk.red(` ✗ Recall failed: ${msg}\n`));
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
export async function agentRememberCommand(name, text, options = {}) {
|
|
1037
|
+
if (!isAuthenticated()) {
|
|
1038
|
+
console.log(chalk.red(' ✗ Authentication required. Run \'myvillage login\' first.'));
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
const agentProfileId = resolveAgentProfileId(name);
|
|
1042
|
+
if (!agentProfileId) return;
|
|
1043
|
+
if (!text) {
|
|
1044
|
+
console.log(chalk.red(' ✗ Usage: myvillage agent remember <name> "<text to remember>"\n'));
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const themes = options.themes
|
|
1049
|
+
? options.themes.split(',').map(t => t.trim()).filter(Boolean)
|
|
1050
|
+
: [];
|
|
1051
|
+
|
|
1052
|
+
try {
|
|
1053
|
+
const result = await shareKnowledgeAsAgent({
|
|
1054
|
+
original_text: text,
|
|
1055
|
+
summary: options.summary || null,
|
|
1056
|
+
themes,
|
|
1057
|
+
sharing_option: options.sharing || 'PRIVATE',
|
|
1058
|
+
agent_profile_id: agentProfileId,
|
|
1059
|
+
});
|
|
1060
|
+
const k = result.knowledge || result.data || result;
|
|
1061
|
+
console.log(brand.green(` ✓ Remembered as ${k.id || 'knowledge entry'}.\n`));
|
|
1062
|
+
} catch (err) {
|
|
1063
|
+
const msg = err.response?.data?.error || err.message;
|
|
1064
|
+
console.log(chalk.red(` ✗ Remember failed: ${msg}\n`));
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
@@ -7,7 +7,7 @@ import inquirer from 'inquirer';
|
|
|
7
7
|
import { isAuthenticated } from '../utils/auth.js';
|
|
8
8
|
import { createGameCommand } from './create-game.js';
|
|
9
9
|
import { createAgenticAppProject } from '../utils/agentic-templates.js';
|
|
10
|
-
import { registerOAuthClient } from '../utils/api.js';
|
|
10
|
+
import { registerOAuthClient, registerClientAgent, listMyAgents } from '../utils/api.js';
|
|
11
11
|
|
|
12
12
|
export async function createCommand() {
|
|
13
13
|
// Check authentication
|
|
@@ -160,6 +160,65 @@ async function applicationFlow() {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
// Client agent automation (optional)
|
|
164
|
+
let agentConfig = null;
|
|
165
|
+
if (hasOAuth && oauthCredentials) {
|
|
166
|
+
const { enableAgent } = await inquirer.prompt([{
|
|
167
|
+
type: 'confirm',
|
|
168
|
+
name: 'enableAgent',
|
|
169
|
+
message: 'Enable platform agent automation?',
|
|
170
|
+
default: false,
|
|
171
|
+
}]);
|
|
172
|
+
|
|
173
|
+
if (enableAgent) {
|
|
174
|
+
try {
|
|
175
|
+
const agentResult = await listMyAgents();
|
|
176
|
+
const agents = agentResult.data || agentResult;
|
|
177
|
+
|
|
178
|
+
if (Array.isArray(agents) && agents.length > 0) {
|
|
179
|
+
const { selectedAgent, workflowTypes } = await inquirer.prompt([
|
|
180
|
+
{
|
|
181
|
+
type: 'list',
|
|
182
|
+
name: 'selectedAgent',
|
|
183
|
+
message: 'Which agent should automate this app?',
|
|
184
|
+
choices: agents.map(a => ({ name: `@${a.handle} — ${a.displayName || a.handle}`, value: a })),
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
type: 'checkbox',
|
|
188
|
+
name: 'workflowTypes',
|
|
189
|
+
message: 'Select workflow types:',
|
|
190
|
+
choices: [
|
|
191
|
+
{ name: 'Submission Processor', value: 'SUBMISSION_PROCESSOR', checked: true },
|
|
192
|
+
{ name: 'Digest Generator', value: 'DIGEST_GENERATOR' },
|
|
193
|
+
{ name: 'Member Matcher', value: 'MEMBER_MATCHER' },
|
|
194
|
+
{ name: 'Stale Data Detector', value: 'STALE_DATA_DETECTOR' },
|
|
195
|
+
],
|
|
196
|
+
validate: input => input.length > 0 ? true : 'Select at least one workflow type.',
|
|
197
|
+
},
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
const agentSpinner = villageSpinner('Registering client agent...').start();
|
|
201
|
+
const regResult = await registerClientAgent({
|
|
202
|
+
villageAgentId: selectedAgent.villageAgent?.id || selectedAgent.villageAgentId || selectedAgent.id,
|
|
203
|
+
clientId: slug,
|
|
204
|
+
clientName: basics.name,
|
|
205
|
+
baseUrl: 'http://localhost:3000',
|
|
206
|
+
workflowType: workflowTypes[0],
|
|
207
|
+
});
|
|
208
|
+
agentConfig = { apiKey: regResult.apiKey, clientId: slug };
|
|
209
|
+
agentSpinner.succeed('Client agent registered!');
|
|
210
|
+
console.log(chalk.yellow.bold(' ⚠ Save your API key — it won\'t be shown again!'));
|
|
211
|
+
console.log(brand.teal(` API Key: ${brand.gold(regResult.apiKey)}`));
|
|
212
|
+
} else {
|
|
213
|
+
console.log(brand.teal(' No agents found. Create one first with: myvillage agent create'));
|
|
214
|
+
}
|
|
215
|
+
} catch (err) {
|
|
216
|
+
console.log(chalk.yellow(` ⚠ Failed to register client agent: ${err.response?.data?.error || err.message}`));
|
|
217
|
+
console.log(chalk.dim(' You can register later with: myvillage agent register-client'));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
163
222
|
// Scaffold project (always Next.js)
|
|
164
223
|
const spinner = villageSpinner('Creating application...').start();
|
|
165
224
|
try {
|
|
@@ -172,6 +231,7 @@ async function applicationFlow() {
|
|
|
172
231
|
includeRestApi,
|
|
173
232
|
mcpToolGroups,
|
|
174
233
|
oauthCredentials,
|
|
234
|
+
agentConfig,
|
|
175
235
|
});
|
|
176
236
|
|
|
177
237
|
spinner.text = 'Installing dependencies...';
|