diff --git a/src/main.cpp b/src/main.cpp
index 43e396d..e41e856 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -53,6 +53,8 @@ Subcommands:
list List installed packages
rollback Show available generations
index
Build an index.kap from .kap files in a directory
+ add [v] Add a package to system config (optional version)
+ remove Remove a package from system config
Options:
-h, --help Show this help message
@@ -218,7 +220,9 @@ int main(int argc, char* argv[]) {
|| (subcommand == "list")
|| (subcommand == "fetch-package")
|| (subcommand == "rollback")
- || (subcommand == "index");
+ || (subcommand == "index")
+ || (subcommand == "add")
+ || (subcommand == "remove");
if (!valid_subcommand) {
std::cerr << "error: unknown subcommand '" << subcommand << "'\n\n";
@@ -227,6 +231,7 @@ int main(int argc, char* argv[]) {
}
const char* file_arg = nullptr;
+ const char* version_arg = nullptr;
bool dry_run = false;
for (int i = arg_start + 1; i < argc; ++i) {
if (std::string_view(argv[i]) == "-h"
@@ -254,8 +259,12 @@ int main(int argc, char* argv[]) {
continue;
}
if (!is_flag(argv[i])) {
- file_arg = argv[i];
- break;
+ if (file_arg == nullptr) {
+ file_arg = argv[i];
+ } else if (version_arg == nullptr) {
+ version_arg = argv[i];
+ break;
+ }
}
}
@@ -320,6 +329,102 @@ int main(int argc, char* argv[]) {
}
}
+ if (subcommand == "add") {
+ if (file_arg == nullptr) {
+ std::cerr << "error: no package name specified\n";
+ return 1;
+ }
+ try {
+ auto config_path = std::filesystem::path(paths::system_dir()) / "config.kap";
+ dsl::SystemConfig cfg;
+
+ if (std::filesystem::exists(config_path)) {
+ auto src = read_file(config_path.c_str());
+ cfg = dsl::parse_system_config(src);
+ }
+
+ bool found = false;
+ for (auto& p : cfg.packages) {
+ if (p.name == file_arg) {
+ found = true;
+ if (version_arg != nullptr) {
+ p.version = version_arg;
+ std::cout << "updated " << file_arg
+ << " → version " << version_arg << "\n";
+ } else {
+ std::cout << file_arg << " already in packages\n";
+ }
+ break;
+ }
+ }
+
+ if (!found) {
+ dsl::PackageRef pref;
+ pref.name = file_arg;
+ if (version_arg != nullptr) {
+ pref.version = version_arg;
+ }
+ cfg.packages.push_back(std::move(pref));
+ std::cout << "added " << file_arg;
+ if (version_arg != nullptr) {
+ std::cout << " " << version_arg;
+ }
+ std::cout << "\n";
+ }
+
+ std::error_code ec;
+ std::filesystem::create_directories(paths::system_dir(), ec);
+ std::ofstream out(config_path);
+ if (!out) {
+ std::cerr << "error: cannot write " << config_path.string() << "\n";
+ return 1;
+ }
+ tools::format_config(out, cfg);
+ return 0;
+ } catch (const std::exception& e) {
+ std::cerr << "add error: " << e.what() << "\n";
+ return 1;
+ }
+ }
+
+ if (subcommand == "remove") {
+ if (file_arg == nullptr) {
+ std::cerr << "error: no package name specified\n";
+ return 1;
+ }
+ try {
+ auto config_path = std::filesystem::path(paths::system_dir()) / "config.kap";
+ if (!std::filesystem::exists(config_path)) {
+ std::cout << file_arg << " not found (no config)\n";
+ return 0;
+ }
+
+ auto src = read_file(config_path.c_str());
+ auto cfg = dsl::parse_system_config(src);
+
+ auto it = std::remove_if(cfg.packages.begin(), cfg.packages.end(),
+ [&](const dsl::PackageRef& p) { return p.name == file_arg; });
+
+ if (it == cfg.packages.end()) {
+ std::cout << file_arg << " not in packages\n";
+ return 0;
+ }
+ cfg.packages.erase(it, cfg.packages.end());
+ std::cout << "removed " << file_arg << "\n";
+
+ std::ofstream out(config_path);
+ if (!out) {
+ std::cerr << "error: cannot write " << config_path.string() << "\n";
+ return 1;
+ }
+ tools::format_config(out, cfg);
+ return 0;
+ } catch (const std::exception& e) {
+ std::cerr << "remove error: " << e.what() << "\n";
+ return 1;
+ }
+ }
+
if (subcommand == "fetch-package") {
if (file_arg == nullptr) {
std::cerr << "error: no package name specified\n";