@atlashub/smartstack-cli 1.14.1 → 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/.documentation/gitflow.html +330 -162
- package/dist/index.js +183 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/skills/_shared.md +29 -6
- package/templates/skills/apex/SKILL.md +56 -0
- package/templates/skills/apex/steps/step-01-analyze.md +22 -5
- package/templates/skills/apex/steps/step-04-validate.md +32 -6
- package/templates/skills/ui-components/SKILL.md +167 -5
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
|
-
|
|
39154
|
-
|
|
39155
|
-
|
|
39156
|
-
|
|
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
|
-
|
|
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({
|
|
42031
|
-
|
|
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
|
|
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
|
}
|