heyio 0.23.0 → 0.24.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/dist/copilot/tools.js +4 -1
- package/dist/copilot/universes.js +63 -0
- package/package.json +1 -1
package/dist/copilot/tools.js
CHANGED
|
@@ -4,7 +4,7 @@ import { execSync, execFileSync } from "child_process";
|
|
|
4
4
|
import { readFileSync, writeFileSync, readdirSync, statSync, existsSync, mkdirSync } from "fs";
|
|
5
5
|
import { join, dirname, resolve } from "path";
|
|
6
6
|
import { homedir } from "os";
|
|
7
|
-
import { UNIVERSES, getOrCreateUniverse } from "./universes.js";
|
|
7
|
+
import { UNIVERSES, getOrCreateUniverse, generateUniverseRoster } from "./universes.js";
|
|
8
8
|
import { createFeedEntry } from "../store/feed.js";
|
|
9
9
|
import { validateCron, nextRun } from "./cron.js";
|
|
10
10
|
import { createIoSchedule, deleteIoSchedule, getIoSchedule, listIoSchedules, setIoScheduleEnabled, updateIoScheduleNextRun, } from "../store/io-schedules.js";
|
|
@@ -300,6 +300,9 @@ export function createTools(deps) {
|
|
|
300
300
|
}),
|
|
301
301
|
handler: async ({ slug, name, project_path, universe }) => {
|
|
302
302
|
try {
|
|
303
|
+
if (universe) {
|
|
304
|
+
await generateUniverseRoster(universe);
|
|
305
|
+
}
|
|
303
306
|
deps.createSquad(slug, name, project_path, universe);
|
|
304
307
|
const squad = deps.getSquad(slug);
|
|
305
308
|
const universeName = squad?.universe ? getOrCreateUniverse(squad.universe).name : "random";
|
|
@@ -378,6 +378,69 @@ export function getOrCreateUniverse(input) {
|
|
|
378
378
|
UNIVERSES.push(custom);
|
|
379
379
|
return custom;
|
|
380
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* Generate a universe roster using the LLM for unknown universes.
|
|
383
|
+
* Falls back to archetype characters if the LLM call fails.
|
|
384
|
+
* For known/well-known universes, returns them directly without an LLM call.
|
|
385
|
+
*/
|
|
386
|
+
export async function generateUniverseRoster(input) {
|
|
387
|
+
// First try synchronous resolution
|
|
388
|
+
const existing = getOrCreateUniverse(input);
|
|
389
|
+
// If it resolved to something other than archetypes, use it
|
|
390
|
+
const isArchetype = existing.characters[0]?.name === "Commander";
|
|
391
|
+
if (!isArchetype)
|
|
392
|
+
return existing;
|
|
393
|
+
// Use LLM to generate real characters for the unknown universe
|
|
394
|
+
let session;
|
|
395
|
+
try {
|
|
396
|
+
const { getClient } = await import("./client.js");
|
|
397
|
+
const { approveAll } = await import("@github/copilot-sdk");
|
|
398
|
+
const client = await getClient();
|
|
399
|
+
session = await client.createSession({
|
|
400
|
+
systemMessage: { mode: "replace", content: "You are a pop-culture expert. Generate character rosters for fictional universes. Respond ONLY with valid JSON, no markdown fencing." },
|
|
401
|
+
onPermissionRequest: approveAll,
|
|
402
|
+
});
|
|
403
|
+
const prompt = `Generate a roster of 8 characters from the universe "${input}". For each character provide their canonical name and a one-sentence personality description suitable for a software engineering team role.
|
|
404
|
+
|
|
405
|
+
Return ONLY a JSON object with this exact shape:
|
|
406
|
+
{"name":"<Universe Display Name>","tagline":"<iconic catchphrase or tagline>","characters":[{"name":"<Character Name>","personality":"<1-sentence personality description>"}]}
|
|
407
|
+
|
|
408
|
+
Use well-known, iconic characters from this universe. The personality should reflect the actual character's traits.`;
|
|
409
|
+
const response = await session.sendAndWait({ prompt }, 30_000);
|
|
410
|
+
const rawContent = response?.data?.content ?? "";
|
|
411
|
+
const jsonStr = rawContent.replace(/```json?\n?/g, "").replace(/```/g, "").trim();
|
|
412
|
+
const parsed = JSON.parse(jsonStr);
|
|
413
|
+
if (parsed?.characters?.length > 0) {
|
|
414
|
+
const slug = slugify(input);
|
|
415
|
+
const universe = {
|
|
416
|
+
id: slug,
|
|
417
|
+
name: parsed.name || input,
|
|
418
|
+
tagline: parsed.tagline || `Welcome to ${input}.`,
|
|
419
|
+
characters: parsed.characters.slice(0, 8).map((c) => ({
|
|
420
|
+
name: String(c.name),
|
|
421
|
+
personality: String(c.personality),
|
|
422
|
+
})),
|
|
423
|
+
};
|
|
424
|
+
// Replace the archetype entry with the real one
|
|
425
|
+
const idx = UNIVERSES.findIndex((u) => u.id === slug);
|
|
426
|
+
if (idx >= 0)
|
|
427
|
+
UNIVERSES.splice(idx, 1);
|
|
428
|
+
UNIVERSES.push(universe);
|
|
429
|
+
return universe;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
catch (err) {
|
|
433
|
+
console.error(`[io] Failed to generate universe roster for "${input}":`, err);
|
|
434
|
+
}
|
|
435
|
+
finally {
|
|
436
|
+
try {
|
|
437
|
+
await session?.destroy();
|
|
438
|
+
}
|
|
439
|
+
catch { /* best-effort cleanup */ }
|
|
440
|
+
}
|
|
441
|
+
// LLM failed — return the archetype fallback (already registered)
|
|
442
|
+
return existing;
|
|
443
|
+
}
|
|
381
444
|
/**
|
|
382
445
|
* Get a universe by ID.
|
|
383
446
|
*/
|