agentloom 0.1.5 → 0.1.7

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.
@@ -56,55 +56,179 @@ export function prepareSource(options) {
56
56
  },
57
57
  };
58
58
  }
59
+ export function discoverPluginSourceRoots(importRoot) {
60
+ const marketplacePath = path.join(importRoot, ".claude-plugin", "marketplace.json");
61
+ if (!fs.existsSync(marketplacePath) ||
62
+ !fs.statSync(marketplacePath).isFile()) {
63
+ return [];
64
+ }
65
+ let parsed;
66
+ try {
67
+ parsed = JSON.parse(fs.readFileSync(marketplacePath, "utf8"));
68
+ }
69
+ catch {
70
+ return [];
71
+ }
72
+ const plugins = Array.isArray(parsed?.plugins)
73
+ ? parsed.plugins
74
+ : [];
75
+ const discoveredRoots = [];
76
+ for (const plugin of plugins) {
77
+ if (!plugin ||
78
+ typeof plugin !== "object" ||
79
+ Array.isArray(plugin) ||
80
+ typeof plugin.source !== "string") {
81
+ continue;
82
+ }
83
+ const source = plugin.source.trim();
84
+ if (!source)
85
+ continue;
86
+ const pluginRoot = path.resolve(importRoot, source);
87
+ if (!isPathWithinRoot(importRoot, pluginRoot)) {
88
+ continue;
89
+ }
90
+ if (!fs.existsSync(pluginRoot) || !fs.statSync(pluginRoot).isDirectory()) {
91
+ continue;
92
+ }
93
+ discoveredRoots.push(pluginRoot);
94
+ }
95
+ return dedupePaths(discoveredRoots);
96
+ }
59
97
  export function discoverSourceAgentsDir(importRoot) {
60
- const direct = path.join(importRoot, "agents");
61
- if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
98
+ return discoverSourceAgentsDirs(importRoot)[0] ?? null;
99
+ }
100
+ export function discoverSourceAgentsDirs(importRoot) {
101
+ const direct = discoverSourceAgentsDirsForRoot(importRoot);
102
+ if (direct.length > 0) {
62
103
  return direct;
63
104
  }
64
- const nested = path.join(importRoot, ".agents", "agents");
65
- if (fs.existsSync(nested) && fs.statSync(nested).isDirectory()) {
66
- return nested;
67
- }
68
- return null;
105
+ return dedupePaths(discoverPluginSourceRoots(importRoot).flatMap((pluginRoot) => discoverSourceAgentsDirsForRoot(pluginRoot)));
69
106
  }
70
107
  export function discoverSourceMcpPath(importRoot) {
71
- const nested = path.join(importRoot, ".agents", "mcp.json");
72
- if (fs.existsSync(nested))
73
- return nested;
74
- const direct = path.join(importRoot, "mcp.json");
75
- if (fs.existsSync(direct))
108
+ return discoverSourceMcpPaths(importRoot)[0] ?? null;
109
+ }
110
+ export function discoverSourceMcpPaths(importRoot) {
111
+ const direct = discoverSourceMcpPathsForRoot(importRoot);
112
+ if (direct.length > 0) {
76
113
  return direct;
77
- return null;
114
+ }
115
+ return dedupePaths(discoverPluginSourceRoots(importRoot).flatMap((pluginRoot) => discoverSourceMcpPathsForRoot(pluginRoot)));
78
116
  }
79
- export function discoverSourceCommandsDir(importRoot) {
117
+ export function discoverSourceCommandsDirs(importRoot) {
118
+ const direct = discoverSourceCommandsDirsForRoot(importRoot);
119
+ if (direct.length > 0) {
120
+ return direct;
121
+ }
122
+ return dedupePaths(discoverPluginSourceRoots(importRoot).flatMap((pluginRoot) => discoverSourceCommandsDirsForRoot(pluginRoot)));
123
+ }
124
+ function discoverSourceCommandsDirsForRoot(importRoot) {
80
125
  const nested = path.join(importRoot, ".agents", "commands");
81
126
  if (fs.existsSync(nested) && fs.statSync(nested).isDirectory()) {
82
- return nested;
127
+ return [nested];
83
128
  }
84
129
  const direct = path.join(importRoot, "commands");
85
130
  if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
86
- return direct;
131
+ return [direct];
87
132
  }
88
133
  const prompts = path.join(importRoot, "prompts");
89
134
  if (fs.existsSync(prompts) && fs.statSync(prompts).isDirectory()) {
90
- return prompts;
135
+ return [prompts];
136
+ }
137
+ const providerFallbacks = [];
138
+ const githubPrompts = path.join(importRoot, ".github", "prompts");
139
+ if (fs.existsSync(githubPrompts) &&
140
+ fs.statSync(githubPrompts).isDirectory()) {
141
+ providerFallbacks.push(githubPrompts);
142
+ }
143
+ const geminiCommands = path.join(importRoot, ".gemini", "commands");
144
+ if (fs.existsSync(geminiCommands) &&
145
+ fs.statSync(geminiCommands).isDirectory()) {
146
+ providerFallbacks.push(geminiCommands);
91
147
  }
92
- return null;
148
+ return providerFallbacks;
149
+ }
150
+ export function discoverSourceCommandsDir(importRoot) {
151
+ return discoverSourceCommandsDirs(importRoot)[0] ?? null;
93
152
  }
