@aexol/spectral 0.4.4 → 0.4.8
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/agent/agents.js +117 -0
- package/dist/agent/index.js +951 -0
- package/dist/cli.js +42 -9
- package/dist/memory/hooks/observer-trigger.js +11 -3
- package/dist/memory/index.js +2 -0
- package/dist/relay/dispatcher.js +4 -0
- package/dist/relay/models-fetch.js +5 -1
- package/dist/server/pi-bridge.js +11 -4
- package/dist/server/session-stream.js +55 -0
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent discovery and configuration for the subagent delegation extension.
|
|
3
|
+
*
|
|
4
|
+
* Agent definitions are markdown files with YAML frontmatter:
|
|
5
|
+
*
|
|
6
|
+
* ---
|
|
7
|
+
* name: my-agent
|
|
8
|
+
* description: What this agent does
|
|
9
|
+
* tools: read, grep, find, ls
|
|
10
|
+
* model: claude-haiku-4-5
|
|
11
|
+
* ---
|
|
12
|
+
* System prompt for the agent goes here.
|
|
13
|
+
*
|
|
14
|
+
* Locations:
|
|
15
|
+
* ~/.pi/agent/agents/*.md — User-level (always loaded)
|
|
16
|
+
* .pi/agents/*.md — Project-level (opt-in via agentScope)
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from "node:fs";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { getAgentDir, parseFrontmatter } from "@mariozechner/pi-coding-agent";
|
|
21
|
+
function loadAgentsFromDir(dir, source) {
|
|
22
|
+
const agents = [];
|
|
23
|
+
if (!fs.existsSync(dir)) {
|
|
24
|
+
return agents;
|
|
25
|
+
}
|
|
26
|
+
let entries;
|
|
27
|
+
try {
|
|
28
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return agents;
|
|
32
|
+
}
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (!entry.name.endsWith(".md"))
|
|
35
|
+
continue;
|
|
36
|
+
if (!entry.isFile() && !entry.isSymbolicLink())
|
|
37
|
+
continue;
|
|
38
|
+
const filePath = path.join(dir, entry.name);
|
|
39
|
+
let content;
|
|
40
|
+
try {
|
|
41
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
47
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const tools = frontmatter.tools
|
|
51
|
+
?.split(",")
|
|
52
|
+
.map((t) => t.trim())
|
|
53
|
+
.filter(Boolean);
|
|
54
|
+
agents.push({
|
|
55
|
+
name: frontmatter.name,
|
|
56
|
+
description: frontmatter.description,
|
|
57
|
+
tools: tools && tools.length > 0 ? tools : undefined,
|
|
58
|
+
model: frontmatter.model,
|
|
59
|
+
systemPrompt: body,
|
|
60
|
+
source,
|
|
61
|
+
filePath,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return agents;
|
|
65
|
+
}
|
|
66
|
+
function isDirectory(p) {
|
|
67
|
+
try {
|
|
68
|
+
return fs.statSync(p).isDirectory();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function findNearestProjectAgentsDir(cwd) {
|
|
75
|
+
let currentDir = cwd;
|
|
76
|
+
while (true) {
|
|
77
|
+
const candidate = path.join(currentDir, ".pi", "agents");
|
|
78
|
+
if (isDirectory(candidate))
|
|
79
|
+
return candidate;
|
|
80
|
+
const parentDir = path.dirname(currentDir);
|
|
81
|
+
if (parentDir === currentDir)
|
|
82
|
+
return null;
|
|
83
|
+
currentDir = parentDir;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function discoverAgents(cwd, scope) {
|
|
87
|
+
const userDir = path.join(getAgentDir(), "agents");
|
|
88
|
+
const projectAgentsDir = findNearestProjectAgentsDir(cwd);
|
|
89
|
+
const userAgents = scope === "project" ? [] : loadAgentsFromDir(userDir, "user");
|
|
90
|
+
const projectAgents = scope === "user" || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, "project");
|
|
91
|
+
const agentMap = new Map();
|
|
92
|
+
if (scope === "both") {
|
|
93
|
+
for (const agent of userAgents)
|
|
94
|
+
agentMap.set(agent.name, agent);
|
|
95
|
+
for (const agent of projectAgents)
|
|
96
|
+
agentMap.set(agent.name, agent);
|
|
97
|
+
}
|
|
98
|
+
else if (scope === "user") {
|
|
99
|
+
for (const agent of userAgents)
|
|
100
|
+
agentMap.set(agent.name, agent);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
for (const agent of projectAgents)
|
|
104
|
+
agentMap.set(agent.name, agent);
|
|
105
|
+
}
|
|
106
|
+
return { agents: Array.from(agentMap.values()), projectAgentsDir };
|
|
107
|
+
}
|
|
108
|
+
export function formatAgentList(agents, maxItems) {
|
|
109
|
+
if (agents.length === 0)
|
|
110
|
+
return { text: "none", remaining: 0 };
|
|
111
|
+
const listed = agents.slice(0, maxItems);
|
|
112
|
+
const remaining = agents.length - listed.length;
|
|
113
|
+
return {
|
|
114
|
+
text: listed.map((a) => `${a.name} (${a.source}): ${a.description}`).join("; "),
|
|
115
|
+
remaining,
|
|
116
|
+
};
|
|
117
|
+
}
|