@bahulam/code 0.1.14 → 0.1.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bahulam/code",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Bahulam Code — abundance, in your terminal. CLI-first, reliability-first, sub-agents, 65.6% SWE-bench Verified.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -40,6 +40,9 @@ const CYAN = '\x1b[36m';
40
40
  const GREEN = '\x1b[32m';
41
41
  const YELLOW = '\x1b[33m';
42
42
 
43
+ const DEFAULT_BAHULAM_PLUGIN_REGISTRY_URL =
44
+ 'https://raw.githubusercontent.com/BahulamAI/awesome-bahulam-plugins/main/registry.json';
45
+
43
46
  function parseArgs(argv) {
44
47
  const parsed = {
45
48
  source: null,
@@ -152,10 +155,13 @@ export async function handleInstallCommand(argv, { cwd = process.cwd() } = {}) {
152
155
 
153
156
  Install a pack. For pi sources, pulls the ingredient and scaffolds a
154
157
  full Bahulam pack (composition + state layer + workspace + agent), then
155
- installs it. For git/tarball/local sources, installs the existing pack.
158
+ installs it. For registry/git/tarball/local sources, installs the
159
+ existing pack.
156
160
 
157
161
  Sources:
158
162
  pi:<npm-package>[@<version>] Scaffold + install from a pi package
163
+ <registry-name> Install from awesome-bahulam-plugins
164
+ bahulam:<registry-name> Explicit awesome-bahulam-plugins lookup
159
165
  <git-url>[.git] Clone a hand-authored pack
160
166
  <tarball-url> Download + install a pack tarball
161
167
  <local-path> Copy + install a local pack directory
@@ -166,7 +172,7 @@ export async function handleInstallCommand(argv, { cwd = process.cwd() } = {}) {
166
172
  --no-state Skip the persistent state layer (pi sources only)
167
173
  --no-workspace Skip the reactive workspace panel (pi sources only)
168
174
  --project Install into ./.bahulam/plugins/ instead of ~/.bahulam/plugins/
169
- --ref <ref> Git branch/tag/commit (git sources only)
175
+ --ref <ref> Git branch/tag/commit (git or registry sources)
170
176
  --json Machine-readable output
171
177
 
172
178
  `);
@@ -186,6 +192,12 @@ export async function handleInstallCommand(argv, { cwd = process.cwd() } = {}) {
186
192
  throw new Error(`unrecognized source: ${args.source}`);
187
193
  }
188
194
 
195
+ const registryName = registryNameFromSource(args.source, classified);
196
+ if (registryName) {
197
+ await installFromBahulamRegistry({ name: registryName, targetDir, cwd, args });
198
+ return;
199
+ }
200
+
189
201
  // Non-pi paths reuse the plugin-manage install machinery.
190
202
  let dest;
191
203
  if (classified.kind === 'git') {
@@ -194,8 +206,6 @@ export async function handleInstallCommand(argv, { cwd = process.cwd() } = {}) {
194
206
  dest = await installFromTarball({ url: classified.url, targetDir, force: args.force });
195
207
  } else if (classified.kind === 'local') {
196
208
  dest = await installFromLocal({ src: classified.path, targetDir, force: args.force });
197
- } else if (classified.kind === 'name') {
198
- throw new Error(`registry lookup for bare names is not yet wired into \`bahulam install\`. Provide a git URL, tarball URL, local path, or pi: source.`);
199
209
  } else {
200
210
  throw new Error(`could not resolve source: ${args.source}`);
201
211
  }
@@ -207,6 +217,118 @@ export async function handleInstallCommand(argv, { cwd = process.cwd() } = {}) {
207
217
  }
208
218
  }
209
219
 
220
+ export function registryNameFromSource(source, classified = null) {
221
+ const raw = String(source || '').trim();
222
+ const prefix = 'bahulam:';
223
+ if (raw.toLowerCase().startsWith(prefix)) {
224
+ const name = raw.slice(prefix.length).trim();
225
+ if (!name) {
226
+ throw new Error(`bahulam registry source requires a plugin name, e.g. ${CYAN}bahulam:manim-studio${RESET}`);
227
+ }
228
+ return name;
229
+ }
230
+ if (classified?.kind === 'name') return classified.name;
231
+ return null;
232
+ }
233
+
234
+ async function installFromBahulamRegistry({ name, targetDir, cwd, args }) {
235
+ const entry = await resolveBahulamRegistryPlugin(name, { cwd });
236
+ const repository = entry.repository || entry.repo || entry.url;
237
+ if (!repository) {
238
+ throw new Error(`registry entry "${entry.name}" is missing a repository URL`);
239
+ }
240
+ const ref = args.ref || entry.ref || null;
241
+ const subdir = entry.subdir || entry.path || null;
242
+
243
+ process.stderr.write(
244
+ `${DIM}registry${RESET} ${entry.name} → ${repository}` +
245
+ `${subdir ? `#${subdir}` : ''}${ref ? ` @ ${ref}` : ''}\n`,
246
+ );
247
+
248
+ const dest = await installFromGit({
249
+ url: repository,
250
+ targetDir,
251
+ name: entry.name,
252
+ ref,
253
+ subdir,
254
+ force: args.force,
255
+ });
256
+ await preflightAndReport({ dest, args, cwd });
257
+ }
258
+
259
+ export async function resolveBahulamRegistryPlugin(name, { cwd = process.cwd(), registry = null } = {}) {
260
+ const doc = registry || await loadBahulamPluginRegistry({ cwd });
261
+ const entries = Array.isArray(doc) ? doc : Array.isArray(doc?.plugins) ? doc.plugins : [];
262
+ if (!entries.length) {
263
+ throw new Error('Bahulam plugin registry did not contain any plugins');
264
+ }
265
+
266
+ const needle = normalizeRegistryName(name);
267
+ const entry = entries.find(item => {
268
+ const names = [
269
+ item?.name,
270
+ item?.slug,
271
+ item?.id,
272
+ ...(Array.isArray(item?.aliases) ? item.aliases : []),
273
+ ];
274
+ return names.some(candidate => normalizeRegistryName(candidate) === needle);
275
+ });
276
+
277
+ if (!entry) {
278
+ throw new Error(
279
+ `plugin not found in Bahulam registry: ${name}\n` +
280
+ `Available: ${entries.map(item => item.name).filter(Boolean).join(', ') || '(none)'}`,
281
+ );
282
+ }
283
+ return entry;
284
+ }
285
+
286
+ async function loadBahulamPluginRegistry({ cwd = process.cwd() } = {}) {
287
+ const explicit =
288
+ process.env.BAHULAM_PLUGIN_REGISTRY ||
289
+ process.env.BAHULAM_PLUGIN_REGISTRY_PATH ||
290
+ process.env.BAHULAM_PLUGIN_REGISTRY_URL;
291
+ if (explicit) return readRegistryLocation(explicit, cwd);
292
+
293
+ try {
294
+ return await readRegistryLocation(DEFAULT_BAHULAM_PLUGIN_REGISTRY_URL, cwd);
295
+ } catch (err) {
296
+ const localPath = findLocalRegistry(cwd);
297
+ if (localPath) return readRegistryJson(localPath);
298
+ throw new Error(`failed to load Bahulam plugin registry: ${err.message}`);
299
+ }
300
+ }
301
+
302
+ async function readRegistryLocation(location, cwd) {
303
+ if (/^https?:\/\//i.test(location)) {
304
+ const res = await fetch(location, { headers: { accept: 'application/json' } });
305
+ if (!res.ok) throw new Error(`${location} returned HTTP ${res.status}`);
306
+ return res.json();
307
+ }
308
+ const filePath = path.isAbsolute(location) ? location : path.resolve(cwd, location);
309
+ return readRegistryJson(filePath);
310
+ }
311
+
312
+ function readRegistryJson(filePath) {
313
+ try {
314
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
315
+ } catch (err) {
316
+ throw new Error(`failed to read registry ${filePath}: ${err.message}`);
317
+ }
318
+ }
319
+
320
+ function findLocalRegistry(cwd) {
321
+ const candidates = [
322
+ path.resolve(cwd, '../awesome-bahulam-plugins/registry.json'),
323
+ path.resolve(cwd, 'awesome-bahulam-plugins/registry.json'),
324
+ ];
325
+ return candidates.find(candidate => fs.existsSync(candidate)) || null;
326
+ }
327
+
328
+ function normalizeRegistryName(name) {
329
+ return String(name || '').trim().replace(/^bahulam:/i, '').toLowerCase();
330
+ }
331
+
210
332
  async function installPiWithScaffolding({ classified, targetDir, cwd, args }) {
211
333
  const { bahulamHome } = await import('../core/paths.mjs');
212
334
  const { discoverPiTools } = await import('../plugins/pi-compat/probe.mjs');
@@ -297,7 +297,7 @@ async function main() {
297
297
  const verb = subcommandArgs[0];
298
298
  const rest = subcommandArgs.slice(1);
299
299
  process.stderr.write(`\x1b[33m!\x1b[0m \`bahulam plugin ${verb}\` moved to top-level. Use:\n`);
300
- process.stderr.write(` \x1b[36mbahulam install ${rest.join(' ')}\x1b[0m (pack — scaffolds around pi:, installs git/tarball/local)\n`);
300
+ process.stderr.write(` \x1b[36mbahulam install ${rest.join(' ')}\x1b[0m (pack — registry, scaffolds around pi:, git/tarball/local)\n`);
301
301
  process.stderr.write(` \x1b[36mbahulam pull ${rest.join(' ')}\x1b[0m (ingredient only — pi: sources)\n`);
302
302
  process.exit(2);
303
303
  }
@@ -356,6 +356,8 @@ async function main() {
356
356
  \x1b[1mPacks & ingredients:\x1b[0m
357
357
  bahulam pull pi:<name> Pull a pi ingredient (composable, not runnable on its own)
358
358
  bahulam install pi:<name> Pull ingredient + scaffold a full Bahulam pack around it
359
+ bahulam install <name> Install from awesome-bahulam-plugins
360
+ bahulam install bahulam:<name> Explicit awesome-bahulam-plugins lookup
359
361
  bahulam install <git-url> Install a hand-authored pack from git
360
362
  bahulam install <local-path> Install a hand-authored pack from disk
361
363
  bahulam plugin list List installed packs and pi ingredients