@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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/dist/apiContracts.cjs +17 -0
  4. package/dist/apiContracts.cjs.map +1 -0
  5. package/dist/apiContracts.d.mts +53 -0
  6. package/dist/apiContracts.d.mts.map +1 -0
  7. package/dist/apiContracts.mjs +13 -0
  8. package/dist/apiContracts.mjs.map +1 -0
  9. package/dist/apply.cjs +76 -0
  10. package/dist/apply.cjs.map +1 -0
  11. package/dist/apply.d.mts +23 -0
  12. package/dist/apply.d.mts.map +1 -0
  13. package/dist/apply.mjs +72 -0
  14. package/dist/apply.mjs.map +1 -0
  15. package/dist/domainText-Dm8k7p4z.mjs +97 -0
  16. package/dist/domainText-Dm8k7p4z.mjs.map +1 -0
  17. package/dist/domainText-UIRIz6-_.cjs +204 -0
  18. package/dist/domainText-UIRIz6-_.cjs.map +1 -0
  19. package/dist/extensions/mcp.cjs +26 -0
  20. package/dist/extensions/mcp.cjs.map +1 -0
  21. package/dist/extensions/mcp.d.mts +5 -0
  22. package/dist/extensions/mcp.d.mts.map +1 -0
  23. package/dist/extensions/mcp.mjs +21 -0
  24. package/dist/extensions/mcp.mjs.map +1 -0
  25. package/dist/extensions/server.cjs +229 -0
  26. package/dist/extensions/server.cjs.map +1 -0
  27. package/dist/extensions/server.d.mts +5 -0
  28. package/dist/extensions/server.d.mts.map +1 -0
  29. package/dist/extensions/server.mjs +221 -0
  30. package/dist/extensions/server.mjs.map +1 -0
  31. package/dist/extensions/web.mjs +26 -0
  32. package/dist/extensions/web.mjs.map +1 -0
  33. package/dist/index.cjs +19 -0
  34. package/dist/index.d.mts +84 -0
  35. package/dist/index.d.mts.map +1 -0
  36. package/dist/index.mjs +3 -0
  37. package/dist/mcp.cjs +32 -0
  38. package/dist/mcp.cjs.map +1 -0
  39. package/dist/mcp.d.mts +52 -0
  40. package/dist/mcp.d.mts.map +1 -0
  41. package/dist/mcp.mjs +19 -0
  42. package/dist/mcp.mjs.map +1 -0
  43. package/dist/mcpFilter-CZNmU_VF.cjs +189 -0
  44. package/dist/mcpFilter-CZNmU_VF.cjs.map +1 -0
  45. package/dist/mcpFilter-Cf-czy8O.mjs +151 -0
  46. package/dist/mcpFilter-Cf-czy8O.mjs.map +1 -0
  47. package/dist/pluginMaterializer-C8IZ1PSY.cjs +267 -0
  48. package/dist/pluginMaterializer-C8IZ1PSY.cjs.map +1 -0
  49. package/dist/pluginMaterializer-D9-oDF-b.mjs +259 -0
  50. package/dist/pluginMaterializer-D9-oDF-b.mjs.map +1 -0
  51. package/dist/plugins.cjs +3 -0
  52. package/dist/plugins.d.mts +14 -0
  53. package/dist/plugins.d.mts.map +1 -0
  54. package/dist/plugins.mjs +2 -0
  55. package/dist/resourceCollector-CRY5cmeY.mjs +773 -0
  56. package/dist/resourceCollector-CRY5cmeY.mjs.map +1 -0
  57. package/dist/resourceCollector-Cu-lp5hJ.cjs +837 -0
  58. package/dist/resourceCollector-Cu-lp5hJ.cjs.map +1 -0
  59. package/dist/resources-BEjxE3Zn.d.mts +66 -0
  60. package/dist/resources-BEjxE3Zn.d.mts.map +1 -0
  61. package/dist/resources.cjs +12 -0
  62. package/dist/resources.d.mts +43 -0
  63. package/dist/resources.d.mts.map +1 -0
  64. package/dist/resources.mjs +2 -0
  65. package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
  66. package/dist/toolNames-Ckp5VLMD.mjs +43 -0
  67. package/dist/toolNames-Ckp5VLMD.mjs.map +1 -0
  68. package/dist/toolNames-Cuu-u8r1.cjs +48 -0
  69. package/dist/toolNames-Cuu-u8r1.cjs.map +1 -0
  70. package/llms.txt +12 -0
  71. package/package.json +158 -0
  72. package/src/prompts/po-author-domain/SKILL.md +18 -0
  73. 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