promptup-plugin 0.1.8 → 0.2.0

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/dist/evaluator.js CHANGED
@@ -7,10 +7,14 @@
7
7
  * STANDALONE copy — no imports from @promptup/shared or session-watcher.
8
8
  */
9
9
  import { spawn } from 'node:child_process';
10
+ import { writeFileSync, unlinkSync } from 'node:fs';
11
+ import { tmpdir } from 'node:os';
12
+ import { join } from 'node:path';
10
13
  import { ulid } from 'ulid';
11
14
  import { BASE_DIMENSIONS, BASE_DIMENSION_KEYS, DOMAIN_DIMENSIONS, DOMAIN_DIMENSION_KEYS, WEIGHT_PROFILES, } from './shared/dimensions.js';
12
15
  import { computeCompositeScore, computeDomainComposite, computeTechComposite, computeOverallComposite, computeGrandComposite, computeRiskFlagsWithHistory, } from './shared/scoring.js';
13
16
  import { getLatestEvaluation, insertEvaluation, insertDecision, } from './db.js';
17
+ import { detectDecisions } from './decision-detector.js';
14
18
  /**
15
19
  * Combined role + skill roadmaps catalog for tech detection.
16
20
  * Mirrors the full list from @promptup/shared/roadmaps without importing it.
@@ -182,12 +186,18 @@ Return ONLY valid JSON with no markdown formatting, no code fences, no extra tex
182
186
  }
183
187
  function runClaudeCode(prompt, timeoutMs = 180_000) {
184
188
  return new Promise((resolve, reject) => {
189
+ // Write prompt to temp file to avoid stdin piping conflicts.
190
+ // The MCP server uses stdio for its protocol — spawning claude -p
191
+ // with piped stdin from within an MCP server causes hangs because
192
+ // the child's stdin competes with the parent's MCP pipe.
193
+ const tmpFile = join(tmpdir(), `promptup-eval-${Date.now()}.txt`);
194
+ writeFileSync(tmpFile, prompt, 'utf-8');
185
195
  // Strip CLAUDECODE env var to allow spawning from within a Claude Code session
186
196
  const env = { ...process.env };
187
197
  delete env.CLAUDECODE;
188
198
  delete env.CLAUDE_CODE;
189
- const proc = spawn('claude', ['-p', '--output-format', 'text', '--no-session-persistence'], {
190
- stdio: ['pipe', 'pipe', 'pipe'],
199
+ const proc = spawn('bash', ['-c', `cat "${tmpFile}" | claude -p --output-format text --no-session-persistence`], {
200
+ stdio: ['ignore', 'pipe', 'pipe'],
191
201
  env,
192
202
  });
193
203
  let stdout = '';
@@ -196,10 +206,12 @@ function runClaudeCode(prompt, timeoutMs = 180_000) {
196
206
  proc.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
197
207
  const timer = setTimeout(() => {
198
208
  proc.kill('SIGTERM');
209
+ try { unlinkSync(tmpFile); } catch {}
199
210
  reject(new Error(`[timeout] Claude Code timed out after ${timeoutMs}ms (prompt size: ${prompt.length} chars)`));
200
211
  }, timeoutMs);
201
212
  proc.on('close', (code) => {
202
213
  clearTimeout(timer);
214
+ try { unlinkSync(tmpFile); } catch {}
203
215
  if (code === 0) {
204
216
  resolve(stdout.trim());
205
217
  }
@@ -209,17 +221,9 @@ function runClaudeCode(prompt, timeoutMs = 180_000) {
209
221
  });
210
222
  proc.on('error', (err) => {
211
223
  clearTimeout(timer);
224
+ try { unlinkSync(tmpFile); } catch {}
212
225
  reject(new Error(`[spawn] Could not start claude: ${err.message}`));
213
226
  });
214
- // Write prompt to stdin with backpressure handling
215
- const ok = proc.stdin.write(prompt);
216
- if (!ok) {
217
- // Buffer is full — wait for drain before closing
218
- proc.stdin.once('drain', () => { proc.stdin.end(); });
219
- }
220
- else {
221
- proc.stdin.end();
222
- }
223
227
  });
224
228
  }
225
229
  function parseClaudeResponse(raw) {
@@ -362,6 +366,19 @@ export async function evaluateSession(sessionId, messages, triggerType, weightPr
362
366
  domainDimensionScores = heuristic.domainDimensionScores;
363
367
  techExpertise = heuristicTechDetect(messages);
364
368
  recommendations = heuristic.recommendations;
369
+ // Extract decisions via heuristic detector (Claude path does this via LLM)
370
+ try {
371
+ const heuristicDecisions = detectDecisions(messages, sessionId);
372
+ if (heuristicDecisions.length > 0) {
373
+ for (const d of heuristicDecisions) {
374
+ insertDecision(d);
375
+ }
376
+ console.log(`[eval] Heuristic extracted ${heuristicDecisions.length} decisions`);
377
+ }
378
+ }
379
+ catch (decErr) {
380
+ console.warn(`[eval] Heuristic decision extraction failed: ${decErr}`);
381
+ }
365
382
  rawEvaluation = JSON.stringify({
366
383
  activity_log: heuristicActivityLog(messages),
367
384
  domain_dimensions: domainDimensionScores,
@@ -130,22 +130,23 @@ function matchSessionsToBranch(branch, commits, projectPath) {
130
130
  }
131
131
  // ─── Decision gathering ───────────────────────────────────────────────────────
132
132
  function gatherDecisions(sessionIds) {
133
- // First try plugin/daemon-captured decisions
133
+ // Get existing decisions from DB
134
134
  const existing = getDecisionsBySessions(sessionIds);
135
- if (existing.length > 0)
136
- return existing;
137
- // Fall back to heuristic detection from messages
138
- const heuristic = [];
135
+ // Also run heuristic detection on sessions that have no decisions yet
136
+ const sessionsWithDecisions = new Set(existing.map(d => d.session_id));
139
137
  for (const sid of sessionIds) {
138
+ if (sessionsWithDecisions.has(sid))
139
+ continue; // Already has decisions from Claude or previous heuristic
140
140
  const messages = getMessagesBySession(sid, 10000, 0);
141
+ if (messages.length < 3)
142
+ continue;
141
143
  const detected = detectDecisions(messages, sid);
142
- // Persist detected decisions so they're available in future queries
143
144
  for (const d of detected) {
144
145
  insertDecision(d);
145
146
  }
146
- heuristic.push(...detected);
147
+ existing.push(...detected);
147
148
  }
148
- return heuristic;
149
+ return existing;
149
150
  }
150
151
  function getSignal(d) {
151
152
  return d.signal ?? 'low';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptup-plugin",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
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",