@atlashub/smartstack-cli 1.14.2 → 1.14.3

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/dist/index.js CHANGED
@@ -38996,6 +38996,31 @@ var import_fs_extra = __toESM(require_lib());
38996
38996
  var PACKAGE_ROOT = (0, import_path.join)(__dirname, "..");
38997
38997
  var TEMPLATES_DIR = (0, import_path.join)(PACKAGE_ROOT, "templates");
38998
38998
  var INSTALL_DIRS = ["commands", "agents", "hooks", "skills", "scripts"];
38999
+ var MANIFEST_FILE = ".smartstack-manifest.json";
39000
+ async function readManifest(claudeDir) {
39001
+ const manifestPath = (0, import_path.join)(claudeDir, MANIFEST_FILE);
39002
+ try {
39003
+ if (await import_fs_extra.default.pathExists(manifestPath)) {
39004
+ const content = await import_fs_extra.default.readFile(manifestPath, "utf-8");
39005
+ return JSON.parse(content);
39006
+ }
39007
+ } catch {
39008
+ }
39009
+ return null;
39010
+ }
39011
+ async function writeManifest(claudeDir, manifest) {
39012
+ const manifestPath = (0, import_path.join)(claudeDir, MANIFEST_FILE);
39013
+ await import_fs_extra.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
39014
+ }
39015
+ function getPackageVersion() {
39016
+ try {
39017
+ const packagePath = (0, import_path.join)(PACKAGE_ROOT, "package.json");
39018
+ const pkg2 = JSON.parse(import_fs_extra.default.readFileSync(packagePath, "utf-8"));
39019
+ return pkg2.version || "0.0.0";
39020
+ } catch {
39021
+ return "0.0.0";
39022
+ }
39023
+ }
38999
39024
  var DEFAULT_PLUGINS = [];
