@@ -284,12 +284,204 @@ namespace vix::commands
284284 label,
285285 path_with_state (path));
286286 }
287+
288+ struct ParsedArgs
289+ {
290+ bool globalMode{false };
291+ std::string packageId;
292+ };
293+
294+ static fs::path project_lock_path ()
295+ {
296+ return fs::current_path () / " vix.lock" ;
297+ }
298+
299+ static ParsedArgs parse_args (const std::vector<std::string> &args)
300+ {
301+ ParsedArgs parsed;
302+
303+ for (const auto &arg : args)
304+ {
305+ if (arg == " -g" || arg == " --global" )
306+ {
307+ parsed.globalMode = true ;
308+ continue ;
309+ }
310+
311+ if (parsed.packageId .empty ())
312+ parsed.packageId = arg;
313+ }
314+
315+ return parsed;
316+ }
317+
318+ static json read_json_or_throw (const fs::path &p)
319+ {
320+ std::ifstream in (p);
321+ if (!in)
322+ throw std::runtime_error (" cannot open: " + p.string ());
323+
324+ json j;
325+ in >> j;
326+ return j;
327+ }
328+
329+ static const json *get_packages_array (const json &root)
330+ {
331+ if (root.is_array ())
332+ return &root;
333+
334+ if (root.is_object () && root.contains (" packages" ) && root[" packages" ].is_array ())
335+ return &root[" packages" ];
336+
337+ if (root.is_object () && root.contains (" dependencies" ) && root[" dependencies" ].is_array ())
338+ return &root[" dependencies" ];
339+
340+ return nullptr ;
341+ }
342+
343+ static std::string package_version (const json &pkg)
344+ {
345+ if (pkg.contains (" version" ) && pkg[" version" ].is_string ())
346+ return pkg[" version" ].get <std::string>();
347+
348+ if (pkg.contains (" tag" ) && pkg[" tag" ].is_string ())
349+ {
350+ std::string tag = pkg[" tag" ].get <std::string>();
351+ if (!tag.empty () && tag.front () == ' v' )
352+ tag.erase (tag.begin ());
353+ return tag;
354+ }
355+
356+ return {};
357+ }
358+
359+ static std::string package_repo (const json &pkg)
360+ {
361+ if (!pkg.contains (" repo" ))
362+ return {};
363+
364+ if (pkg[" repo" ].is_string ())
365+ return pkg[" repo" ].get <std::string>();
366+
367+ if (pkg[" repo" ].is_object ())
368+ return pkg[" repo" ].value (" url" , " " );
369+
370+ return {};
371+ }
372+
373+ static const json *find_package_by_id (const json &packages, const std::string &id)
374+ {
375+ for (const auto &pkg : packages)
376+ {
377+ if (pkg.value (" id" , " " ) == id)
378+ return &pkg;
379+ }
380+
381+ return nullptr ;
382+ }
383+
384+ static void print_package_info (const json &pkg, bool globalMode)
385+ {
386+ const std::string id = pkg.value (" id" , " " );
387+ const std::string version = package_version (pkg);
388+ const std::string commit = pkg.value (" commit" , " " );
389+ const std::string repo = package_repo (pkg);
390+ const std::string type = pkg.value (" type" , " " );
391+ const std::string include = pkg.value (" include" , " " );
392+ const std::string installedPath = pkg.value (" installed_path" , " " );
393+
394+ vix::cli::util::section (std::cout, globalMode ? " Global package" : " Project package" );
395+
396+ if (!id.empty ())
397+ vix::cli::util::kv (std::cout, " id" , id);
398+
399+ if (!version.empty ())
400+ vix::cli::util::kv (std::cout, " version" , version);
401+
402+ if (!commit.empty ())
403+ vix::cli::util::kv (std::cout, " commit" , commit);
404+
405+ if (!repo.empty ())
406+ vix::cli::util::kv (std::cout, " repo" , repo);
407+
408+ if (!type.empty ())
409+ vix::cli::util::kv (std::cout, " type" , type);
410+
411+ if (!include.empty ())
412+ vix::cli::util::kv (std::cout, " include" , include);
413+
414+ if (!installedPath.empty ())
415+ vix::cli::util::kv (std::cout, " path" , installedPath);
416+
417+ vix::cli::util::one_line_spacer (std::cout);
418+ vix::cli::util::ok_line (std::cout, " Package info loaded" );
419+ }
420+
421+ static int print_package_info_from_file (
422+ const fs::path &path,
423+ const std::string &packageId,
424+ bool globalMode)
425+ {
426+ if (!is_regular_file_path (path))
427+ {
428+ vix::cli::util::err_line (
429+ std::cerr,
430+ globalMode ? " global package manifest not found" : " project lock file not found" );
431+ return 1 ;
432+ }
433+
434+ json root;
435+ try
436+ {
437+ root = read_json_or_throw (path);
438+ }
439+ catch (const std::exception &ex)
440+ {
441+ vix::cli::util::err_line (std::cerr, std::string (" failed to read package data: " ) + ex.what ());
442+ return 1 ;
443+ }
444+
445+ const json *packages = get_packages_array (root);
446+ if (!packages)
447+ {
448+ vix::cli::util::err_line (
449+ std::cerr,
450+ globalMode ? " invalid global manifest" : " invalid project lock file" );
451+ return 1 ;
452+ }
453+
454+ const json *pkg = find_package_by_id (*packages, packageId);
455+ if (!pkg)
456+ {
457+ vix::cli::util::err_line (std::cerr, " package not found: " + packageId);
458+ return 1 ;
459+ }
460+
461+ print_package_info (*pkg, globalMode);
462+ return 0 ;
463+ }
287464 }
288465
289466 int InfoCommand::run (const std::vector<std::string> &args)
290467 {
291- if (!args.empty ())
292- return help ();
468+ const ParsedArgs parsed = parse_args (args);
469+
470+ if (!parsed.packageId .empty ())
471+ {
472+ if (parsed.globalMode )
473+ {
474+ return print_package_info_from_file (
475+ global_manifest_path (),
476+ parsed.packageId ,
477+ true );
478+ }
479+
480+ return print_package_info_from_file (
481+ project_lock_path (),
482+ parsed.packageId ,
483+ false );
484+ }
293485
294486 const fs::path root = vix_root ();
295487 const fs::path registry = registry_index_dir ();
@@ -364,12 +556,26 @@ namespace vix::commands
364556 {
365557 std::cout
366558 << " vix info\n "
367- << " Show Vix environment, paths, caches, and local state.\n\n "
559+ << " Show Vix environment, package details, paths, caches, and local state.\n\n "
368560
369561 << " Usage\n "
370- << " vix info\n\n "
371-
372- << " What it shows\n "
562+ << " vix info\n "
563+ << " vix info [@]namespace/name\n "
564+ << " vix info -g [@]namespace/name\n "
565+ << " vix info --global [@]namespace/name\n\n "
566+
567+ << " Examples\n "
568+ << " vix info\n "
569+ << " vix info softadastra/json\n "
570+ << " vix info -g softadastra/json\n "
571+ << " vix info -g @softadastra/json\n\n "
572+
573+ << " What happens\n "
574+ << " • vix info shows the local Vix environment\n "
575+ << " • vix info <package> shows details for a project dependency from vix.lock\n "
576+ << " • vix info -g <package> shows details for a globally installed package\n\n "
577+
578+ << " Environment info\n "
373579 << " • Vix version\n "
374580 << " • Vix root path\n "
375581 << " • Registry index path and state\n "
@@ -380,7 +586,20 @@ namespace vix::commands
380586 << " • Number of package directories in store/git\n "
381587 << " • Number of cached commits in store/git\n "
382588 << " • Disk usage of store/git\n "
383- << " • Disk usage of build artifact cache\n " ;
589+ << " • Disk usage of build artifact cache\n\n "
590+
591+ << " Package info\n "
592+ << " • Package id\n "
593+ << " • Version\n "
594+ << " • Commit\n "
595+ << " • Repository\n "
596+ << " • Type\n "
597+ << " • Include directory\n "
598+ << " • Installed path, when available\n\n "
599+
600+ << " Notes\n "
601+ << " • Project package info is read from ./vix.lock\n "
602+ << " • Global package info is read from ~/.vix/global/installed.json\n " ;
384603
385604 return 0 ;
386605 }
0 commit comments