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 +28 -11
- package/dist/pr-report-generator.js +9 -8
- package/package.json +1 -1
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('
|
|
190
|
-
stdio: ['
|
|
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
|
-
//
|
|
133
|
+
// Get existing decisions from DB
|
|
134
134
|
const existing = getDecisionsBySessions(sessionIds);
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
147
|
+
existing.push(...detected);
|
|
147
148
|
}
|
|
148
|
-
return
|
|
149
|
+
return existing;
|
|
149
150
|
}
|
|
150
151
|
function getSignal(d) {
|
|
151
152
|
return d.signal ?? 'low';
|
package/package.json
CHANGED