39000
39025
  function getClaudeDir(global3) {
39001
39026
  if (global3) {
@@ -39026,7 +39051,8 @@ async function installCommands(options = {}) {
39026
39051
  const result = {
39027
39052
  installed: 0,
39028
39053
  skipped: 0,
39029
- errors: []
39054
+ errors: [],
39055
+ installedFiles: []
39030
39056
  };
39031
39057
  logger.info(`Installing to: ${claudeDir}`);
39032
39058
  const dirsToInstall = installAll ? INSTALL_DIRS : INSTALL_DIRS.filter((d) => components.includes(d));
@@ -39041,15 +39067,18 @@ async function installCommands(options = {}) {
39041
39067
  for (const file of files) {
39042
39068
  const src = (0, import_path.join)(sourceDir, file);
39043
39069
  const dest = (0, import_path.join)(targetDir, file);
39070
+ const relativePath = (0, import_path.join)(dir, file);
39044
39071
  try {
39045
39072
  const exists = await import_fs_extra.default.pathExists(dest);
39046
39073
  if (exists && !options.force) {
39047
39074
  result.skipped++;
39075
+ result.installedFiles.push(relativePath);
39048
39076
  continue;
39049
39077
  }
39050
39078
  await import_fs_extra.default.ensureDir((0, import_path.dirname)(dest));
39051
39079
  await import_fs_extra.default.copy(src, dest, { overwrite: options.force });
39052
39080
  result.installed++;
39081
+ result.installedFiles.push(relativePath);
39053
39082
  if (file.endsWith(".md") && !file.includes("/")) {
39054
39083
  logger.success(`Installed ${dir}/${file}`);
39055
39084
  }
@@ -39080,6 +39109,17 @@ async function installCommands(options = {}) {
39080
39109
  }
39081
39110
  }
39082
39111
  }
39112
+ const now = (/* @__PURE__ */ new Date()).toISOString();
39113
+ const manifest = {
39114
+ version: getPackageVersion(),
39115
+ installedAt: now,
39116
+ updatedAt: now,
39117
+ files: result.installedFiles.map((path3) => ({
39118
+ path: path3,
39119
+ installedAt: now
39120
+ }))
39121
+ };
39122
+ await writeManifest(claudeDir, manifest);
39083
39123
  return result;
39084
39124
  }
39085
39125
  async function uninstallCommands(options = {}) {
@@ -39150,11 +39190,108 @@ async function uninstallCommands(options = {}) {
39150
39190
  return result;
39151
39191
  }
39152
39192
  async function updateCommands(options = {}) {
39153
- await installCommands({
39154
- force: true,
39155
- skipConfig: true,
39156
- global: options.global ?? true
39157
- });
39193
+ const global3 = options.global ?? true;
39194
+ const claudeDir = getClaudeDir(global3);
39195
+ const dryRun = options.dryRun ?? false;
39196
+ const result = {
39197
+ installed: 0,
39198
+ updated: 0,
39199
+ removed: 0,
39200
+ removedFiles: [],
39201
+ errors: []
39202
+ };
39203
+ const oldManifest = await readManifest(claudeDir);
39204
+ const oldFiles = new Set(oldManifest?.files.map((f) => f.path) || []);
39205
+ if (dryRun) {
39206
+ logger.info("DRY RUN - No files will be modified");
39207
+ }
39208
+ if (!dryRun) {
39209
+ const installResult = await installCommands({
39210
+ force: true,
39211
+ skipConfig: true,
39212
+ global: global3
39213
+ });
39214
+ result.installed = installResult.installed;
39215
+ result.updated = installResult.installed;
39216
+ const newManifest = await readManifest(claudeDir);
39217
+ const newFiles = new Set(newManifest?.files.map((f) => f.path) || []);
39218
+ const obsoleteFiles = [];
39219
+ for (const oldFile of oldFiles) {
39220
+ if (!newFiles.has(oldFile)) {
39221
+ obsoleteFiles.push(oldFile);
39222
+ }
39223
+ }
39224
+ for (const file of obsoleteFiles) {
39225
+ const filePath = (0, import_path.join)(claudeDir, file);
39226
+ try {
39227
+ if (await import_fs_extra.default.pathExists(filePath)) {
39228
+ await import_fs_extra.default.remove(filePath);
39229
+ result.removed++;
39230
+ result.removedFiles.push(file);
39231
+ logger.warning(`Removed obsolete: ${file}`);
39232
+ }
39233
+ } catch (error) {
39234
+ const msg = `Failed to remove ${file}: ${error}`;
39235
+ result.errors.push(msg);
39236
+ logger.error(msg);
39237
+ }
39238
+ }
39239
+ await cleanEmptyDirectories(claudeDir);
39240
+ } else {
39241
+ const newFiles = /* @__PURE__ */ new Set();
39242
+ for (const dir of INSTALL_DIRS) {
39243
+ const sourceDir = (0, import_path.join)(TEMPLATES_DIR, dir);
39244
+ if (await import_fs_extra.default.pathExists(sourceDir)) {
39245
+ const files = await getTemplateFiles(sourceDir);
39246
+ for (const file of files) {
39247
+ newFiles.add((0, import_path.join)(dir, file));
39248
+ }
39249
+ }
39250
+ }
39251
+ const wouldRemove = [];
39252
+ for (const oldFile of oldFiles) {
39253
+ if (!newFiles.has(oldFile)) {
39254
+ wouldRemove.push(oldFile);
39255
+ }
39256
+ }
39257
+ if (wouldRemove.length > 0) {
39258
+ logger.info(`
39259
+ Files that would be REMOVED (${wouldRemove.length}):`);
39260
+ for (const file of wouldRemove) {
39261
+ logger.warning(` - ${file}`);
39262
+ result.removedFiles.push(file);
39263
+ }
39264
+ } else {
39265
+ logger.info("No obsolete files to remove");
39266
+ }
39267
+ result.removed = wouldRemove.length;
39268
+ }
39269
+ return result;
39270
+ }
39271
+ async function cleanEmptyDirectories(dir) {
39272
+ for (const subDir of INSTALL_DIRS) {
39273
+ const targetDir = (0, import_path.join)(dir, subDir);
39274
+ if (await import_fs_extra.default.pathExists(targetDir)) {
39275
+ await removeEmptyDirs(targetDir);
39276
+ }
39277
+ }
39278
+ }
39279
+ async function removeEmptyDirs(dir) {
39280
+ try {
39281
+ const entries = await import_fs_extra.default.readdir(dir, { withFileTypes: true });
39282
+ for (const entry of entries) {
39283
+ if (entry.isDirectory()) {
39284
+ await removeEmptyDirs((0, import_path.join)(dir, entry.name));
39285
+ }
39286
+ }
39287
+ const remaining = await import_fs_extra.default.readdir(dir);
39288
+ if (remaining.length === 0) {
39289
+ await import_fs_extra.default.rmdir(dir);
39290
+ return true;
39291
+ }
39292
+ } catch {
39293
+ }
39294
+ return false;
39158
39295
  }
39159
39296
  async function checkInstallation(options = {}) {
39160
39297
  const global3 = options.global ?? true;
@@ -42016,8 +42153,13 @@ var statusCommand = new Command("status").alias("s").description("Show installat
42016
42153
  });
42017
42154
 
42018
42155
  // src/commands/update.ts
42019
- var updateCommand = new Command("update").description("Update commands to the latest version").option("-g, --global", "Update user directory ~/.claude (default)", true).option("-l, --local", "Update project directory ./.claude").action(async (options) => {
42020
- logger.header("Updating SmartStack Commands");
42156
+ var updateCommand = new Command("update").description("Update commands to the latest version").option("-g, --global", "Update user directory ~/.claude (default)", true).option("-l, --local", "Update project directory ./.claude").option("-n, --dry-run", "Preview changes without modifying files").action(async (options) => {
42157
+ const dryRun = options.dryRun ?? false;
42158
+ if (dryRun) {
42159
+ logger.header("SmartStack Update - DRY RUN");
42160
+ } else {
42161
+ logger.header("Updating SmartStack Commands");
42162
+ }
42021
42163
  const isGlobal = !options.local;
42022
42164
  const installation = await checkInstallation({ global: isGlobal });
42023
42165
  if (!installation.installed) {
@@ -42025,12 +42167,41 @@ var updateCommand = new Command("update").description("Update commands to the la
42025
42167
  logger.info("Run: smartstack install");
42026
42168
  return;
42027
42169
  }
42028
- const spinner = logger.spinner("Updating commands...");
42170
+ const spinner = dryRun ? null : logger.spinner("Updating commands...");
42029
42171
  try {
42030
- await updateCommands({ global: isGlobal });
42031
- spinner.succeed("Commands updated successfully!");
42172
+ const result = await updateCommands({
42173
+ global: isGlobal,
42174
+ dryRun
42175
+ });
42176
+ if (spinner) {
42177
+ spinner.succeed("Commands updated successfully!");
42178
+ }
42179
+ logger.info("");
42180
+ logger.info("Update Summary:");
42181
+ logger.info(` Files updated: ${result.updated}`);
42182
+ logger.info(` Files removed: ${result.removed}`);
42183
+ if (result.removedFiles.length > 0 && !dryRun) {
42184
+ logger.info("");
42185
+ logger.info("Removed obsolete files:");
42186
+ for (const file of result.removedFiles) {
42187
+ logger.warning(` - ${file}`);
42188
+ }
42189
+ }
42190
+ if (result.errors.length > 0) {
42191
+ logger.info("");
42192
+ logger.warning(`Errors: ${result.errors.length}`);
42193
+ for (const err of result.errors) {
42194
+ logger.error(` ${err}`);
42195
+ }
42196
+ }
42197
+ if (dryRun && result.removed > 0) {
42198
+ logger.info("");
42199
+ logger.info("Run without --dry-run to apply changes.");
42200
+ }
42032
42201
  } catch (error) {
42033
- spinner.fail("Update failed");
42202
+ if (spinner) {
42203
+ spinner.fail("Update failed");
42204
+ }
42034
42205
  logger.error(error instanceof Error ? error.message : "Unknown error");
42035
42206
  process.exit(1);
42036
42207
  }