ramadan-cal 1.0.1 → 1.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ramadan-cal",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "🌙 A beautiful terminal Ramadan calendar with prayer times, daily progress, and Hijri date",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,11 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
4
- * Postinstall script — runs automatically after npm install.
5
- * Interactively sets up Claude Code integration if the user wants it.
6
- */
7
-
8
- import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from 'fs';
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, createReadStream, createWriteStream, openSync, closeSync } from 'fs';
9
4
  import { join } from 'path';
10
5
  import { homedir } from 'os';
11
6
  import { execSync } from 'child_process';
@@ -14,6 +9,9 @@ import { createInterface } from 'readline';
14
9
  const home = homedir();
15
10
  const claudeDir = join(home, '.claude');
16
11
  const claudeMd = join(claudeDir, 'CLAUDE.md');
12
+ const skillsDir = join(claudeDir, 'skills');
13
+ const ramadanSkillDir = join(skillsDir, 'ramadan');
14
+ const ramadanSkillFile = join(ramadanSkillDir, 'SKILL.md');
17
15
 
18
16
  const MARKER = '<!-- ramadan-cal -->';
19
17
  const CLAUDE_ENTRY = `
@@ -23,6 +21,26 @@ When the user types "ramadan" or any command starting with "ramadan" (like "rama
23
21
  ${MARKER}
24
22
  `;
25
23
 
