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 skillsDir = path.join(CLAUDE_DIR, 'skills');
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(skillsDir, skill);
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 MCP from global settings
53
- const settingsLocal = path.join(CLAUDE_DIR, 'settings.local.json');
54
- if (fs.existsSync(settingsLocal)) {
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(settingsLocal, 'utf-8'));
57
- if (settings.mcpServers?.promptup) {
58
- delete settings.mcpServers.promptup;
59
- fs.writeFileSync(settingsLocal, JSON.stringify(settings, null, 2) + '\n');
60
- console.log(` ${red}✗${reset} Removed MCP server from settings.local.json`);
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 skills to ~/.claude/skills/ ────────────────────────────
210
+ // ─── Step 2: Install dependencies ───────────────────────────────────────────
129
211
 
130
- const skillsDir = path.join(CLAUDE_DIR, 'skills');
131
- fs.mkdirSync(skillsDir, { recursive: true });
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(skillsDir, skill);
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: /${skill}`);
235
+ console.log(` ${green}✓${reset} Skill: /pup:${skill}`);
139
236
  }
140
237
  }
141
238
 
142
- // ─── Step 3: Configure MCP server ───────────────────────────────────────────
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
- if (scope === 'global') {
150
- // Add to ~/.claude/.mcp.json (global MCP config)
151
- const mcpPath = path.join(CLAUDE_DIR, '.mcp.json');
152
- const mcp = fs.existsSync(mcpPath)
153
- ? JSON.parse(fs.readFileSync(mcpPath, 'utf-8'))
154
- : {};
155
-
156
- if (!mcp.mcpServers) mcp.mcpServers = {};
157
- mcp.mcpServers.promptup = mcpEntry;
158
- fs.writeFileSync(mcpPath, JSON.stringify(mcp, null, 2) + '\n');
159
- console.log(` ${green}✓${reset} MCP server → ~/.claude/.mcp.json (global)`);
160
- } else {
161
- // Add to .mcp.json in current directory
162
- const mcpPath = path.join(process.cwd(), '.mcp.json');
163
- const mcp = fs.existsSync(mcpPath)
164
- ? JSON.parse(fs.readFileSync(mcpPath, 'utf-8'))
165
- : {};
166
-
167
- if (!mcp.mcpServers) mcp.mcpServers = {};
168
- mcp.mcpServers.promptup = mcpEntry;
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 4: Configure hooks ────────────────────────────────────────────────
268
+ // ─── Step 5: Configure hooks (in settings.json like GSD does) ───────────────
174
269
 
175
- const settingsPath = path.join(CLAUDE_DIR, 'settings.local.json');
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 5: Create default config ─────────────────────────────────────────
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 — Run an evaluation
281
- /pr-report — Generate PR report
282
- /status — Check 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptup-plugin",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "AI coding skill evaluator for Claude Code — 11-dimension scoring, decision intelligence, PR reports",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
File without changes
File without changes
File without changes