awesome-agents 0.1.5 → 0.1.6
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/CHANGELOG.md +9 -0
- package/README.md +34 -9
- package/docs/cli.md +28 -7
- package/docs/product/README.md +1 -1
- package/docs/product/command-model.md +13 -12
- package/docs/product/harness-targets.md +23 -1
- package/docs/product/product-scope.md +4 -4
- package/docs/product/profile-source-format.md +17 -0
- package/package.json +3 -2
- package/src/cli.js +140 -10
- package/src/constants.js +8 -4
- package/src/help.js +12 -10
- package/src/installer.js +99 -11
- package/src/renderers.js +111 -3
- package/src/skills.js +397 -0
- package/src/source.js +26 -4
package/src/help.js
CHANGED
|
@@ -188,13 +188,13 @@ const banner = [
|
|
|
188
188
|
color(" | (_| | (_| | __/ | | | |_\\__ \\ ", codes.bannerB),
|
|
189
189
|
color(" \\__,_|\\__, |\\___|_| |_|\\__|___/ ", codes.bannerA),
|
|
190
190
|
color(" |___/ ", codes.bannerA),
|
|
191
|
-
` ${ui.dim("Operational profiles for Codex, Claude Code, and
|
|
191
|
+
` ${ui.dim("Operational profiles for Codex, Claude Code, OpenCode, and tenex-edge")}`
|
|
192
192
|
];
|
|
193
193
|
|
|
194
194
|
const mainHelp = {
|
|
195
195
|
banner,
|
|
196
196
|
usage: `${PACKAGE_NAME} <command> [options]`,
|
|
197
|
-
description: "Install reusable operational agent profiles into Codex, Claude Code, and
|
|
197
|
+
description: "Install reusable operational agent profiles into Codex, Claude Code, OpenCode, and tenex-edge.",
|
|
198
198
|
sections: [
|
|
199
199
|
{
|
|
200
200
|
title: "Manage Profiles:",
|
|
@@ -270,7 +270,7 @@ const mainHelp = {
|
|
|
270
270
|
examples: [
|
|
271
271
|
{ command: `${PACKAGE_NAME} add ${exampleSource} --list`, comment: "inspect source profiles" },
|
|
272
272
|
{ command: `${PACKAGE_NAME} add ${exampleSource} --agent ${exampleProfile}` },
|
|
273
|
-
{ command: `${PACKAGE_NAME} add ${exampleSource} --agent ${exampleProfile} --harness
|
|
273
|
+
{ command: `${PACKAGE_NAME} add ${exampleSource} --agent ${exampleProfile} --harness tenex-edge` },
|
|
274
274
|
{ command: `${PACKAGE_NAME} add ${exampleSource} --all --dry-run`, comment: "preview every write" },
|
|
275
275
|
{ command: `${PACKAGE_NAME} use ${exampleSource}@${exampleProfile} --harness claude-code` },
|
|
276
276
|
{ command: `${PACKAGE_NAME} list --json`, comment: "machine-readable output" },
|
|
@@ -289,8 +289,8 @@ const commandHelp = {
|
|
|
289
289
|
title: "Install Flow:",
|
|
290
290
|
rows: [
|
|
291
291
|
"1. Read canonical definitions from agents/<slug>/agent.yaml or compatible variants.",
|
|
292
|
-
"2. Install agent-owned scripts and
|
|
293
|
-
"3. Render Codex, Claude Code, or
|
|
292
|
+
"2. Install agent-owned scripts, references, and declared skills into ~/.agents/homes/<slug>/.",
|
|
293
|
+
"3. Render Codex, Claude Code, OpenCode, or tenex-edge files and record them in the registry."
|
|
294
294
|
]
|
|
295
295
|
},
|
|
296
296
|
{
|
|
@@ -308,6 +308,7 @@ const commandHelp = {
|
|
|
308
308
|
{ term: ui.option("--agent triage-agent"), description: "Preferred shorthand: install one profile slug" },
|
|
309
309
|
{ term: ui.option("--profile triage-agent"), description: "Explicit profile selector" },
|
|
310
310
|
{ term: ui.option("--skill triage-agent"), description: "Compatibility alias for --profile" },
|
|
311
|
+
{ term: ui.option("(interactive)"), description: "Without a profile selector, choose source profiles from a checkbox list" },
|
|
311
312
|
{ term: ui.option("--all"), description: "Install every source profile" },
|
|
312
313
|
{ term: ui.option("--list"), description: "Show available profiles and exit" }
|
|
313
314
|
]
|
|
@@ -315,9 +316,9 @@ const commandHelp = {
|
|
|
315
316
|
{
|
|
316
317
|
title: "Choose Targets:",
|
|
317
318
|
rows: [
|
|
318
|
-
{ term: ui.option("(
|
|
319
|
+
{ term: ui.option("(auto-detect)"), description: "Every harness detected on PATH; interactive multi-detect opens a checked selector; pass --harness when none are detected" },
|
|
319
320
|
{ term: ui.option("--harness opencode"), description: "Render for one harness" },
|
|
320
|
-
{ term: ui.option("--harness codex
|
|
321
|
+
{ term: ui.option("--harness codex tenex-edge"), description: "Render for multiple harnesses" },
|
|
321
322
|
{ term: ui.option("--harness *"), description: "Render for every supported harness" },
|
|
322
323
|
{ term: ui.option("--global"), description: "Install into the user-level harness directory" },
|
|
323
324
|
{ term: ui.option("--project"), description: "Install project-local files where the harness supports it" }
|
|
@@ -329,6 +330,7 @@ const commandHelp = {
|
|
|
329
330
|
{ term: ui.command("codex"), description: `${ui.path("~/.codex/<profile>.config.toml")} (project-local Codex profiles are not supported)` },
|
|
330
331
|
{ term: ui.command("claude-code"), description: `${ui.path("~/.claude/agents/<profile>.md")} or ${ui.path(".claude/agents/<profile>.md")}` },
|
|
331
332
|
{ term: ui.command("opencode"), description: `${ui.path("~/.config/opencode/agents/<profile>.md")} or ${ui.path(".opencode/agents/<profile>.md")}` },
|
|
333
|
+
{ term: ui.command("tenex-edge"), description: `${ui.path("~/.tenex-edge/agents/<profile>.json")} (project-local tenex-edge agents are not supported)` },
|
|
332
334
|
{ term: ui.command("registry"), description: `${ui.path("~/.awesome-agents/installed.json")} or ${ui.path(".awesome-agents/installed.json")}` }
|
|
333
335
|
]
|
|
334
336
|
},
|
|
@@ -346,7 +348,7 @@ const commandHelp = {
|
|
|
346
348
|
examples: [
|
|
347
349
|
{ command: `${PACKAGE_NAME} add ${exampleSource} --list` },
|
|
348
350
|
{ command: `${PACKAGE_NAME} add ${exampleSource} --agent ${exampleProfile}` },
|
|
349
|
-
{ command: `${PACKAGE_NAME} add ${exampleSource} --agent ${exampleProfile} --harness codex
|
|
351
|
+
{ command: `${PACKAGE_NAME} add ${exampleSource} --agent ${exampleProfile} --harness codex tenex-edge` },
|
|
350
352
|
{ command: `${PACKAGE_NAME} add ${exampleSource} --all --dry-run` }
|
|
351
353
|
],
|
|
352
354
|
footer: `Generated files are marked with ${ui.profile("Generated by awesome-agents")}.`
|
|
@@ -449,14 +451,14 @@ const commandHelp = {
|
|
|
449
451
|
function installOptions({ includeHome = false } = {}) {
|
|
450
452
|
return compact([
|
|
451
453
|
{ term: ui.option("-g, --global"), description: "Install globally instead of into the current project" },
|
|
452
|
-
{ term: ui.option("-p, --project"), description: "Install into the current project; not supported for Codex profiles" },
|
|
454
|
+
{ term: ui.option("-p, --project"), description: "Install into the current project; not supported for Codex or tenex-edge profiles" },
|
|
453
455
|
{ term: ui.option("-a, --agent <profiles...>"), description: "Select agent profile slugs; harness names still work for compatibility" },
|
|
454
456
|
{ term: ui.option("--harness <harnesses...>"), description: `Select target harnesses: ${harnesses}, or *` },
|
|
455
457
|
{ term: ui.option("-s, --profile <profiles...>"), description: "Explicit profile selector; accepts *" },
|
|
456
458
|
{ term: ui.option("--skill <profiles...>"), description: "Compatibility alias for --profile" },
|
|
457
459
|
{ term: ui.option("-l, --list"), description: "List available source profiles without installing" },
|
|
458
460
|
{ term: ui.option("--all"), description: "Install all profiles to all supported harnesses" },
|
|
459
|
-
{ term: ui.option("-y, --yes"), description: "
|
|
461
|
+
{ term: ui.option("-y, --yes"), description: "Accept detected profile and harness selections without opening selectors" },
|
|
460
462
|
{ term: ui.option("--dry-run"), description: "Print planned installs without writing files" },
|
|
461
463
|
{ term: ui.option("--force"), description: "Allow overwriting files without the generated marker" },
|
|
462
464
|
{ term: ui.option("--json"), description: "Output JSON without ANSI color" },
|
package/src/installer.js
CHANGED
|
@@ -6,6 +6,7 @@ import { AGENT_ALIASES, HARNESS_COMMANDS, SUPPORTED_AGENTS } from "./constants.j
|
|
|
6
6
|
import { contentHash, isGeneratedContent, normalizeAgentList, renderForAgent, resolveTargetPath } from "./renderers.js";
|
|
7
7
|
import { expandHome, loadCatalog, materializeSource, splitSourceSpec } from "./source.js";
|
|
8
8
|
import { readRegistry, registryPath, removeInstall, upsertInstall, writeRegistry } from "./registry.js";
|
|
9
|
+
import { installProfileSkills } from "./skills.js";
|
|
9
10
|
|
|
10
11
|
export async function listAvailable(sourceInput, options = {}) {
|
|
11
12
|
const { source } = splitSourceSpec(sourceInput);
|
|
@@ -22,16 +23,13 @@ export async function installFromSource(sourceSpec, options = {}) {
|
|
|
22
23
|
const split = splitSourceSpec(sourceSpec);
|
|
23
24
|
const source = split.source;
|
|
24
25
|
const { selectors, harnessInput } = resolveInstallSelection(split, options);
|
|
25
|
-
const harnesses =
|
|
26
|
-
all: options.all,
|
|
27
|
-
defaultAgent: detectAvailableHarnesses()
|
|
28
|
-
});
|
|
26
|
+
const harnesses = await resolveHarnessesForInstall(harnessInput, options);
|
|
29
27
|
const scope = resolveScope(options, harnesses);
|
|
30
28
|
|
|
31
29
|
const materialized = await materializeSource(source, options);
|
|
32
30
|
try {
|
|
33
31
|
const catalog = await loadCatalog(materialized.path);
|
|
34
|
-
const profiles =
|
|
32
|
+
const profiles = await resolveProfilesForInstall(catalog.profiles, selectors, options.all, options.chooseProfiles, materialized.source);
|
|
35
33
|
const registryOptions = { ...options, scope };
|
|
36
34
|
const registry = await readRegistry(registryOptions);
|
|
37
35
|
const operations = [];
|
|
@@ -39,9 +37,18 @@ export async function installFromSource(sourceSpec, options = {}) {
|
|
|
39
37
|
|
|
40
38
|
for (const profile of profiles) {
|
|
41
39
|
const supportTargets = await installProfileSupport(profile, options);
|
|
40
|
+
const installedSkills = await installProfileSkills(profile, materialized.path, options);
|
|
41
|
+
const renderProfile = {
|
|
42
|
+
...profile,
|
|
43
|
+
installedSkills
|
|
44
|
+
};
|
|
42
45
|
for (const harness of harnesses) {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
46
|
+
const target = resolveTargetPath(renderProfile, harness, { ...options, scope });
|
|
47
|
+
const existingContent = await readExistingContent(target);
|
|
48
|
+
const content = renderForAgent(renderProfile, harness, {
|
|
49
|
+
source: materialized.source,
|
|
50
|
+
existingContent
|
|
51
|
+
});
|
|
45
52
|
await writeManagedFile(target, content, options);
|
|
46
53
|
|
|
47
54
|
const install = {
|
|
@@ -56,6 +63,8 @@ export async function installFromSource(sourceSpec, options = {}) {
|
|
|
56
63
|
installedAt,
|
|
57
64
|
contentSha256: contentHash(content),
|
|
58
65
|
supportTargets,
|
|
66
|
+
skillTargets: installedSkills.map((skill) => skill.path),
|
|
67
|
+
installedSkills,
|
|
59
68
|
dryRun: Boolean(options.dryRun)
|
|
60
69
|
};
|
|
61
70
|
|
|
@@ -91,9 +100,7 @@ export async function useFromSource(sourceSpec, options = {}) {
|
|
|
91
100
|
throw new Error("Specify a profile with source@profile or --profile <profile>.");
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
const harness =
|
|
95
|
-
defaultAgent: detectAvailableHarnesses()
|
|
96
|
-
})[0];
|
|
103
|
+
const harness = resolveHarnessForUse(harnessInput, options);
|
|
97
104
|
const materialized = await materializeSource(split.source, options);
|
|
98
105
|
try {
|
|
99
106
|
const catalog = await loadCatalog(materialized.path);
|
|
@@ -311,6 +318,8 @@ function runInstructionForOperation(operation, env) {
|
|
|
311
318
|
}
|
|
312
319
|
return {
|
|
313
320
|
profile: operation.profile,
|
|
321
|
+
name: operation.name,
|
|
322
|
+
summary: operation.summary,
|
|
314
323
|
harness: operation.harness,
|
|
315
324
|
command: `codex --profile ${shellWord(operation.profile)}`,
|
|
316
325
|
note: "Starts Codex with this installed profile."
|
|
@@ -323,6 +332,8 @@ function runInstructionForOperation(operation, env) {
|
|
|
323
332
|
}
|
|
324
333
|
return {
|
|
325
334
|
profile: operation.profile,
|
|
335
|
+
name: operation.name,
|
|
336
|
+
summary: operation.summary,
|
|
326
337
|
harness: operation.harness,
|
|
327
338
|
command: `claude --agent ${shellWord(operation.profile)}`,
|
|
328
339
|
note: "Starts Claude Code with this installed agent profile."
|
|
@@ -335,19 +346,77 @@ function runInstructionForOperation(operation, env) {
|
|
|
335
346
|
}
|
|
336
347
|
return {
|
|
337
348
|
profile: operation.profile,
|
|
349
|
+
name: operation.name,
|
|
350
|
+
summary: operation.summary,
|
|
338
351
|
harness: operation.harness,
|
|
339
352
|
command: "opencode",
|
|
340
353
|
note: `Start OpenCode, then invoke @${operation.profile} in the session.`
|
|
341
354
|
};
|
|
342
355
|
}
|
|
343
356
|
|
|
357
|
+
if (operation.harness === "tenex-edge") {
|
|
358
|
+
if (!commandExists("tenex-edge", env)) {
|
|
359
|
+
return undefined;
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
profile: operation.profile,
|
|
363
|
+
name: operation.name,
|
|
364
|
+
summary: operation.summary,
|
|
365
|
+
harness: operation.harness,
|
|
366
|
+
command: `tenex-edge launch ${shellWord(operation.profile)}`,
|
|
367
|
+
note: "Starts this profile as a tenex-edge-managed Claude Code agent."
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
344
371
|
return undefined;
|
|
345
372
|
}
|
|
346
373
|
|
|
374
|
+
async function resolveHarnessesForInstall(harnessInput, options = {}) {
|
|
375
|
+
if (options.all || harnessInput !== undefined) {
|
|
376
|
+
return normalizeAgentList(harnessInput, { all: options.all });
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const detected = detectAvailableHarnesses(options.env ?? process.env);
|
|
380
|
+
if (detected.length === 0) {
|
|
381
|
+
throw new Error(noDetectedHarnessMessage());
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (detected.length > 1 && typeof options.chooseHarnesses === "function") {
|
|
385
|
+
const chosen = await options.chooseHarnesses(detected);
|
|
386
|
+
const harnesses = normalizeAgentList(chosen);
|
|
387
|
+
if (harnesses.length === 0) {
|
|
388
|
+
throw new Error("Select at least one harness to install, or pass --harness <harness>.");
|
|
389
|
+
}
|
|
390
|
+
return harnesses;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return detected;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function resolveHarnessForUse(harnessInput, options = {}) {
|
|
397
|
+
const explicit = normalizeAgentList(harnessInput);
|
|
398
|
+
if (explicit.length > 0) {
|
|
399
|
+
return explicit[0];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const detected = detectAvailableHarnesses(options.env ?? process.env);
|
|
403
|
+
if (detected.length === 0) {
|
|
404
|
+
throw new Error(noDetectedHarnessMessage());
|
|
405
|
+
}
|
|
406
|
+
if (detected.length > 1) {
|
|
407
|
+
throw new Error(`Multiple harnesses detected (${detected.join(", ")}). Pass --harness <harness> because use renders one target at a time.`);
|
|
408
|
+
}
|
|
409
|
+
return detected[0];
|
|
410
|
+
}
|
|
411
|
+
|
|
347
412
|
function detectAvailableHarnesses(env = process.env) {
|
|
348
413
|
return SUPPORTED_AGENTS.filter((harness) => commandExists(HARNESS_COMMANDS.get(harness), env));
|
|
349
414
|
}
|
|
350
415
|
|
|
416
|
+
function noDetectedHarnessMessage() {
|
|
417
|
+
return `No supported harness CLI detected on PATH. Pass --harness <${SUPPORTED_AGENTS.join("|")}> to choose a target.`;
|
|
418
|
+
}
|
|
419
|
+
|
|
351
420
|
function commandExists(command, env) {
|
|
352
421
|
const pathValue = env.PATH ?? "";
|
|
353
422
|
if (!pathValue) {
|
|
@@ -372,6 +441,15 @@ function shellWord(value) {
|
|
|
372
441
|
return `'${text.replaceAll("'", "'\\''")}'`;
|
|
373
442
|
}
|
|
374
443
|
|
|
444
|
+
async function resolveProfilesForInstall(profiles, selectors, all, chooseProfiles, source) {
|
|
445
|
+
if (!all && selectors.length === 0 && typeof chooseProfiles === "function") {
|
|
446
|
+
const chosen = await chooseProfiles(profiles.map((profile) => profileSummary(profile, source)));
|
|
447
|
+
return selectProfiles(profiles, normalizeProfileSelectors(chosen), false);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return selectProfiles(profiles, selectors, all);
|
|
451
|
+
}
|
|
452
|
+
|
|
375
453
|
function selectProfiles(profiles, selectors, all = false) {
|
|
376
454
|
if (all || selectors.length === 0 || selectors.includes("*")) {
|
|
377
455
|
return profiles;
|
|
@@ -406,6 +484,13 @@ async function writeManagedFile(target, content, options = {}) {
|
|
|
406
484
|
await fs.writeFile(target, content);
|
|
407
485
|
}
|
|
408
486
|
|
|
487
|
+
async function readExistingContent(target) {
|
|
488
|
+
if (!existsSync(target)) {
|
|
489
|
+
return undefined;
|
|
490
|
+
}
|
|
491
|
+
return fs.readFile(target, "utf8");
|
|
492
|
+
}
|
|
493
|
+
|
|
409
494
|
function resolveScope(options = {}, harnesses = []) {
|
|
410
495
|
if (options.project && options.global) {
|
|
411
496
|
throw new Error("Use either --global or --project, not both.");
|
|
@@ -413,13 +498,16 @@ function resolveScope(options = {}, harnesses = []) {
|
|
|
413
498
|
if (options.project && harnesses.includes("codex")) {
|
|
414
499
|
throw new Error("Codex profiles are loaded from $CODEX_HOME/<name>.config.toml and cannot be installed project-locally. Use --global or choose a different harness.");
|
|
415
500
|
}
|
|
501
|
+
if (options.project && harnesses.includes("tenex-edge")) {
|
|
502
|
+
throw new Error("tenex-edge agents are machine-local under $TENEX_EDGE_HOME/agents or ~/.tenex-edge/agents and cannot be installed project-locally. Use --global or choose a different harness.");
|
|
503
|
+
}
|
|
416
504
|
if (options.global) {
|
|
417
505
|
return "global";
|
|
418
506
|
}
|
|
419
507
|
if (options.project) {
|
|
420
508
|
return "project";
|
|
421
509
|
}
|
|
422
|
-
if (harnesses.includes("codex")) {
|
|
510
|
+
if (harnesses.includes("codex") || harnesses.includes("tenex-edge")) {
|
|
423
511
|
return "global";
|
|
424
512
|
}
|
|
425
513
|
return "project";
|
package/src/renderers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { AGENT_ALIASES,
|
|
4
|
+
import { AGENT_ALIASES, GENERATED_MARKER, SUPPORTED_AGENTS } from "./constants.js";
|
|
5
5
|
import { stringifyFrontmatter } from "./frontmatter.js";
|
|
6
6
|
import { expandHome } from "./source.js";
|
|
7
7
|
|
|
@@ -20,8 +20,7 @@ export function normalizeAgentList(input, options = {}) {
|
|
|
20
20
|
|
|
21
21
|
const rawAgents = flattenValues(input);
|
|
22
22
|
if (rawAgents.length === 0) {
|
|
23
|
-
|
|
24
|
-
return fallback.length > 0 ? fallback : [DEFAULT_AGENT];
|
|
23
|
+
return arrayify(options.defaultAgent);
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
if (rawAgents.includes("*")) {
|
|
@@ -42,6 +41,9 @@ export function renderForAgent(profile, agent, context) {
|
|
|
42
41
|
if (normalized === "opencode") {
|
|
43
42
|
return renderOpenCode(profile, context);
|
|
44
43
|
}
|
|
44
|
+
if (normalized === "tenex-edge") {
|
|
45
|
+
return renderTenexEdge(profile, context);
|
|
46
|
+
}
|
|
45
47
|
throw new Error(`Unsupported agent "${agent}"`);
|
|
46
48
|
}
|
|
47
49
|
|
|
@@ -86,6 +88,15 @@ export function resolveTargetPath(profile, agent, options = {}) {
|
|
|
86
88
|
return path.join(opencodeHome, "agents", `${profile.slug}.md`);
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
if (normalized === "tenex-edge") {
|
|
92
|
+
const tenexEdgeHome = options.tenexEdgeHome
|
|
93
|
+
? path.resolve(expandHome(options.tenexEdgeHome, home))
|
|
94
|
+
: process.env.TENEX_EDGE_HOME
|
|
95
|
+
? path.resolve(expandHome(process.env.TENEX_EDGE_HOME, home))
|
|
96
|
+
: path.join(home, ".tenex-edge");
|
|
97
|
+
return path.join(tenexEdgeHome, "agents", `${profile.slug}.json`);
|
|
98
|
+
}
|
|
99
|
+
|
|
89
100
|
throw new Error(`Unsupported agent "${agent}"`);
|
|
90
101
|
}
|
|
91
102
|
|
|
@@ -160,6 +171,38 @@ function renderOpenCode(profile, context) {
|
|
|
160
171
|
return stringifyFrontmatter(attributes, `${marker}\n\n${buildInstructionBody(profile, undefined, "opencode")}`);
|
|
161
172
|
}
|
|
162
173
|
|
|
174
|
+
function renderTenexEdge(profile, context) {
|
|
175
|
+
const existing = tenexEdgeKeyMaterial(context.existingContent);
|
|
176
|
+
const keyMaterial = existing ?? generateNostrKeypair();
|
|
177
|
+
const agent = {
|
|
178
|
+
description: profile.summary || profile.name,
|
|
179
|
+
prompt: buildInstructionBody(profile, profile.adapters["tenex-edge"] ?? profile.adapters["claude-code"], "tenex-edge")
|
|
180
|
+
};
|
|
181
|
+
const model = chooseClaudeModel(profile);
|
|
182
|
+
const effort = profile.attributes.recommended_reasoning_effort;
|
|
183
|
+
|
|
184
|
+
if (model && model !== "inherit") {
|
|
185
|
+
agent.model = model;
|
|
186
|
+
}
|
|
187
|
+
if (effort && effort !== "inherit" && ["low", "medium", "high", "xhigh", "max"].includes(effort)) {
|
|
188
|
+
agent.effort = effort;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const stored = {
|
|
192
|
+
slug: profile.slug,
|
|
193
|
+
secret_key: keyMaterial.secret_key,
|
|
194
|
+
public_key: keyMaterial.public_key,
|
|
195
|
+
created_at: keyMaterial.created_at ?? Math.floor(Date.now() / 1000),
|
|
196
|
+
command: ["claude"],
|
|
197
|
+
agent,
|
|
198
|
+
byline: profile.summary || profile.name,
|
|
199
|
+
managed_by: GENERATED_MARKER,
|
|
200
|
+
source: context.source
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return `${JSON.stringify(stored, null, 2)}\n`;
|
|
204
|
+
}
|
|
205
|
+
|
|
163
206
|
function buildInstructionBody(profile, adapter, harness) {
|
|
164
207
|
const parts = [
|
|
165
208
|
profile.body.trimEnd(),
|
|
@@ -174,6 +217,20 @@ function buildInstructionBody(profile, adapter, harness) {
|
|
|
174
217
|
"- The runtime agent manages any profile-specific home directory and notes at task time."
|
|
175
218
|
];
|
|
176
219
|
|
|
220
|
+
if (profile.installedSkills?.length) {
|
|
221
|
+
const skillBase = path.join(path.dirname(profile.installedSkills[0].path), "<skill>");
|
|
222
|
+
parts.push(
|
|
223
|
+
"",
|
|
224
|
+
"## Immediately Relevant Skills",
|
|
225
|
+
"",
|
|
226
|
+
`Immediately relevant skills; you should load these right away from \`${skillBase}\`.`,
|
|
227
|
+
""
|
|
228
|
+
);
|
|
229
|
+
for (const skill of profile.installedSkills) {
|
|
230
|
+
parts.push(`- \`${skill.name}\`: \`${skill.path}\``);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
177
234
|
if (adapter?.body) {
|
|
178
235
|
parts.push("", "## Harness Adapter", "", adapter.body.trimEnd());
|
|
179
236
|
}
|
|
@@ -220,6 +277,57 @@ function chooseOpenCodeModel(profile) {
|
|
|
220
277
|
return candidates.find((model) => model.includes("/"));
|
|
221
278
|
}
|
|
222
279
|
|
|
280
|
+
function tenexEdgeKeyMaterial(content) {
|
|
281
|
+
if (!content) {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let parsed;
|
|
286
|
+
try {
|
|
287
|
+
parsed = JSON.parse(content);
|
|
288
|
+
} catch {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!isHex64(parsed.secret_key)) {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const derived = derivePublicKey(parsed.secret_key);
|
|
297
|
+
if (!derived) {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
secret_key: parsed.secret_key,
|
|
303
|
+
public_key: isHex64(parsed.public_key) ? parsed.public_key : derived,
|
|
304
|
+
created_at: Number.isInteger(parsed.created_at) ? parsed.created_at : undefined
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function generateNostrKeypair() {
|
|
309
|
+
const ecdh = crypto.createECDH("secp256k1");
|
|
310
|
+
const publicKey = ecdh.generateKeys(undefined, "uncompressed");
|
|
311
|
+
return {
|
|
312
|
+
secret_key: ecdh.getPrivateKey("hex"),
|
|
313
|
+
public_key: publicKey.subarray(1, 33).toString("hex")
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function derivePublicKey(secretKey) {
|
|
318
|
+
try {
|
|
319
|
+
const ecdh = crypto.createECDH("secp256k1");
|
|
320
|
+
ecdh.setPrivateKey(Buffer.from(secretKey, "hex"));
|
|
321
|
+
return ecdh.getPublicKey(undefined, "uncompressed").subarray(1, 33).toString("hex");
|
|
322
|
+
} catch {
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function isHex64(value) {
|
|
328
|
+
return typeof value === "string" && /^[0-9a-fA-F]{64}$/.test(value);
|
|
329
|
+
}
|
|
330
|
+
|
|
223
331
|
function tomlString(value) {
|
|
224
332
|
return JSON.stringify(String(value));
|
|
225
333
|
}
|