94
153
  export function discoverSourceSkillsDir(importRoot) {
154
+ return discoverSourceSkillsDirs(importRoot)[0] ?? null;
155
+ }
156
+ export function discoverSourceSkillsDirs(importRoot) {
157
+ const direct = discoverSourceSkillsDirsForRoot(importRoot);
158
+ if (direct.length > 0) {
159
+ return direct;
160
+ }
161
+ return dedupePaths(discoverPluginSourceRoots(importRoot).flatMap((pluginRoot) => discoverSourceSkillsDirsForRoot(pluginRoot)));
162
+ }
163
+ export function discoverSourceRulesDir(importRoot) {
164
+ return discoverSourceRulesDirs(importRoot)[0] ?? null;
165
+ }
166
+ export function discoverSourceRulesDirs(importRoot) {
167
+ const direct = discoverSourceRulesDirsForRoot(importRoot);
168
+ if (direct.length > 0) {
169
+ return direct;
170
+ }
171
+ return dedupePaths(discoverPluginSourceRoots(importRoot).flatMap((pluginRoot) => discoverSourceRulesDirsForRoot(pluginRoot)));
172
+ }
173
+ function discoverSourceAgentsDirsForRoot(importRoot) {
174
+ const direct = path.join(importRoot, "agents");
175
+ if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
176
+ return [direct];
177
+ }
178
+ const nested = path.join(importRoot, ".agents", "agents");
179
+ if (fs.existsSync(nested) && fs.statSync(nested).isDirectory()) {
180
+ return [nested];
181
+ }
182
+ const githubAgents = path.join(importRoot, ".github", "agents");
183
+ if (fs.existsSync(githubAgents) && fs.statSync(githubAgents).isDirectory()) {
184
+ return [githubAgents];
185
+ }
186
+ return [];
187
+ }
188
+ function discoverSourceMcpPathsForRoot(importRoot) {
189
+ const nested = path.join(importRoot, ".agents", "mcp.json");
190
+ if (fs.existsSync(nested) && fs.statSync(nested).isFile()) {
191
+ return [nested];
192
+ }
193
+ const direct = path.join(importRoot, "mcp.json");
194
+ if (fs.existsSync(direct) && fs.statSync(direct).isFile()) {
195
+ return [direct];
196
+ }
197
+ return [];
198
+ }
199
+ function discoverSourceSkillsDirsForRoot(importRoot) {
95
200
  const nested = path.join(importRoot, ".agents", "skills");
96
201
  if (fs.existsSync(nested) && fs.statSync(nested).isDirectory()) {
97
- return nested;
202
+ return [nested];
98
203
  }
99
204
  const direct = path.join(importRoot, "skills");
100
205
  if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
101
- return direct;
206
+ return [direct];
102
207
  }
103
208
  const rootSkill = path.join(importRoot, "SKILL.md");
104
209
  if (fs.existsSync(rootSkill) && fs.statSync(rootSkill).isFile()) {
105
- return importRoot;
210
+ return [importRoot];
106
211
  }
107
- return null;
212
+ return [];
213
+ }
214
+ function discoverSourceRulesDirsForRoot(importRoot) {
215
+ const nested = path.join(importRoot, ".agents", "rules");
216
+ if (fs.existsSync(nested) && fs.statSync(nested).isDirectory()) {
217
+ return [nested];
218
+ }
219
+ const direct = path.join(importRoot, "rules");
220
+ if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
221
+ return [direct];
222
+ }
223
+ return [];
224
+ }
225
+ function dedupePaths(paths) {
226
+ return [...new Set(paths)];
227
+ }
228
+ function isPathWithinRoot(rootPath, targetPath) {
229
+ const relative = path.relative(rootPath, targetPath);
230
+ return (relative === "" ||
231
+ (!relative.startsWith("..") && !path.isAbsolute(relative)));
108
232
  }
109
233
  function resolveImportRoot(rootPath, subdir) {
110
234
  if (!subdir)
@@ -4,7 +4,7 @@ export type TelemetrySource = {
4
4
  repo: string;
5
5
  };
6
6
  export type TelemetryItem = {
7
- entityType: "agent" | "skill" | "command" | "mcp";
7
+ entityType: "agent" | "skill" | "command" | "mcp" | "rule";
8
8
  name: string;
9
9
  filePath: string;
10
10
  };
@@ -47,6 +47,7 @@ export function parseGitHubSource(input) {
47
47
  }
48
48
  export function buildTelemetryItems(summary) {
49
49
  const items = [];
50
+ const importedRules = summary.importedRules ?? [];
50
51
  for (const filePath of summary.importedAgents) {
51
52
  const name = path.basename(filePath, path.extname(filePath));
52
53
  items.push({ entityType: "agent", name, filePath });
@@ -58,6 +59,21 @@ export function buildTelemetryItems(summary) {
58
59
  for (const serverName of summary.importedMcpServers) {
59
60
  items.push({ entityType: "mcp", name: serverName, filePath: "mcp.json" });
60
61
  }
62
+ if (summary.telemetryRules && summary.telemetryRules.length > 0) {
63
+ for (const rule of summary.telemetryRules) {
64
+ items.push({
65
+ entityType: "rule",
66
+ name: rule.name,
67
+ filePath: rule.filePath.replace(/^\/+/, ""),
68
+ });
69
+ }
70
+ }
71
+ else {
72
+ for (const filePath of importedRules) {
73
+ const name = path.basename(filePath, path.extname(filePath));
74
+ items.push({ entityType: "rule", name, filePath });
75
+ }
76
+ }
61
77
  if (summary.telemetrySkills && summary.telemetrySkills.length > 0) {
62
78
  for (const skill of summary.telemetrySkills) {
63
79
  items.push({