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 +1 -1
- package/scripts/postinstall.js +127 -66
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -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
|
|
53
|
+
function isClaudeMdConfigured() {
|
|
36
54
|
if (!existsSync(claudeMd)) return false;
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
});
|