promptup-plugin 0.1.1 → 0.1.2
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/bin/install.cjs +133 -42
- package/package.json +1 -1
package/bin/install.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
6
7
|
|
|
7
8
|
// Colors
|
|
8
9
|
const cyan = '\x1b[36m';
|
|
@@ -40,28 +41,106 @@ if (hasUninstall) {
|
|
|
40
41
|
console.log(`${yellow}Uninstalling PromptUp...${reset}\n`);
|
|
41
42
|
|
|
42
43
|
// Remove skills
|
|
43
|
-
const skillsDir = path.join(CLAUDE_DIR, 'skills');
|
|
44
44
|
for (const skill of ['eval', 'pr-report', 'status']) {
|
|
45
|
-
const dest = path.join(
|
|
45
|
+
const dest = path.join(CLAUDE_DIR, 'skills', skill);
|
|
46
46
|
if (fs.existsSync(dest)) {
|
|
47
47
|
fs.rmSync(dest, { recursive: true });
|
|
48
48
|
console.log(` ${red}✗${reset} Removed skill: ${skill}`);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// Remove
|
|
53
|
-
const
|
|
54
|
-
if (fs.existsSync(
|
|
52
|
+
// Remove hooks from settings.json
|
|
53
|
+
const settingsPath = path.join(CLAUDE_DIR, 'settings.json');
|
|
54
|
+
if (fs.existsSync(settingsPath)) {
|
|
55
55
|
try {
|
|
56
|
-
const settings = JSON.parse(fs.readFileSync(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
57
|
+
let changed = false;
|
|
58
|
+
|
|
59
|
+
for (const event of ['SessionStart', 'UserPromptSubmit']) {
|
|
60
|
+
if (settings.hooks?.[event]) {
|
|
61
|
+
settings.hooks[event] = settings.hooks[event].filter(
|
|
62
|
+
(h) => !h.hooks?.some((hk) => hk.command?.includes('.promptup')),
|
|
63
|
+
);
|
|
64
|
+
if (settings.hooks[event].length === 0) delete settings.hooks[event];
|
|
65
|
+
changed = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (settings.statusLine?.command?.includes('.promptup')) {
|
|
70
|
+
delete settings.statusLine;
|
|
71
|
+
changed = true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (changed) {
|
|
75
|
+
if (settings.hooks && Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
76
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
77
|
+
console.log(` ${red}✗${reset} Removed hooks and statusline from settings.json`);
|
|
78
|
+
}
|
|
79
|
+
} catch {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Also clean settings.local.json (from older installs)
|
|
83
|
+
const settingsLocalPath = path.join(CLAUDE_DIR, 'settings.local.json');
|
|
84
|
+
if (fs.existsSync(settingsLocalPath)) {
|
|
85
|
+
try {
|
|
86
|
+
const settings = JSON.parse(fs.readFileSync(settingsLocalPath, 'utf-8'));
|
|
87
|
+
let changed = false;
|
|
88
|
+
|
|
89
|
+
for (const event of ['SessionStart', 'UserPromptSubmit']) {
|
|
90
|
+
if (settings.hooks?.[event]) {
|
|
91
|
+
settings.hooks[event] = settings.hooks[event].filter(
|
|
92
|
+
(h) => !h.hooks?.some((hk) => hk.command?.includes('.promptup')),
|
|
93
|
+
);
|
|
94
|
+
if (settings.hooks[event].length === 0) delete settings.hooks[event];
|
|
95
|
+
changed = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (settings.statusLine?.command?.includes('.promptup')) {
|
|
100
|
+
delete settings.statusLine;
|
|
101
|
+
changed = true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (changed) {
|
|
105
|
+
if (settings.hooks && Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
106
|
+
const remaining = Object.keys(settings).length;
|
|
107
|
+
if (remaining === 0) {
|
|
108
|
+
fs.unlinkSync(settingsLocalPath);
|
|
109
|
+
} else {
|
|
110
|
+
fs.writeFileSync(settingsLocalPath, JSON.stringify(settings, null, 2) + '\n');
|
|
111
|
+
}
|
|
112
|
+
console.log(` ${red}✗${reset} Cleaned settings.local.json`);
|
|
61
113
|
}
|
|
62
114
|
} catch {}
|
|
63
115
|
}
|
|
64
116
|
|
|
117
|
+
// Remove MCP from .mcp.json files
|
|
118
|
+
for (const mcpPath of [
|
|
119
|
+
path.join(CLAUDE_DIR, '.mcp.json'),
|
|
120
|
+
path.join(process.cwd(), '.mcp.json'),
|
|
121
|
+
]) {
|
|
122
|
+
if (fs.existsSync(mcpPath)) {
|
|
123
|
+
try {
|
|
124
|
+
const mcp = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));
|
|
125
|
+
if (mcp.mcpServers?.promptup) {
|
|
126
|
+
delete mcp.mcpServers.promptup;
|
|
127
|
+
if (Object.keys(mcp.mcpServers).length === 0) {
|
|
128
|
+
fs.unlinkSync(mcpPath);
|
|
129
|
+
} else {
|
|
130
|
+
fs.writeFileSync(mcpPath, JSON.stringify(mcp, null, 2) + '\n');
|
|
131
|
+
}
|
|
132
|
+
console.log(` ${red}✗${reset} Removed MCP from ${mcpPath}`);
|
|
133
|
+
}
|
|
134
|
+
} catch {}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Remove plugin dir
|
|
139
|
+
if (fs.existsSync(PLUGIN_DIR)) {
|
|
140
|
+
fs.rmSync(PLUGIN_DIR, { recursive: true });
|
|
141
|
+
console.log(` ${red}✗${reset} Removed plugin at ${PLUGIN_DIR}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
65
144
|
console.log(`\n${green}PromptUp uninstalled.${reset}`);
|
|
66
145
|
console.log(`${dim}Data preserved at ${DATA_DIR} — delete manually if desired.${reset}\n`);
|
|
67
146
|
process.exit(0);
|
|
@@ -74,7 +153,6 @@ if (hasLocal) scope = 'local';
|
|
|
74
153
|
if (hasGlobal) scope = 'global';
|
|
75
154
|
|
|
76
155
|
if (!hasLocal && !hasGlobal) {
|
|
77
|
-
// Default to global
|
|
78
156
|
scope = 'global';
|
|
79
157
|
console.log(`${dim}Installing globally (use --local for project-only)${reset}\n`);
|
|
80
158
|
}
|
|
@@ -87,7 +165,6 @@ const packageRoot = path.resolve(__dirname, '..');
|
|
|
87
165
|
|
|
88
166
|
console.log(`${bold}Setting up PromptUp...${reset}\n`);
|
|
89
167
|
|
|
90
|
-
// Ensure dirs exist
|
|
91
168
|
fs.mkdirSync(PLUGIN_DIR, { recursive: true });
|
|
92
169
|
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
93
170
|
|
|
@@ -97,7 +174,6 @@ console.log(` ${green}✓${reset} Installed plugin runtime`);
|
|
|
97
174
|
|
|
98
175
|
// Copy hooks/
|
|
99
176
|
copyDirSync(path.join(packageRoot, 'hooks'), path.join(PLUGIN_DIR, 'hooks'));
|
|
100
|
-
// Make hooks executable
|
|
101
177
|
for (const f of fs.readdirSync(path.join(PLUGIN_DIR, 'hooks'))) {
|
|
102
178
|
if (f.endsWith('.sh')) {
|
|
103
179
|
fs.chmodSync(path.join(PLUGIN_DIR, 'hooks', f), 0o755);
|
|
@@ -119,13 +195,28 @@ if (fs.existsSync(path.join(packageRoot, 'statusline.sh'))) {
|
|
|
119
195
|
console.log(` ${green}✓${reset} Installed statusline`);
|
|
120
196
|
}
|
|
121
197
|
|
|
122
|
-
// Copy package.json for version tracking
|
|
198
|
+
// Copy package.json for version tracking + dependency install
|
|
123
199
|
fs.copyFileSync(
|
|
124
200
|
path.join(packageRoot, 'package.json'),
|
|
125
201
|
path.join(PLUGIN_DIR, 'package.json'),
|
|
126
202
|
);
|
|
127
203
|
|
|
128
|
-
// ─── Step 2: Install
|
|
204
|
+
// ─── Step 2: Install dependencies ───────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
console.log(` ${dim}Installing dependencies (better-sqlite3, MCP SDK)...${reset}`);
|
|
207
|
+
try {
|
|
208
|
+
execSync('npm install --production --no-audit --no-fund', {
|
|
209
|
+
cwd: PLUGIN_DIR,
|
|
210
|
+
stdio: 'pipe',
|
|
211
|
+
timeout: 120000,
|
|
212
|
+
});
|
|
213
|
+
console.log(` ${green}✓${reset} Dependencies installed`);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
console.log(` ${red}✗${reset} Dependency install failed: ${err.message}`);
|
|
216
|
+
console.log(` ${yellow}Try manually: cd ${PLUGIN_DIR} && npm install --production${reset}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ─── Step 3: Install skills to ~/.claude/skills/ ────────────────────────────
|
|
129
220
|
|
|
130
221
|
const skillsDir = path.join(CLAUDE_DIR, 'skills');
|
|
131
222
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
@@ -139,40 +230,38 @@ for (const skill of ['eval', 'pr-report', 'status']) {
|
|
|
139
230
|
}
|
|
140
231
|
}
|
|
141
232
|
|
|
142
|
-
// ─── Step
|
|
233
|
+
// ─── Step 4: Configure MCP server ───────────────────────────────────────────
|
|
143
234
|
|
|
144
235
|
const mcpEntry = {
|
|
145
236
|
command: 'node',
|
|
146
237
|
args: [path.join(PLUGIN_DIR, 'dist', 'index.js')],
|
|
147
238
|
};
|
|
148
239
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
mcp.
|
|
169
|
-
fs.writeFileSync(mcpPath, JSON.stringify(mcp, null, 2) + '\n');
|
|
170
|
-
console.log(` ${green}✓${reset} MCP server → .mcp.json (local)`);
|
|
240
|
+
// MCP always goes to project .mcp.json (Claude Code reads MCP from here)
|
|
241
|
+
const mcpPath = path.join(process.cwd(), '.mcp.json');
|
|
242
|
+
const mcp = fs.existsSync(mcpPath)
|
|
243
|
+
? JSON.parse(fs.readFileSync(mcpPath, 'utf-8'))
|
|
244
|
+
: {};
|
|
245
|
+
|
|
246
|
+
if (!mcp.mcpServers) mcp.mcpServers = {};
|
|
247
|
+
mcp.mcpServers.promptup = mcpEntry;
|
|
248
|
+
fs.writeFileSync(mcpPath, JSON.stringify(mcp, null, 2) + '\n');
|
|
249
|
+
console.log(` ${green}✓${reset} MCP server → .mcp.json`);
|
|
250
|
+
|
|
251
|
+
// Also try to register globally via claude CLI (silent fail if not available)
|
|
252
|
+
try {
|
|
253
|
+
execSync(
|
|
254
|
+
`claude mcp add promptup -s user -- node ${path.join(PLUGIN_DIR, 'dist', 'index.js')}`,
|
|
255
|
+
{ stdio: 'pipe', timeout: 10000 },
|
|
256
|
+
);
|
|
257
|
+
console.log(` ${green}✓${reset} MCP server → claude global config`);
|
|
258
|
+
} catch {
|
|
259
|
+
// claude CLI not available or failed — that's fine, .mcp.json is enough
|
|
171
260
|
}
|
|
172
261
|
|
|
173
|
-
// ─── Step
|
|
262
|
+
// ─── Step 5: Configure hooks (in settings.json like GSD does) ───────────────
|
|
174
263
|
|
|
175
|
-
const settingsPath = path.join(CLAUDE_DIR, 'settings.
|
|
264
|
+
const settingsPath = path.join(CLAUDE_DIR, 'settings.json');
|
|
176
265
|
const settings = fs.existsSync(settingsPath)
|
|
177
266
|
? JSON.parse(fs.readFileSync(settingsPath, 'utf-8'))
|
|
178
267
|
: {};
|
|
@@ -217,7 +306,7 @@ if (!hasAutoEval) {
|
|
|
217
306
|
console.log(` ${green}✓${reset} Hook: UserPromptSubmit → auto-eval`);
|
|
218
307
|
}
|
|
219
308
|
|
|
220
|
-
// Statusline
|
|
309
|
+
// Statusline (respect existing — prompt if already set, like GSD)
|
|
221
310
|
if (!settings.statusLine) {
|
|
222
311
|
settings.statusLine = {
|
|
223
312
|
type: 'command',
|
|
@@ -225,11 +314,13 @@ if (!settings.statusLine) {
|
|
|
225
314
|
padding: 2,
|
|
226
315
|
};
|
|
227
316
|
console.log(` ${green}✓${reset} Statusline: pupmeter`);
|
|
317
|
+
} else if (!settings.statusLine.command?.includes('.promptup')) {
|
|
318
|
+
console.log(` ${yellow}⚠${reset} Statusline already configured — skipped (existing: ${settings.statusLine.command?.slice(0, 40)}...)`);
|
|
228
319
|
}
|
|
229
320
|
|
|
230
321
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
231
322
|
|
|
232
|
-
// ─── Step
|
|
323
|
+
// ─── Step 6: Create default config ─────────────────────────────────────────
|
|
233
324
|
|
|
234
325
|
const configPath = path.join(DATA_DIR, 'config.json');
|
|
235
326
|
if (!fs.existsSync(configPath)) {
|
package/package.json
CHANGED