@agimon-ai/po-domain 0.0.0-alpha.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/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/apiContracts.cjs +17 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +13 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/apply.cjs +76 -0
- package/dist/apply.cjs.map +1 -0
- package/dist/apply.d.mts +23 -0
- package/dist/apply.d.mts.map +1 -0
- package/dist/apply.mjs +72 -0
- package/dist/apply.mjs.map +1 -0
- package/dist/domainText-Dm8k7p4z.mjs +97 -0
- package/dist/domainText-Dm8k7p4z.mjs.map +1 -0
- package/dist/domainText-UIRIz6-_.cjs +204 -0
- package/dist/domainText-UIRIz6-_.cjs.map +1 -0
- package/dist/extensions/mcp.cjs +26 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +21 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/extensions/server.cjs +229 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +221 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +26 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/index.cjs +19 -0
- package/dist/index.d.mts +84 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/mcp.cjs +32 -0
- package/dist/mcp.cjs.map +1 -0
- package/dist/mcp.d.mts +52 -0
- package/dist/mcp.d.mts.map +1 -0
- package/dist/mcp.mjs +19 -0
- package/dist/mcp.mjs.map +1 -0
- package/dist/mcpFilter-CZNmU_VF.cjs +189 -0
- package/dist/mcpFilter-CZNmU_VF.cjs.map +1 -0
- package/dist/mcpFilter-Cf-czy8O.mjs +151 -0
- package/dist/mcpFilter-Cf-czy8O.mjs.map +1 -0
- package/dist/pluginMaterializer-C8IZ1PSY.cjs +267 -0
- package/dist/pluginMaterializer-C8IZ1PSY.cjs.map +1 -0
- package/dist/pluginMaterializer-D9-oDF-b.mjs +259 -0
- package/dist/pluginMaterializer-D9-oDF-b.mjs.map +1 -0
- package/dist/plugins.cjs +3 -0
- package/dist/plugins.d.mts +14 -0
- package/dist/plugins.d.mts.map +1 -0
- package/dist/plugins.mjs +2 -0
- package/dist/resourceCollector-CRY5cmeY.mjs +773 -0
- package/dist/resourceCollector-CRY5cmeY.mjs.map +1 -0
- package/dist/resourceCollector-Cu-lp5hJ.cjs +837 -0
- package/dist/resourceCollector-Cu-lp5hJ.cjs.map +1 -0
- package/dist/resources-BEjxE3Zn.d.mts +66 -0
- package/dist/resources-BEjxE3Zn.d.mts.map +1 -0
- package/dist/resources.cjs +12 -0
- package/dist/resources.d.mts +43 -0
- package/dist/resources.d.mts.map +1 -0
- package/dist/resources.mjs +2 -0
- package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
- package/dist/toolNames-Ckp5VLMD.mjs +43 -0
- package/dist/toolNames-Ckp5VLMD.mjs.map +1 -0
- package/dist/toolNames-Cuu-u8r1.cjs +48 -0
- package/dist/toolNames-Cuu-u8r1.cjs.map +1 -0
- package/llms.txt +12 -0
- package/package.json +158 -0
- package/src/prompts/po-author-domain/SKILL.md +18 -0
- package/src/prompts/po-author-domain/references/domains-contract.md +165 -0
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
|
|
2
|
+
const require_mcpFilter = require("./mcpFilter-CZNmU_VF.cjs");
|
|
3
|
+
const require_toolNames = require("./toolNames-Cuu-u8r1.cjs");
|
|
4
|
+
let node_fs = require("node:fs");
|
|
5
|
+
node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
|
|
6
|
+
let node_path = require("node:path");
|
|
7
|
+
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
8
|
+
let yaml = require("yaml");
|
|
9
|
+
let _agimon_ai_po_core_piSettings = require("@agimon-ai/po-core/piSettings");
|
|
10
|
+
let node_crypto = require("node:crypto");
|
|
11
|
+
node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
|
|
12
|
+
let node_os = require("node:os");
|
|
13
|
+
node_os = require_rolldown_runtime.__toESM(node_os, 1);
|
|
14
|
+
let _agimon_ai_po_config_agentPluginMcp = require("@agimon-ai/po-config/agentPluginMcp");
|
|
15
|
+
let _agimon_ai_po_config_config = require("@agimon-ai/po-config/config");
|
|
16
|
+
let _agimon_ai_po_core_mcpProjection = require("@agimon-ai/po-core/mcpProjection");
|
|
17
|
+
//#region src/services/skillCacheLocation/index.ts
|
|
18
|
+
const SYNC_DIRECTORY = "sync";
|
|
19
|
+
const WORKTREES_DIRECTORY = "worktrees";
|
|
20
|
+
const SKILL_CACHE_SEGMENTS = ["cache", "skills"];
|
|
21
|
+
const GIT_DIRECTORY = ".git";
|
|
22
|
+
const COMMON_DIRECTORY_FILE = "commondir";
|
|
23
|
+
const IDENTITY_HEX_LENGTH = 32;
|
|
24
|
+
const LABEL_MAX_LENGTH = 48;
|
|
25
|
+
const DEFAULT_REPOSITORY_LABEL = "repo";
|
|
26
|
+
const DEFAULT_WORKTREE_LABEL = "worktree";
|
|
27
|
+
/**
|
|
28
|
+
* The home-scoped skill manifest cache for one worktree.
|
|
29
|
+
*
|
|
30
|
+
* This mirrors the namespace the Po launcher publishes into, so a session
|
|
31
|
+
* that stages resources through the launcher and one that stages them through a
|
|
32
|
+
* `/domains` switch hit the same manifests instead of rescanning every tree.
|
|
33
|
+
* Two worktrees of one repository stay separate, and a checkout reached through
|
|
34
|
+
* a symlink resolves to the same identity as the real path.
|
|
35
|
+
*/
|
|
36
|
+
function resolveSkillCacheDirectory(repositoryRoot, homeDirectory = node_os.default.homedir()) {
|
|
37
|
+
const root = canonicalPath(repositoryRoot);
|
|
38
|
+
const commonDirectory = gitCommonDirectory(root);
|
|
39
|
+
const repositoryToken = commonDirectory ? `git:${commonDirectory}` : `root:${root}`;
|
|
40
|
+
const repositoryId = identityHash(repositoryToken);
|
|
41
|
+
const worktreeId = identityHash(`${repositoryToken}\0worktree:${root}`);
|
|
42
|
+
const repoLabel = repositoryLabel(commonDirectory, root);
|
|
43
|
+
const worktreeLabel = sanitizeSyncLabel(node_path.default.basename(root), DEFAULT_WORKTREE_LABEL);
|
|
44
|
+
return node_path.default.join((0, _agimon_ai_po_core_piSettings.poHomeDirectory)(node_path.default.resolve(homeDirectory)), SYNC_DIRECTORY, `${repoLabel}--${repositoryId}`, WORKTREES_DIRECTORY, `${worktreeLabel}--${worktreeId}`, ...SKILL_CACHE_SEGMENTS);
|
|
45
|
+
}
|
|
46
|
+
function canonicalPath(target) {
|
|
47
|
+
const absolute = node_path.default.resolve(target);
|
|
48
|
+
try {
|
|
49
|
+
return node_fs.default.realpathSync.native(absolute);
|
|
50
|
+
} catch {
|
|
51
|
+
return absolute;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function readGitDirectory(repositoryRoot) {
|
|
55
|
+
const gitPath = node_path.default.join(repositoryRoot, GIT_DIRECTORY);
|
|
56
|
+
try {
|
|
57
|
+
const stat = node_fs.default.statSync(gitPath);
|
|
58
|
+
if (stat.isDirectory()) return canonicalPath(gitPath);
|
|
59
|
+
if (!stat.isFile()) return void 0;
|
|
60
|
+
const match = /^gitdir:\s*(.+)$/imu.exec(node_fs.default.readFileSync(gitPath, "utf8").trim());
|
|
61
|
+
return match?.[1] ? canonicalPath(node_path.default.resolve(repositoryRoot, match[1].trim())) : void 0;
|
|
62
|
+
} catch {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function gitCommonDirectory(repositoryRoot) {
|
|
67
|
+
const gitDirectory = readGitDirectory(repositoryRoot);
|
|
68
|
+
if (!gitDirectory) return void 0;
|
|
69
|
+
try {
|
|
70
|
+
const relative = node_fs.default.readFileSync(node_path.default.join(gitDirectory, COMMON_DIRECTORY_FILE), "utf8").trim();
|
|
71
|
+
return relative ? canonicalPath(node_path.default.resolve(gitDirectory, relative)) : gitDirectory;
|
|
72
|
+
} catch {
|
|
73
|
+
return gitDirectory;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function identityHash(value) {
|
|
77
|
+
return node_crypto.default.createHash("sha256").update(value, "utf8").digest("hex").slice(0, IDENTITY_HEX_LENGTH);
|
|
78
|
+
}
|
|
79
|
+
/** Keeps a repository or worktree name usable as one path segment. */
|
|
80
|
+
function sanitizeSyncLabel(value, fallback) {
|
|
81
|
+
const normalized = value.normalize("NFKD").replace(/[\u0300-\u036f]/gu, "").replace(/[^A-Za-z0-9._-]+/gu, "-").replace(/^[._-]+|[._-]+$/gu, "").slice(0, LABEL_MAX_LENGTH).replace(/[._-]+$/gu, "");
|
|
82
|
+
return normalized === "" || normalized === "." || normalized === ".." ? fallback : normalized;
|
|
83
|
+
}
|
|
84
|
+
function repositoryLabel(commonDirectory, repositoryRoot) {
|
|
85
|
+
if (!commonDirectory) return sanitizeSyncLabel(node_path.default.basename(repositoryRoot), DEFAULT_REPOSITORY_LABEL);
|
|
86
|
+
const base = node_path.default.basename(commonDirectory);
|
|
87
|
+
return sanitizeSyncLabel(base === GIT_DIRECTORY ? node_path.default.basename(node_path.default.dirname(commonDirectory)) : base, DEFAULT_REPOSITORY_LABEL);
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/services/resourceCollector/index.ts
|
|
91
|
+
const DISPATCHER_AGENT_NAME = "agiflow-dispatcher";
|
|
92
|
+
const DISPATCHER_TOOLS = [
|
|
93
|
+
"read",
|
|
94
|
+
"grep",
|
|
95
|
+
"find",
|
|
96
|
+
"ls",
|
|
97
|
+
"bash",
|
|
98
|
+
"launch_workflow",
|
|
99
|
+
"list_workflows"
|
|
100
|
+
];
|
|
101
|
+
const SKILL_MANIFEST_VERSION = 4;
|
|
102
|
+
const SKILL_MANIFEST_HASH_LENGTH = 16;
|
|
103
|
+
const AGENT_PLUGIN_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
|
|
104
|
+
const PLUGIN_DATA_DIRECTORY = "plugin-data";
|
|
105
|
+
function isJsonObject(value) {
|
|
106
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
107
|
+
}
|
|
108
|
+
function canonicalJson(value) {
|
|
109
|
+
const normalize = (candidate) => {
|
|
110
|
+
if (Array.isArray(candidate)) return candidate.map(normalize);
|
|
111
|
+
if (!isJsonObject(candidate)) return candidate;
|
|
112
|
+
return Object.fromEntries(Object.entries(candidate).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => [key, normalize(child)]));
|
|
113
|
+
};
|
|
114
|
+
return JSON.stringify(normalize(value));
|
|
115
|
+
}
|
|
116
|
+
function projectionFingerprint(input) {
|
|
117
|
+
return (0, node_crypto.createHash)("sha256").update(canonicalJson(input)).digest("hex");
|
|
118
|
+
}
|
|
119
|
+
function stablePluginIdentity(entry) {
|
|
120
|
+
const source = entry.source;
|
|
121
|
+
const identity = source?.type === "git" ? [
|
|
122
|
+
"git",
|
|
123
|
+
source.url,
|
|
124
|
+
source.path ?? ""
|
|
125
|
+
] : source?.type === "npm" ? [
|
|
126
|
+
"npm",
|
|
127
|
+
source.registry ?? "",
|
|
128
|
+
source.package
|
|
129
|
+
] : ["local", node_fs.default.realpathSync(entry.directory)];
|
|
130
|
+
return (0, node_crypto.createHash)("sha256").update(JSON.stringify(identity)).digest("hex");
|
|
131
|
+
}
|
|
132
|
+
function rootAgentPluginSchema(entry) {
|
|
133
|
+
const manifestPath = node_path.default.join(entry.directory, "plugin.json");
|
|
134
|
+
if (entry.manifest?.path === manifestPath && entry.manifest.agentPluginSchema === AGENT_PLUGIN_SCHEMA) return AGENT_PLUGIN_SCHEMA;
|
|
135
|
+
try {
|
|
136
|
+
const stat = node_fs.default.lstatSync(manifestPath);
|
|
137
|
+
if (stat.isSymbolicLink() || !stat.isFile()) return void 0;
|
|
138
|
+
const parsed = JSON.parse(node_fs.default.readFileSync(manifestPath, "utf8"));
|
|
139
|
+
return isJsonObject(parsed) && parsed.$schema === AGENT_PLUGIN_SCHEMA ? AGENT_PLUGIN_SCHEMA : void 0;
|
|
140
|
+
} catch {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function pluginMcpSource(entry, pluginDataRoot) {
|
|
145
|
+
if (entry.mcp === false) return void 0;
|
|
146
|
+
const pluginRoot = node_fs.default.realpathSync(entry.directory);
|
|
147
|
+
const agentPlugin = rootAgentPluginSchema(entry) === AGENT_PLUGIN_SCHEMA;
|
|
148
|
+
const configPath = node_path.default.join(pluginRoot, agentPlugin ? "mcp.json" : ".mcp.json");
|
|
149
|
+
let stat;
|
|
150
|
+
try {
|
|
151
|
+
stat = await node_fs.default.promises.lstat(configPath);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (error.code === "ENOENT") return void 0;
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
if (!stat.isFile() || stat.isSymbolicLink()) return void 0;
|
|
157
|
+
const content = await node_fs.default.promises.readFile(configPath);
|
|
158
|
+
const contentDigest = (0, node_crypto.createHash)("sha256").update(content).digest("hex");
|
|
159
|
+
const pluginId = stablePluginIdentity(entry);
|
|
160
|
+
const sourceId = `plugin:${pluginId}`;
|
|
161
|
+
if (!agentPlugin) return {
|
|
162
|
+
sourceId,
|
|
163
|
+
owner: "plugin",
|
|
164
|
+
format: "native",
|
|
165
|
+
configPath,
|
|
166
|
+
contentDigest
|
|
167
|
+
};
|
|
168
|
+
return {
|
|
169
|
+
sourceId,
|
|
170
|
+
owner: "plugin",
|
|
171
|
+
format: "agent-plugin-v1",
|
|
172
|
+
configPath,
|
|
173
|
+
contentDigest,
|
|
174
|
+
pluginId,
|
|
175
|
+
pluginRoot,
|
|
176
|
+
pluginDataDirectory: node_path.default.join(pluginDataRoot, pluginId),
|
|
177
|
+
mcpSchemaUrl: _agimon_ai_po_core_mcpProjection.AGENT_PLUGIN_MCP_SCHEMA_URL
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function splitFrontmatter(content) {
|
|
181
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
182
|
+
if (!match) throw new Error("Resource is missing YAML frontmatter");
|
|
183
|
+
return {
|
|
184
|
+
attributes: (0, yaml.parse)(match[1]),
|
|
185
|
+
body: match[2] ?? ""
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const CLAUDE_MODEL_ALIASES = {
|
|
189
|
+
sonnet: "anthropic/claude-sonnet-4-6",
|
|
190
|
+
opus: "anthropic/claude-opus-4-6",
|
|
191
|
+
haiku: "anthropic/claude-haiku-4-5"
|
|
192
|
+
};
|
|
193
|
+
function assertSafeAgentName(name) {
|
|
194
|
+
const trimmed = name.trim();
|
|
195
|
+
if (!trimmed || trimmed !== name || trimmed === "." || trimmed === ".." || trimmed.includes("\0") || /[\\/]/.test(trimmed)) throw new Error(`Unsafe agent name: ${JSON.stringify(name)}`);
|
|
196
|
+
return trimmed;
|
|
197
|
+
}
|
|
198
|
+
function normalizeClaudeModel(value) {
|
|
199
|
+
if (typeof value === "string") return CLAUDE_MODEL_ALIASES[value.toLowerCase()] ?? value;
|
|
200
|
+
if (Array.isArray(value)) return value.map((entry) => normalizeClaudeModel(entry));
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
/** Rewrites a Claude-authored agent definition into the form Pi loads. */
|
|
204
|
+
function adaptAgentDefinition(content) {
|
|
205
|
+
const { attributes, body } = splitFrontmatter(content);
|
|
206
|
+
const rawName = typeof attributes.name === "string" ? attributes.name : void 0;
|
|
207
|
+
if (!rawName) throw new Error("Agent definition requires a name");
|
|
208
|
+
const name = assertSafeAgentName(rawName);
|
|
209
|
+
const hasTools = Object.hasOwn(attributes, "tools");
|
|
210
|
+
const rawTools = Array.isArray(attributes.tools) ? attributes.tools.map(String) : typeof attributes.tools === "string" ? attributes.tools.split(",").map((tool) => tool.trim()).filter(Boolean) : [];
|
|
211
|
+
const mappedTools = [...new Set(rawTools.map(require_toolNames.toPiToolName).filter((tool) => Boolean(tool)))];
|
|
212
|
+
const converted = {
|
|
213
|
+
...attributes,
|
|
214
|
+
inheritProjectContext: true,
|
|
215
|
+
inheritSkills: true,
|
|
216
|
+
systemPromptMode: attributes.systemPromptMode ?? "replace"
|
|
217
|
+
};
|
|
218
|
+
if (hasTools) converted.tools = Array.isArray(attributes.tools) ? mappedTools : mappedTools.join(", ");
|
|
219
|
+
else delete converted.tools;
|
|
220
|
+
if (converted.model !== void 0) converted.model = normalizeClaudeModel(converted.model);
|
|
221
|
+
if (converted.fallbackModels !== void 0) converted.fallbackModels = normalizeClaudeModel(converted.fallbackModels);
|
|
222
|
+
if (converted.memory === "project" || converted.memory === "user") converted.memory = {
|
|
223
|
+
scope: converted.memory,
|
|
224
|
+
path: name
|
|
225
|
+
};
|
|
226
|
+
else if (converted.memory !== void 0 && (typeof converted.memory !== "object" || converted.memory === null || Array.isArray(converted.memory))) throw new Error("Unsupported memory declaration");
|
|
227
|
+
delete converted.permissionMode;
|
|
228
|
+
return {
|
|
229
|
+
name,
|
|
230
|
+
content: `---\n${(0, yaml.stringify)(converted).trim()}\n---\n\n${body.trimStart()}`
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function skillManifestPath(repoRoot, skillRoot, discovery, cacheDirectory) {
|
|
234
|
+
const key = (0, node_crypto.createHash)("sha256").update(`${node_path.default.resolve(skillRoot)}\0${discovery}`).digest("hex").slice(0, SKILL_MANIFEST_HASH_LENGTH);
|
|
235
|
+
return node_path.default.join(cacheDirectory ?? resolveSkillCacheDirectory(repoRoot), `${key}.json`);
|
|
236
|
+
}
|
|
237
|
+
function fileMatches(previous) {
|
|
238
|
+
try {
|
|
239
|
+
const stat = node_fs.default.statSync(previous.path);
|
|
240
|
+
return stat.isFile() && stat.size === previous.size && stat.mtimeMs === previous.mtimeMs && stat.ctimeMs === previous.ctimeMs;
|
|
241
|
+
} catch {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function directoryMatches(previous) {
|
|
246
|
+
try {
|
|
247
|
+
const stat = node_fs.default.statSync(previous.path);
|
|
248
|
+
return stat.isDirectory() && stat.mtimeMs === previous.mtimeMs && stat.ctimeMs === previous.ctimeMs;
|
|
249
|
+
} catch {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function readSkillManifest(manifestPath, root, discovery) {
|
|
254
|
+
try {
|
|
255
|
+
const manifest = JSON.parse(node_fs.default.readFileSync(manifestPath, "utf8"));
|
|
256
|
+
if (manifest.version !== SKILL_MANIFEST_VERSION || manifest.root !== node_path.default.resolve(root) || manifest.discovery !== discovery || !Array.isArray(manifest.files) || !Array.isArray(manifest.directories) || !manifest.files.every(fileMatches) || !manifest.directories.every(directoryMatches)) return;
|
|
257
|
+
return manifest;
|
|
258
|
+
} catch {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function writeSkillManifest(manifestPath, manifest) {
|
|
263
|
+
node_fs.default.mkdirSync(node_path.default.dirname(manifestPath), {
|
|
264
|
+
recursive: true,
|
|
265
|
+
mode: 448
|
|
266
|
+
});
|
|
267
|
+
const temporary = `${manifestPath}.${process.pid}.${Date.now()}.tmp`;
|
|
268
|
+
node_fs.default.writeFileSync(temporary, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 384 });
|
|
269
|
+
try {
|
|
270
|
+
node_fs.default.renameSync(temporary, manifestPath);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
node_fs.default.rmSync(temporary, { force: true });
|
|
273
|
+
if (!node_fs.default.existsSync(manifestPath)) throw error;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async function pathExists(target) {
|
|
277
|
+
try {
|
|
278
|
+
await node_fs.default.promises.access(target);
|
|
279
|
+
return true;
|
|
280
|
+
} catch {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async function readSkillManifestAsync(manifestPath, root, discovery) {
|
|
285
|
+
try {
|
|
286
|
+
const manifest = JSON.parse(await node_fs.default.promises.readFile(manifestPath, "utf8"));
|
|
287
|
+
if (manifest.version !== SKILL_MANIFEST_VERSION || manifest.root !== node_path.default.resolve(root) || manifest.discovery !== discovery || !Array.isArray(manifest.files) || !Array.isArray(manifest.directories)) return;
|
|
288
|
+
for (const previous of manifest.files) {
|
|
289
|
+
const stat = await node_fs.default.promises.stat(previous.path);
|
|
290
|
+
if (!stat.isFile() || stat.size !== previous.size || stat.mtimeMs !== previous.mtimeMs || stat.ctimeMs !== previous.ctimeMs) return;
|
|
291
|
+
}
|
|
292
|
+
for (const previous of manifest.directories) {
|
|
293
|
+
const stat = await node_fs.default.promises.stat(previous.path);
|
|
294
|
+
if (!stat.isDirectory() || stat.mtimeMs !== previous.mtimeMs || stat.ctimeMs !== previous.ctimeMs) return;
|
|
295
|
+
}
|
|
296
|
+
return manifest;
|
|
297
|
+
} catch {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async function writeSkillManifestAsync(manifestPath, manifest) {
|
|
302
|
+
await node_fs.default.promises.mkdir(node_path.default.dirname(manifestPath), {
|
|
303
|
+
recursive: true,
|
|
304
|
+
mode: 448
|
|
305
|
+
});
|
|
306
|
+
const temporary = `${manifestPath}.${process.pid}.${Date.now()}.tmp`;
|
|
307
|
+
await node_fs.default.promises.writeFile(temporary, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 384 });
|
|
308
|
+
try {
|
|
309
|
+
await node_fs.default.promises.rename(temporary, manifestPath);
|
|
310
|
+
} catch (error) {
|
|
311
|
+
await node_fs.default.promises.rm(temporary, { force: true });
|
|
312
|
+
if (!await pathExists(manifestPath)) throw error;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* A directory entry resolved through any symlink, or undefined for a broken one.
|
|
317
|
+
*
|
|
318
|
+
* `readdir` reports symlinks with lstat semantics, so a symlinked skill
|
|
319
|
+
* directory answers false to both `isDirectory()` and `isFile()` and would be
|
|
320
|
+
* skipped by a naive branch. Pi stats through the link instead
|
|
321
|
+
* (dist/core/skills.js:171-183) and treats a failing stat as a broken link to
|
|
322
|
+
* skip, which is what keeps a shared skill published as a symlink visible.
|
|
323
|
+
*/
|
|
324
|
+
function resolveEntryKind(entry, candidate) {
|
|
325
|
+
if (!entry.isSymbolicLink()) return {
|
|
326
|
+
directory: entry.isDirectory(),
|
|
327
|
+
file: entry.isFile()
|
|
328
|
+
};
|
|
329
|
+
try {
|
|
330
|
+
const stat = node_fs.default.statSync(candidate);
|
|
331
|
+
return {
|
|
332
|
+
directory: stat.isDirectory(),
|
|
333
|
+
file: stat.isFile()
|
|
334
|
+
};
|
|
335
|
+
} catch {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async function resolveEntryKindAsync(entry, candidate) {
|
|
340
|
+
if (!entry.isSymbolicLink()) return {
|
|
341
|
+
directory: entry.isDirectory(),
|
|
342
|
+
file: entry.isFile()
|
|
343
|
+
};
|
|
344
|
+
try {
|
|
345
|
+
const stat = await node_fs.default.promises.stat(candidate);
|
|
346
|
+
return {
|
|
347
|
+
directory: stat.isDirectory(),
|
|
348
|
+
file: stat.isFile()
|
|
349
|
+
};
|
|
350
|
+
} catch {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* The skill's declared name, or the name of the directory holding it.
|
|
356
|
+
*
|
|
357
|
+
* Pi falls back to the containing directory rather than rejecting the file
|
|
358
|
+
* (dist/core/skills.js:245). Rejecting is worse than one missing skill here:
|
|
359
|
+
* this walk feeds the whole resource collection, so a single unnamed SKILL.md
|
|
360
|
+
* from a third-party plugin would fail session launch outright rather than
|
|
361
|
+
* degrade.
|
|
362
|
+
*/
|
|
363
|
+
function skillName(filePath, attributes) {
|
|
364
|
+
const declared = typeof attributes.name === "string" ? attributes.name.trim() : "";
|
|
365
|
+
if (declared) return declared;
|
|
366
|
+
const fallback = node_path.default.basename(node_path.default.dirname(filePath));
|
|
367
|
+
if (!fallback) throw new Error(`Resource requires a name: ${filePath}`);
|
|
368
|
+
return fallback;
|
|
369
|
+
}
|
|
370
|
+
function scanSkillManifest(root, discovery) {
|
|
371
|
+
const directories = [];
|
|
372
|
+
const files = [];
|
|
373
|
+
const visited = /* @__PURE__ */ new Set();
|
|
374
|
+
const walk = (directory, depth) => {
|
|
375
|
+
if (!node_fs.default.existsSync(directory)) return;
|
|
376
|
+
const realDirectory = node_fs.default.realpathSync(directory);
|
|
377
|
+
if (visited.has(realDirectory)) return;
|
|
378
|
+
visited.add(realDirectory);
|
|
379
|
+
const directoryStat = node_fs.default.statSync(directory);
|
|
380
|
+
directories.push({
|
|
381
|
+
path: directory,
|
|
382
|
+
mtimeMs: directoryStat.mtimeMs,
|
|
383
|
+
ctimeMs: directoryStat.ctimeMs
|
|
384
|
+
});
|
|
385
|
+
for (const entry of node_fs.default.readdirSync(directory, { withFileTypes: true })) {
|
|
386
|
+
const candidate = node_path.default.join(directory, entry.name);
|
|
387
|
+
const kind = resolveEntryKind(entry, candidate);
|
|
388
|
+
if (!kind) continue;
|
|
389
|
+
if (kind.directory) {
|
|
390
|
+
if (discovery === "recursive" || depth === 0) walk(candidate, depth + 1);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (!kind.file || entry.name !== "SKILL.md" || discovery === "direct-children" && depth !== 1) continue;
|
|
394
|
+
const content = node_fs.default.readFileSync(candidate, "utf8");
|
|
395
|
+
const { attributes } = splitFrontmatter(content);
|
|
396
|
+
const stat = node_fs.default.statSync(candidate);
|
|
397
|
+
files.push({
|
|
398
|
+
path: candidate,
|
|
399
|
+
name: skillName(candidate, attributes),
|
|
400
|
+
...typeof attributes.description === "string" ? { description: attributes.description.trim() } : {},
|
|
401
|
+
digest: (0, node_crypto.createHash)("sha256").update(content).digest("hex"),
|
|
402
|
+
size: stat.size,
|
|
403
|
+
mtimeMs: stat.mtimeMs,
|
|
404
|
+
ctimeMs: stat.ctimeMs
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
const resolvedRoot = node_path.default.resolve(root);
|
|
409
|
+
walk(resolvedRoot, 0);
|
|
410
|
+
files.sort((left, right) => left.path.localeCompare(right.path));
|
|
411
|
+
directories.sort((left, right) => left.path.localeCompare(right.path));
|
|
412
|
+
return {
|
|
413
|
+
version: SKILL_MANIFEST_VERSION,
|
|
414
|
+
root: resolvedRoot,
|
|
415
|
+
discovery,
|
|
416
|
+
files,
|
|
417
|
+
directories
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
/** Cached skill metadata and exact files, ready for Pi without a second tree walk. */
|
|
421
|
+
function discoverSkills(repoRoot, root, discovery = "recursive", cacheDirectory) {
|
|
422
|
+
if (!node_fs.default.existsSync(root)) return [];
|
|
423
|
+
const manifestPath = skillManifestPath(repoRoot, root, discovery, cacheDirectory);
|
|
424
|
+
const cached = readSkillManifest(manifestPath, root, discovery);
|
|
425
|
+
const manifest = cached ?? scanSkillManifest(root, discovery);
|
|
426
|
+
if (!cached) writeSkillManifest(manifestPath, manifest);
|
|
427
|
+
return manifest.files.map(({ name, path: filePath, description, digest }) => ({
|
|
428
|
+
name,
|
|
429
|
+
path: filePath,
|
|
430
|
+
digest,
|
|
431
|
+
...description === void 0 ? {} : { description }
|
|
432
|
+
}));
|
|
433
|
+
}
|
|
434
|
+
async function scanSkillManifestAsync(root, discovery) {
|
|
435
|
+
const directories = [];
|
|
436
|
+
const files = [];
|
|
437
|
+
const visited = /* @__PURE__ */ new Set();
|
|
438
|
+
const walk = async (directory, depth) => {
|
|
439
|
+
let entries;
|
|
440
|
+
try {
|
|
441
|
+
const realDirectory = await node_fs.default.promises.realpath(directory);
|
|
442
|
+
if (visited.has(realDirectory)) return;
|
|
443
|
+
visited.add(realDirectory);
|
|
444
|
+
const directoryStat = await node_fs.default.promises.stat(directory);
|
|
445
|
+
directories.push({
|
|
446
|
+
path: directory,
|
|
447
|
+
mtimeMs: directoryStat.mtimeMs,
|
|
448
|
+
ctimeMs: directoryStat.ctimeMs
|
|
449
|
+
});
|
|
450
|
+
entries = await node_fs.default.promises.readdir(directory, { withFileTypes: true });
|
|
451
|
+
} catch (error) {
|
|
452
|
+
if (error.code === "ENOENT") return;
|
|
453
|
+
throw error;
|
|
454
|
+
}
|
|
455
|
+
for (const entry of entries) {
|
|
456
|
+
const candidate = node_path.default.join(directory, entry.name);
|
|
457
|
+
const kind = await resolveEntryKindAsync(entry, candidate);
|
|
458
|
+
if (!kind) continue;
|
|
459
|
+
if (kind.directory) {
|
|
460
|
+
if (discovery === "recursive" || depth === 0) await walk(candidate, depth + 1);
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (!kind.file || entry.name !== "SKILL.md" || discovery === "direct-children" && depth !== 1) continue;
|
|
464
|
+
const content = await node_fs.default.promises.readFile(candidate, "utf8");
|
|
465
|
+
const { attributes } = splitFrontmatter(content);
|
|
466
|
+
const stat = await node_fs.default.promises.stat(candidate);
|
|
467
|
+
files.push({
|
|
468
|
+
path: candidate,
|
|
469
|
+
name: skillName(candidate, attributes),
|
|
470
|
+
...typeof attributes.description === "string" ? { description: attributes.description.trim() } : {},
|
|
471
|
+
digest: (0, node_crypto.createHash)("sha256").update(content).digest("hex"),
|
|
472
|
+
size: stat.size,
|
|
473
|
+
mtimeMs: stat.mtimeMs,
|
|
474
|
+
ctimeMs: stat.ctimeMs
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
const resolvedRoot = node_path.default.resolve(root);
|
|
479
|
+
await walk(resolvedRoot, 0);
|
|
480
|
+
files.sort((left, right) => left.path.localeCompare(right.path));
|
|
481
|
+
directories.sort((left, right) => left.path.localeCompare(right.path));
|
|
482
|
+
return {
|
|
483
|
+
version: SKILL_MANIFEST_VERSION,
|
|
484
|
+
root: resolvedRoot,
|
|
485
|
+
discovery,
|
|
486
|
+
files,
|
|
487
|
+
directories
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
async function discoverSkillsAsync(repoRoot, root, discovery = "recursive", cacheDirectory) {
|
|
491
|
+
if (!await pathExists(root)) return [];
|
|
492
|
+
const manifestPath = skillManifestPath(repoRoot, root, discovery, cacheDirectory);
|
|
493
|
+
const cached = await readSkillManifestAsync(manifestPath, root, discovery);
|
|
494
|
+
const manifest = cached ?? await scanSkillManifestAsync(root, discovery);
|
|
495
|
+
if (!cached) await writeSkillManifestAsync(manifestPath, manifest);
|
|
496
|
+
return manifest.files.map(({ name, path: filePath, description, digest }) => ({
|
|
497
|
+
name,
|
|
498
|
+
path: filePath,
|
|
499
|
+
digest,
|
|
500
|
+
...description === void 0 ? {} : { description }
|
|
501
|
+
}));
|
|
502
|
+
}
|
|
503
|
+
async function markdownFilesAsync(directory) {
|
|
504
|
+
let entries;
|
|
505
|
+
try {
|
|
506
|
+
entries = await node_fs.default.promises.readdir(directory, { withFileTypes: true });
|
|
507
|
+
} catch (error) {
|
|
508
|
+
if (error.code === "ENOENT") return [];
|
|
509
|
+
throw error;
|
|
510
|
+
}
|
|
511
|
+
const results = [];
|
|
512
|
+
for (const entry of entries) {
|
|
513
|
+
const candidate = node_path.default.join(directory, entry.name);
|
|
514
|
+
if (entry.isDirectory()) results.push(...await markdownFilesAsync(candidate));
|
|
515
|
+
if (entry.isFile() && entry.name.endsWith(".md")) results.push(candidate);
|
|
516
|
+
}
|
|
517
|
+
return results;
|
|
518
|
+
}
|
|
519
|
+
async function frontmatterNameAsync(filePath) {
|
|
520
|
+
const { attributes } = splitFrontmatter(await node_fs.default.promises.readFile(filePath, "utf8"));
|
|
521
|
+
if (typeof attributes.name !== "string") throw new Error(`Resource requires a name: ${filePath}`);
|
|
522
|
+
return attributes.name;
|
|
523
|
+
}
|
|
524
|
+
async function assertUniqueAsync(resources, kind) {
|
|
525
|
+
const seen = /* @__PURE__ */ new Map();
|
|
526
|
+
const digestByName = /* @__PURE__ */ new Map();
|
|
527
|
+
for (const resource of resources) {
|
|
528
|
+
const previous = seen.get(resource.name);
|
|
529
|
+
const digest = resource.digest ?? (0, node_crypto.createHash)("sha256").update(await node_fs.default.promises.readFile(resource.path)).digest("hex");
|
|
530
|
+
if (previous && digestByName.get(resource.name) !== digest) throw new Error(`Conflicting ${kind} "${resource.name}": ${previous} and ${resource.path}`);
|
|
531
|
+
seen.set(resource.name, resource.path);
|
|
532
|
+
digestByName.set(resource.name, digest);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function dispatcherAgentSource() {
|
|
536
|
+
const attributes = {
|
|
537
|
+
name: DISPATCHER_AGENT_NAME,
|
|
538
|
+
description: "Selects and launches Agiflow workflows for one loop pass without implementing the work.",
|
|
539
|
+
thinking: "high",
|
|
540
|
+
systemPromptMode: "replace",
|
|
541
|
+
inheritProjectContext: true,
|
|
542
|
+
inheritSkills: true,
|
|
543
|
+
tools: DISPATCHER_TOOLS.join(", "),
|
|
544
|
+
defaultContext: "fresh",
|
|
545
|
+
defaultProgress: false
|
|
546
|
+
};
|
|
547
|
+
const body = [
|
|
548
|
+
"You are the Agiflow workflow dispatcher.",
|
|
549
|
+
"Reason about the supplied dispatch context and launch eligible workflows.",
|
|
550
|
+
"Launch workflows without monitoring them. After dispatching, report and exit.",
|
|
551
|
+
"Follow the task restrictions exactly. Do not implement jobs or edit repository files."
|
|
552
|
+
].join("\n");
|
|
553
|
+
return `---\n${(0, yaml.stringify)(attributes).trim()}\n---\n\n${body}\n`;
|
|
554
|
+
}
|
|
555
|
+
function mergeMcpConfigs(configPaths) {
|
|
556
|
+
const merged = { mcpServers: {} };
|
|
557
|
+
const servers = merged.mcpServers;
|
|
558
|
+
const sources = /* @__PURE__ */ new Map();
|
|
559
|
+
for (const configPath of configPaths) {
|
|
560
|
+
if (!node_fs.default.existsSync(configPath)) continue;
|
|
561
|
+
const incoming = JSON.parse(node_fs.default.readFileSync(configPath, "utf8")).mcpServers ?? {};
|
|
562
|
+
for (const [name, definition] of Object.entries(incoming)) {
|
|
563
|
+
const previous = servers[name];
|
|
564
|
+
if (previous && JSON.stringify(previous) !== JSON.stringify(definition)) throw new Error(`Conflicting MCP server "${name}": ${sources.get(name)} and ${configPath}`);
|
|
565
|
+
servers[name] = definition;
|
|
566
|
+
sources.set(name, configPath);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return merged;
|
|
570
|
+
}
|
|
571
|
+
async function mergeMcpConfigsAsync(configPaths) {
|
|
572
|
+
const merged = { mcpServers: {} };
|
|
573
|
+
const servers = merged.mcpServers;
|
|
574
|
+
const sources = /* @__PURE__ */ new Map();
|
|
575
|
+
for (const configPath of configPaths) {
|
|
576
|
+
let content;
|
|
577
|
+
try {
|
|
578
|
+
content = await node_fs.default.promises.readFile(configPath, "utf8");
|
|
579
|
+
} catch (error) {
|
|
580
|
+
if (error.code === "ENOENT") continue;
|
|
581
|
+
throw error;
|
|
582
|
+
}
|
|
583
|
+
const incoming = JSON.parse(content).mcpServers ?? {};
|
|
584
|
+
for (const [name, definition] of Object.entries(incoming)) {
|
|
585
|
+
const previous = servers[name];
|
|
586
|
+
if (previous && JSON.stringify(previous) !== JSON.stringify(definition)) throw new Error(`Conflicting MCP server "${name}": ${sources.get(name)} and ${configPath}`);
|
|
587
|
+
servers[name] = definition;
|
|
588
|
+
sources.set(name, configPath);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return merged;
|
|
592
|
+
}
|
|
593
|
+
async function mergeProjectedMcpConfigs(sources, stagingDirectory) {
|
|
594
|
+
const merged = { mcpServers: {} };
|
|
595
|
+
const servers = merged.mcpServers;
|
|
596
|
+
const origins = /* @__PURE__ */ new Map();
|
|
597
|
+
for (const source of sources) {
|
|
598
|
+
let config;
|
|
599
|
+
if (source.format === "agent-plugin-v1") config = (0, _agimon_ai_po_config_agentPluginMcp.normalizeAgentPluginMcpSource)(source, {
|
|
600
|
+
stagingDirectory,
|
|
601
|
+
stripProxyWrappers: false
|
|
602
|
+
}).claudeConfig;
|
|
603
|
+
else {
|
|
604
|
+
let content;
|
|
605
|
+
try {
|
|
606
|
+
content = await node_fs.default.promises.readFile(source.configPath, "utf8");
|
|
607
|
+
} catch (error) {
|
|
608
|
+
if (error.code === "ENOENT") continue;
|
|
609
|
+
throw error;
|
|
610
|
+
}
|
|
611
|
+
config = JSON.parse(content);
|
|
612
|
+
}
|
|
613
|
+
const incoming = config.mcpServers ?? {};
|
|
614
|
+
for (const [name, definition] of Object.entries(incoming)) {
|
|
615
|
+
const previous = servers[name];
|
|
616
|
+
if (previous && JSON.stringify(previous) !== JSON.stringify(definition)) throw new Error(`Conflicting MCP server "${name}": ${origins.get(name)} and ${source.configPath}`);
|
|
617
|
+
servers[name] = definition;
|
|
618
|
+
origins.set(name, source.configPath);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return merged;
|
|
622
|
+
}
|
|
623
|
+
function escapeRegExp(value) {
|
|
624
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
625
|
+
}
|
|
626
|
+
/** Matches a resource name against a subset filter. Undefined keeps everything. */
|
|
627
|
+
function keepsName(allowed, name) {
|
|
628
|
+
if (!allowed) return true;
|
|
629
|
+
return allowed.some((pattern) => pattern.includes("*") ? new RegExp(`^${pattern.split("*").map(escapeRegExp).join(".*")}$`).test(name) : pattern === name);
|
|
630
|
+
}
|
|
631
|
+
/** Stages only the MCP slice shared by Po and compatibility frontends. */
|
|
632
|
+
async function stageMcpResources(repoRoot, plugins, options) {
|
|
633
|
+
const pluginEntries = plugins.map((plugin) => typeof plugin === "string" ? { directory: plugin } : plugin);
|
|
634
|
+
const pluginMcpConfigPaths = [];
|
|
635
|
+
const pluginMcpSources = [];
|
|
636
|
+
const mcpSources = [];
|
|
637
|
+
const pluginDataRoot = options.pluginDataRoot ?? node_path.default.join((0, _agimon_ai_po_config_config.globalDoomConfigDirectory)(), PLUGIN_DATA_DIRECTORY);
|
|
638
|
+
const repositoryMcpPath = node_path.default.join(repoRoot, ".mcp.json");
|
|
639
|
+
await node_fs.default.promises.mkdir(options.temporaryDirectory, {
|
|
640
|
+
mode: 448,
|
|
641
|
+
recursive: true
|
|
642
|
+
});
|
|
643
|
+
await node_fs.default.promises.chmod(options.temporaryDirectory, 448);
|
|
644
|
+
if (options.enabled && await pathExists(repositoryMcpPath)) {
|
|
645
|
+
const content = await node_fs.default.promises.readFile(repositoryMcpPath);
|
|
646
|
+
mcpSources.push({
|
|
647
|
+
sourceId: "repository:.mcp.json",
|
|
648
|
+
owner: "repository",
|
|
649
|
+
format: "native",
|
|
650
|
+
configPath: repositoryMcpPath,
|
|
651
|
+
contentDigest: (0, node_crypto.createHash)("sha256").update(content).digest("hex")
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
if (options.enabled) for (const entry of pluginEntries) {
|
|
655
|
+
const source = await pluginMcpSource(entry, pluginDataRoot);
|
|
656
|
+
if (!source) continue;
|
|
657
|
+
mcpSources.push(source);
|
|
658
|
+
pluginMcpSources.push(source);
|
|
659
|
+
pluginMcpConfigPaths.push(source.configPath);
|
|
660
|
+
}
|
|
661
|
+
let mcpConfigPath;
|
|
662
|
+
if (options.enabled) {
|
|
663
|
+
mcpConfigPath = node_path.default.join(options.temporaryDirectory, "mcp.json");
|
|
664
|
+
const merged = await require_mcpFilter.applyMcpAllowlist(await mergeProjectedMcpConfigs(mcpSources, options.temporaryDirectory), options.mcpAllowlist, repoRoot, options.temporaryDirectory);
|
|
665
|
+
await node_fs.default.promises.writeFile(mcpConfigPath, `${JSON.stringify(merged, null, 2)}\n`, { mode: 384 });
|
|
666
|
+
}
|
|
667
|
+
const projectionWithoutRuntimePath = {
|
|
668
|
+
version: _agimon_ai_po_core_mcpProjection.DOOM_MCP_PROJECTION_VERSION,
|
|
669
|
+
enabled: options.enabled,
|
|
670
|
+
repoRoot,
|
|
671
|
+
...mcpConfigPath ? { generatedConfigPath: mcpConfigPath } : {},
|
|
672
|
+
sources: options.enabled ? mcpSources : [],
|
|
673
|
+
...options.mcpAllowlist ? { allowlist: structuredClone(options.mcpAllowlist) } : {}
|
|
674
|
+
};
|
|
675
|
+
return {
|
|
676
|
+
mcpConfigPath,
|
|
677
|
+
mcpProjection: {
|
|
678
|
+
...projectionWithoutRuntimePath,
|
|
679
|
+
stagingDirectory: options.temporaryDirectory,
|
|
680
|
+
fingerprint: projectionFingerprint(projectionWithoutRuntimePath)
|
|
681
|
+
},
|
|
682
|
+
pluginMcpSources,
|
|
683
|
+
pluginMcpConfigPaths
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Stages every resource the selected domains contribute, ready for one Pi session.
|
|
688
|
+
*
|
|
689
|
+
* The caller owns the returned cleanup(): the temporary directory holding agents
|
|
690
|
+
* and the generated MCP config has to outlive this function and be removed once
|
|
691
|
+
* the session that reads it is done.
|
|
692
|
+
*/
|
|
693
|
+
async function collectResources(repoRoot, plugins, options) {
|
|
694
|
+
const pluginEntries = plugins.map((plugin) => typeof plugin === "string" ? { directory: plugin } : plugin);
|
|
695
|
+
const ownsDirectory = options.temporaryDirectory === void 0;
|
|
696
|
+
const temporaryDirectory = options.temporaryDirectory ?? await node_fs.default.promises.mkdtemp(node_path.default.join(node_os.default.tmpdir(), "po-"));
|
|
697
|
+
await node_fs.default.promises.mkdir(temporaryDirectory, {
|
|
698
|
+
mode: 448,
|
|
699
|
+
recursive: true
|
|
700
|
+
});
|
|
701
|
+
await node_fs.default.promises.chmod(temporaryDirectory, 448);
|
|
702
|
+
const sharedSkills = node_path.default.join(repoRoot, ".claude", "skills");
|
|
703
|
+
const sharedSkillSources = options.sharedSkills === false ? [] : await discoverSkillsAsync(repoRoot, sharedSkills, "recursive", options.skillCacheDirectory);
|
|
704
|
+
const skillDirectories = [];
|
|
705
|
+
const skillSources = [];
|
|
706
|
+
const agentSources = [];
|
|
707
|
+
const pluginHooks = [];
|
|
708
|
+
for (const entry of pluginEntries) {
|
|
709
|
+
const pluginDirectory = entry.directory;
|
|
710
|
+
const skills = node_path.default.join(pluginDirectory, "skills");
|
|
711
|
+
if (await pathExists(skills)) {
|
|
712
|
+
const found = (await discoverSkillsAsync(repoRoot, skills, entry.skillDiscovery ?? "recursive", options.skillCacheDirectory)).filter((skill) => keepsName(entry.skills, skill.name));
|
|
713
|
+
skillDirectories.push(...found.map((skill) => skill.path));
|
|
714
|
+
skillSources.push(...found);
|
|
715
|
+
}
|
|
716
|
+
const agentFiles = await markdownFilesAsync(node_path.default.join(pluginDirectory, "agents"));
|
|
717
|
+
const agentNames = await Promise.all(agentFiles.map((filePath) => frontmatterNameAsync(filePath)));
|
|
718
|
+
agentSources.push(...agentFiles.map((filePath, index) => ({
|
|
719
|
+
name: agentNames[index] ?? "",
|
|
720
|
+
path: filePath
|
|
721
|
+
})).filter((agent) => keepsName(entry.agents, agent.name)));
|
|
722
|
+
const hookConfig = node_path.default.join(pluginDirectory, "hooks", "hooks.json");
|
|
723
|
+
if (entry.hooks !== false && await pathExists(hookConfig)) pluginHooks.push({
|
|
724
|
+
pluginRoot: pluginDirectory,
|
|
725
|
+
configPath: hookConfig
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
await assertUniqueAsync(skillSources, "skill");
|
|
729
|
+
await assertUniqueAsync(sharedSkillSources, "skill");
|
|
730
|
+
const pluginSkillNames = new Set(skillSources.map((skill) => skill.name));
|
|
731
|
+
const fallbackSharedSkills = sharedSkillSources.filter((skill) => !pluginSkillNames.has(skill.name));
|
|
732
|
+
skillDirectories.push(...fallbackSharedSkills.map((skill) => skill.path));
|
|
733
|
+
skillSources.push(...fallbackSharedSkills);
|
|
734
|
+
await assertUniqueAsync(agentSources, "agent");
|
|
735
|
+
if (agentSources.some((source) => source.name === "agiflow-dispatcher")) throw new Error(`Conflicting agent "${DISPATCHER_AGENT_NAME}": reserved by the agent harness`);
|
|
736
|
+
const agentDirectories = [];
|
|
737
|
+
const outputDirectory = node_path.default.join(temporaryDirectory, "agents");
|
|
738
|
+
await node_fs.default.promises.rm(outputDirectory, {
|
|
739
|
+
recursive: true,
|
|
740
|
+
force: true
|
|
741
|
+
});
|
|
742
|
+
if (options.agents) {
|
|
743
|
+
await node_fs.default.promises.mkdir(outputDirectory, { mode: 448 });
|
|
744
|
+
for (const source of agentSources) {
|
|
745
|
+
const adapted = adaptAgentDefinition(await node_fs.default.promises.readFile(source.path, "utf8"));
|
|
746
|
+
await node_fs.default.promises.writeFile(node_path.default.join(outputDirectory, `${assertSafeAgentName(adapted.name)}.md`), adapted.content, { mode: 384 });
|
|
747
|
+
}
|
|
748
|
+
await node_fs.default.promises.writeFile(node_path.default.join(outputDirectory, `${DISPATCHER_AGENT_NAME}.md`), dispatcherAgentSource(), { mode: 384 });
|
|
749
|
+
agentDirectories.push(outputDirectory);
|
|
750
|
+
}
|
|
751
|
+
const { mcpConfigPath, mcpProjection, pluginMcpSources, pluginMcpConfigPaths } = await stageMcpResources(repoRoot, pluginEntries, {
|
|
752
|
+
enabled: options.mcp,
|
|
753
|
+
temporaryDirectory,
|
|
754
|
+
...options.mcpAllowlist ? { mcpAllowlist: options.mcpAllowlist } : {},
|
|
755
|
+
...options.pluginDataRoot ? { pluginDataRoot: options.pluginDataRoot } : {}
|
|
756
|
+
});
|
|
757
|
+
return {
|
|
758
|
+
temporaryDirectory,
|
|
759
|
+
skillDirectories,
|
|
760
|
+
skillSources,
|
|
761
|
+
skillCount: skillSources.length,
|
|
762
|
+
agentCount: agentSources.length,
|
|
763
|
+
agentDirectories,
|
|
764
|
+
pluginHooks,
|
|
765
|
+
mcpConfigPath,
|
|
766
|
+
mcpProjection,
|
|
767
|
+
pluginMcpSources,
|
|
768
|
+
pluginMcpConfigPaths,
|
|
769
|
+
cleanup: () => ownsDirectory ? node_fs.default.promises.rm(temporaryDirectory, {
|
|
770
|
+
recursive: true,
|
|
771
|
+
force: true
|
|
772
|
+
}) : Promise.resolve()
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
//#endregion
|
|
776
|
+
Object.defineProperty(exports, "DISPATCHER_AGENT_NAME", {
|
|
777
|
+
enumerable: true,
|
|
778
|
+
get: function() {
|
|
779
|
+
return DISPATCHER_AGENT_NAME;
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
Object.defineProperty(exports, "adaptAgentDefinition", {
|
|
783
|
+
enumerable: true,
|
|
784
|
+
get: function() {
|
|
785
|
+
return adaptAgentDefinition;
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
Object.defineProperty(exports, "collectResources", {
|
|
789
|
+
enumerable: true,
|
|
790
|
+
get: function() {
|
|
791
|
+
return collectResources;
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
Object.defineProperty(exports, "discoverSkills", {
|
|
795
|
+
enumerable: true,
|
|
796
|
+
get: function() {
|
|
797
|
+
return discoverSkills;
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
Object.defineProperty(exports, "discoverSkillsAsync", {
|
|
801
|
+
enumerable: true,
|
|
802
|
+
get: function() {
|
|
803
|
+
return discoverSkillsAsync;
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
Object.defineProperty(exports, "mergeMcpConfigs", {
|
|
807
|
+
enumerable: true,
|
|
808
|
+
get: function() {
|
|
809
|
+
return mergeMcpConfigs;
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
Object.defineProperty(exports, "mergeMcpConfigsAsync", {
|
|
813
|
+
enumerable: true,
|
|
814
|
+
get: function() {
|
|
815
|
+
return mergeMcpConfigsAsync;
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
Object.defineProperty(exports, "resolveSkillCacheDirectory", {
|
|
819
|
+
enumerable: true,
|
|
820
|
+
get: function() {
|
|
821
|
+
return resolveSkillCacheDirectory;
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
Object.defineProperty(exports, "sanitizeSyncLabel", {
|
|
825
|
+
enumerable: true,
|
|
826
|
+
get: function() {
|
|
827
|
+
return sanitizeSyncLabel;
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
Object.defineProperty(exports, "stageMcpResources", {
|
|
831
|
+
enumerable: true,
|
|
832
|
+
get: function() {
|
|
833
|
+
return stageMcpResources;
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
//# sourceMappingURL=resourceCollector-Cu-lp5hJ.cjs.map
|