catylst 1.0.4 → 1.0.6

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/catylst.js CHANGED
@@ -30,6 +30,7 @@ const args = process.argv.slice(2);
30
30
  const child = spawn("java", ["-jar", JAR_PATH, ...args], {
31
31
  cwd: WORK_DIR,
32
32
  stdio: "inherit",
33
+ env: { ...process.env, CATYLST_USER_PWD: process.cwd() },
33
34
  });
34
35
 
35
36
  child.on("exit", (code) => process.exit(code ?? 0));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catylst",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Generate customized Kotlin Multiplatform projects with an interactive wizard",
5
5
  "keywords": [
6
6
  "kotlin",
package/postinstall.js CHANGED
@@ -28,11 +28,62 @@ const CATYLST_DIR = path.join(os.homedir(), ".catylst");
28
28
  const TEMPLATE_DIR = path.join(CATYLST_DIR, "template");
29
29
  const JAR_PATH = path.join(CATYLST_DIR, "catylst-cli.jar");
30
30
 
31
- const green = (s) => `\x1b[32m${s}\x1b[0m`;
31
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
32
32
  const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
33
- const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
34
- const bold = (s) => `\x1b[1m${s}\x1b[0m`;
35
- const dim = (s) => `\x1b[2m${s}\x1b[0m`;
33
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
34
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
35
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
36
+ const purple = (s) => `\x1b[35m${s}\x1b[0m`;
37
+
38
+ // ── Tips & jokes shown while cloning / downloading ───────────────────────────
39
+
40
+ const MESSAGES = [
41
+ "tip Room 3.1 auto-generates all your DAO queries at compile time.",
42
+ "tip Navigation3 uses type-safe routes — no more string typos in nav graphs.",
43
+ "tip Swap AI providers by changing one line in AppModule.kt.",
44
+ "tip bloom-build helps you scaffold a full feature in seconds with Claude Code.",
45
+ "tip Material 3 Expressive ships spring-based motion out of the box.",
46
+ "tip Run ./gradlew :composeApp:kspAndroidMain after every Entity change.",
47
+ "tip Koin multiplatform means one DI graph for Android, iOS, and Desktop.",
48
+ "tip Use bloom-navigate to cleanly remove any feature you do not need.",
49
+ "tip AGP 9 brings predictive back gesture support by default.",
50
+ "tip commonMain code compiles to all targets — write once, ship everywhere.",
51
+ "joke Why do Kotlin developers stay calm? Because they know how to handle exceptions.",
52
+ "joke A null pointer walks into a bar. The bartender says: we don't serve your type here.",
53
+ "joke Why did the Android developer quit? Too many fragments.",
54
+ "joke Kotlin: where semicolons go to retire.",
55
+ "joke iOS dev asks: what is Gradle? Android dev weeps softly.",
56
+ "joke There are only 10 types of developers: those who understand binary and those who do not.",
57
+ "joke A git push a day keeps the merge conflicts away. Usually.",
58
+ "joke My code works. I have no idea why. Shipping it anyway.",
59
+ ];
60
+
61
+ let tipTimer = null;
62
+ let tipIndex = 0;
63
+
64
+ function startTips() {
65
+ // Shuffle so order is different each install
66
+ const msgs = [...MESSAGES].sort(() => Math.random() - 0.5);
67
+ tipIndex = 0;
68
+
69
+ function showNext() {
70
+ const msg = msgs[tipIndex % msgs.length];
71
+ const kind = msg.startsWith("joke") ? purple("joke ") : cyan("tip ");
72
+ const text = msg.replace(/^(tip|joke)\s+/, "");
73
+ process.stdout.write(`\r ${kind} ${dim(text)}${" ".repeat(10)}`);
74
+ tipIndex++;
75
+ tipTimer = setTimeout(showNext, 3000);
76
+ }
77
+
78
+ showNext();
79
+ }
80
+
81
+ function stopTips(finalLine) {
82
+ if (tipTimer) { clearTimeout(tipTimer); tipTimer = null; }
83
+ process.stdout.write(`\r${finalLine}${" ".repeat(30)}\n`);
84
+ }
85
+
86
+ // ── Header ───────────────────────────────────────────────────────────────────
36
87
 
37
88
  console.log("");
38
89
  console.log(bold(" Catylst KMP Project Generator"));
@@ -67,33 +118,33 @@ function setupTemplate() {
67
118
  const isGitRepo = fs.existsSync(path.join(TEMPLATE_DIR, ".git"));
68
119
 
69
120
  if (isGitRepo) {
70
- process.stdout.write(cyan(" ↻ Updating template..."));
71
- // Use spawn array form — no shell interpolation, no injection risk
121
+ startTips();
72
122
  const result = spawnSync("git", ["pull", "--quiet", "--rebase"], {
73
123
  cwd: TEMPLATE_DIR,
74
124
  stdio: "pipe",
75
125
  });
76
126
  if (result.status === 0) {
77
- console.log("\r" + green(" ✓ Template updated "));
127
+ stopTips(green(" ✓ Template updated"));
78
128
  } else {
79
- console.log("\r" + yellow(" ⚠ Could not update template (offline?). Using existing."));
129
+ stopTips(yellow(" ⚠ Could not update template (offline?). Using existing."));
80
130
  }
81
131
  } else {
82
- console.log(cyan(" ↓ Cloning template to ~/.catylst/template ..."));
132
+ console.log(dim(" ↓ Cloning template hang tight...\n"));
133
+ startTips();
83
134
  fs.rmSync(TEMPLATE_DIR, { recursive: true, force: true });
84
- // Use spawn array form — REPO_URL and TEMPLATE_DIR are never shell-interpolated
85
135
  const result = spawnSync(
86
136
  "git",
87
137
  ["clone", "--depth", "1", "--quiet", REPO_URL, TEMPLATE_DIR],
88
138
  { stdio: "pipe" }
89
139
  );
90
140
  if (result.status !== 0) {
141
+ stopTips("");
91
142
  const msg = result.stderr ? result.stderr.toString().trim() : "unknown error";
92
143
  console.error(yellow(" ✗ Failed to clone template. Is git installed?"));
93
144
  console.error(dim(` ${msg}`));
94
145
  process.exit(1);
95
146
  }
96
- console.log(green(" ✓ Template ready"));
147
+ stopTips(green(" ✓ Template ready"));
97
148
  }
98
149
  }
99
150
 
@@ -124,7 +175,8 @@ function downloadJar() {
124
175
  }
125
176
 
126
177
  return new Promise((resolve, reject) => {
127
- process.stdout.write(cyan(" ↓ Downloading catylst-cli.jar ..."));
178
+ console.log(dim(" ↓ Downloading CLI — almost there...\n"));
179
+ startTips();
128
180
 
129
181
  // Write to a temp file first — atomic rename prevents race conditions
130
182
  const tmpPath = JAR_PATH + ".tmp." + process.pid;
@@ -167,18 +219,20 @@ function downloadJar() {
167
219
  }
168
220
 
169
221
  const digest = hash.digest("hex");
170
- console.log("\r" + green(" ✓ CLI ready "));
222
+ stopTips(green(" ✓ CLI ready"));
171
223
  console.log(dim(` SHA-256: ${digest}`));
172
224
  resolve();
173
225
  });
174
226
  });
175
227
 
176
228
  file.on("error", (err) => {
229
+ stopTips("");
177
230
  fs.unlink(tmpPath, () => {});
178
231
  reject(err);
179
232
  });
180
233
  })
181
234
  .on("error", (err) => {
235
+ stopTips("");
182
236
  fs.unlink(tmpPath, () => {});
183
237
  reject(err);
184
238
  });