24
+ const SKILL_MARKER = '<!-- ramadan-cal-skill -->';
25
+ const RAMADAN_SKILL = `---
26
+ name: ramadan
27
+ description: Show Ramadan prayer times, Eid countdown, and Islamic calendar info. Use when the user invokes /ramadan with optional subcommands: times, calendar, eid YYYY-MM-DD, reset, check.
28
+ disable-model-invocation: true
29
+ ---
30
+
31
+ ${SKILL_MARKER}
32
+
33
+ Run the Ramadan CLI and display the output exactly as returned:
34
+
35
+ \`\`\`bash
36
+ ramadan $ARGUMENTS
37
+ \`\`\`
38
+ `;
39
+
40
+ function log(msg) {
41
+ process.stderr.write(msg + '\n');
42
+ }
43
+
26
44
  function isClaudeInstalled() {
27
45
  try {
28
46
  execSync('which claude', { stdio: 'ignore' });
@@ -32,94 +50,137 @@ function isClaudeInstalled() {
32
50
  }
33
51
  }
34
52
 
35
- function isAlreadyConfigured() {
53
+ function isClaudeMdConfigured() {
36
54
  if (!existsSync(claudeMd)) return false;
37
- const content = readFileSync(claudeMd, 'utf-8');
38
- return content.includes(MARKER);
55
+ return readFileSync(claudeMd, 'utf-8').includes(MARKER);
56
+ }
57
+
58
+ function isSkillInstalled() {
59
+ if (!existsSync(ramadanSkillFile)) return false;
60
+ return readFileSync(ramadanSkillFile, 'utf-8').includes(SKILL_MARKER);
61
+ }
62
+
63
+ function configureClaudeMd() {
64
+ if (isClaudeMdConfigured()) return;
65
+ if (!existsSync(claudeDir)) mkdirSync(claudeDir, { recursive: true });
66
+ if (existsSync(claudeMd)) {
67
+ appendFileSync(claudeMd, CLAUDE_ENTRY);
68
+ } else {
69
+ writeFileSync(claudeMd, CLAUDE_ENTRY.trimStart());
70
+ }
71
+ }
72
+
73
+ function createSkill() {
74
+ if (!existsSync(skillsDir)) mkdirSync(skillsDir, { recursive: true });
75
+ if (!existsSync(ramadanSkillDir)) mkdirSync(ramadanSkillDir, { recursive: true });
76
+ writeFileSync(ramadanSkillFile, RAMADAN_SKILL);
77
+ configureClaudeMd();
78
+ }
79
+
80
+ function canPrompt() {
81
+ try {
82
+ const fd = openSync('/dev/tty', 'r+');
83
+ closeSync(fd);
84
+ return true;
85
+ } catch {
86
+ return false;
87
+ }
39
88
  }
40
89
 
41
90
  function ask(question) {
42
- const rl = createInterface({ input: process.stdin, output: process.stdout });
43
91
  return new Promise((resolve) => {
92
+ // Use /dev/tty so prompts work even when stdin is piped (npm install)
93
+ let ttyInput, ttyOutput;
94
+ try {
95
+ ttyInput = createReadStream('/dev/tty');
96
+ ttyOutput = createWriteStream('/dev/tty', { flags: 'a' });
97
+ } catch {
98
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
99
+ rl.question(question, (answer) => { rl.close(); resolve(answer.trim().toLowerCase()); });
100
+ return;
101
+ }
102
+ const rl = createInterface({ input: ttyInput, output: ttyOutput });
44
103
  rl.question(question, (answer) => {
45
104
  rl.close();
105
+ ttyInput.destroy();
106
+ ttyOutput.destroy();
46
107
  resolve(answer.trim().toLowerCase());
47
108
  });
48
109
  });
49
110
  }
50
111
 
51
112
  async function main() {
52
- console.log('');
53
- console.log(' 🌙 Ramadan CLI installed successfully!');
54
- console.log(' Run `ramadan` to get started.');
55
- console.log('');
56
-
57
- // Check if already configured
58
- if (isAlreadyConfigured()) {
59
- console.log(' Claude Code integration already configured');
113
+ log('');
114
+ log(' 🌙 Ramadan CLI installed successfully!');
115
+ log('');
116
+
117
+ // Step 1: Check if Claude Code is installed
118
+ if (!isClaudeInstalled()) {
119
+ log(' Claude Code not detected on your PATH.');
120
+ log(' Run `npm rebuild ramadan-cal` to set up Claude Code integration later.');
121
+ log('');
122
+ log(' ➜ Run `ramadan` to get started.');
123
+ log('');
60
124
  return;
61
125
  }
62
126
 
63
- // Check if Claude is installed
64
- const claudeInstalled = isClaudeInstalled();
65
-
66
- if (!claudeInstalled) {
67
- // Not interactive — just check if they might want it later
68
- const answer = await ask(' Do you use Claude Code? (y/n) ');
69
-
70
- if (answer !== 'y' && answer !== 'yes') {
71
- console.log(' ✓ Skipping Claude Code setup. You\'re all set!\n');
72
- return;
73
- }
127
+ log(' ✓ Claude Code detected');
128
+ log('');
74
129
 
75
- // They use Claude Code but it's not on PATH
76
- console.log(' ℹ Claude Code wasn\'t detected on your PATH.');
130
+ // Already fully set up?
131
+ if (isSkillInstalled() && isClaudeMdConfigured()) {
132
+ log(' ✓ Claude Code integration already configured');
133
+ log(' Type "ramadan" or "/ramadan" in Claude Code.');
134
+ log('');
135
+ return;
136
+ }
77
137
 
78
- const proceed = await ask(' Set up the integration anyway? (y/n) ');
138
+ // If we can't open a terminal for prompts, bail out gracefully
139
+ if (!canPrompt()) {
140
+ log(' Run `npm rebuild ramadan-cal` in your terminal to finish Claude Code setup.');
141
+ log('');
142
+ return;
143
+ }
79
144
 
80
- if (proceed !== 'y' && proceed !== 'yes') {
81
- console.log(' ✓ No worries — run `npm rebuild ramadan-cal` anytime to set it up later.\n');
82
- return;
145
+ // Step 2: Check if skills folder exists
146
+ if (!existsSync(skillsDir)) {
147
+ // Skills folder doesn't exist — ask to create it
148
+ const answer = await ask(' Create a Claude Code skills folder and add the ramadan skill? (y/n) ');
149
+ log('');
150
+ if (answer === 'y' || answer === 'yes') {
151
+ createSkill();
152
+ log(' ✓ Created ~/.claude/skills/ramadan/');
153
+ log(' ✓ Ramadan skill added');
154
+ log('');
155
+ log(' Type "ramadan" or "/ramadan" in Claude Code to use it.');
156
+ } else {
157
+ log(' Skipped. Run `npm rebuild ramadan-cal` or type `ramadan` directly in your terminal.');
83
158
  }
84
159
  } else {
85
- console.log(' ✓ Claude Code detected');
86
-
87
- const answer = await ask(' Set up Claude Code integration? Type `ramadan` in Claude Code sessions. (y/n) ');
88
-
89
- if (answer !== 'y' && answer !== 'yes') {
90
- console.log(' ✓ Skipped. Run `npm rebuild ramadan-cal` anytime to set it up later.\n');
160
+ // Skills folder exists — ask if they want the skill added
161
+ if (isSkillInstalled()) {
162
+ log(' Ramadan skill already installed');
163
+ configureClaudeMd();
164
+ log(' Type "ramadan" or "/ramadan" in Claude Code.');
165
+ log('');
91
166
  return;
92
167
  }
93
- }
94
-
95
- // Create .claude directory if needed
96
- if (!existsSync(claudeDir)) {
97
- mkdirSync(claudeDir, { recursive: true });
98
- console.log(' ✓ Created ~/.claude/');
99
- }
100
168
 
101
- // Write or append to CLAUDE.md
102
- if (existsSync(claudeMd)) {
103
- appendFileSync(claudeMd, CLAUDE_ENTRY);
104
- console.log(' ✓ Added ramadan command to ~/.claude/CLAUDE.md');
105
- } else {
106
- writeFileSync(claudeMd, CLAUDE_ENTRY.trimStart());
107
- console.log(' Created ~/.claude/CLAUDE.md');
169
+ const answer = await ask(' Add the ramadan skill to Claude Code? (y/n) ');
170
+ log('');
171
+ if (answer === 'y' || answer === 'yes') {
172
+ createSkill();
173
+ log(' ✓ Ramadan skill added to ~/.claude/skills/');
174
+ log('');
175
+ log(' Type "ramadan" or "/ramadan" in Claude Code to use it.');
176
+ } else {
177
+ log(' Skipped. Run `npm rebuild ramadan-cal` or type `ramadan` directly in your terminal.');
178
+ }
108
179
  }
109
180
 
110
- console.log(' ✓ Done! Type "ramadan" inside Claude Code and it\'ll just work.\n');
111
- }
112
-
113
- // Handle non-interactive environments (CI, piped input)
114
- if (!process.stdin.isTTY) {
115
- console.log('');
116
- console.log(' 🌙 Ramadan CLI installed successfully!');
117
- console.log(' Run `ramadan` to get started.');
118
- console.log(' Run `npm rebuild ramadan-cal` in an interactive terminal to set up Claude Code.\n');
119
- process.exit(0);
181
+ log('');
120
182
  }
121
183
 
122
184
  main().catch(() => {
123
- // Non-fatal — never block the install
124
185
  process.exit(0);
125
186
  });