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