phewsh 0.15.31 → 0.15.32
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/commands/session.js +40 -5
- package/lib/harnesses.js +22 -0
- package/package.json +1 -1
package/commands/session.js
CHANGED
|
@@ -17,7 +17,7 @@ const intentDir = () => path.join(process.cwd(), '.intent');
|
|
|
17
17
|
const { select, refreshSession: refreshSess } = require('../lib/supabase');
|
|
18
18
|
const { readPPS } = require('../lib/pps');
|
|
19
19
|
const { push, pull, ensureValidToken } = require('./sync');
|
|
20
|
-
const { HARNESSES, interactiveLaunchArgs, listHarnesses, runViaHarness, cancelActive } = require('../lib/harnesses');
|
|
20
|
+
const { HARNESSES, INSTALL, interactiveLaunchArgs, listHarnesses, runViaHarness, cancelActive } = require('../lib/harnesses');
|
|
21
21
|
const { recordDecision, labelOutcome, pendingDecisions, recentDecisions, outcomeStats, OUTCOMES } = require('../lib/outcomes');
|
|
22
22
|
const { suggest, suggestAll } = require('../lib/suggest');
|
|
23
23
|
const continuity = require('../lib/continuity');
|
|
@@ -56,6 +56,23 @@ const { recordProject, listProjects, scanForProjects, fmtAgo } = require('../lib
|
|
|
56
56
|
const { b, d, w, g, green, cyan, yellow,
|
|
57
57
|
teal, peach, sage, slate, cream, ember } = ui;
|
|
58
58
|
|
|
59
|
+
// Best-effort copy to the system clipboard so the brief survives the native
|
|
60
|
+
// tool taking over the terminal (and any trust/permission gate). Returns true
|
|
61
|
+
// if a clipboard tool accepted it. Never throws — it's a convenience.
|
|
62
|
+
function copyToClipboard(text) {
|
|
63
|
+
const { spawnSync } = require('child_process');
|
|
64
|
+
const tries = process.platform === 'darwin' ? [['pbcopy', []]]
|
|
65
|
+
: process.platform === 'win32' ? [['clip', []]]
|
|
66
|
+
: [['wl-copy', []], ['xclip', ['-selection', 'clipboard']], ['xsel', ['--clipboard', '--input']]];
|
|
67
|
+
for (const [bin, args] of tries) {
|
|
68
|
+
try {
|
|
69
|
+
const r = spawnSync(bin, args, { input: text, stdio: ['pipe', 'ignore', 'ignore'] });
|
|
70
|
+
if (r.status === 0) return true;
|
|
71
|
+
} catch (_) { /* try next */ }
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
// Sync awareness: compare local .intent/ timestamps with cloud updated_at
|
|
60
77
|
async function checkSyncStatus(config) {
|
|
61
78
|
if (!config?.supabaseUserId || !config?.supabaseAccessToken) return null;
|
|
@@ -2367,7 +2384,16 @@ async function main() {
|
|
|
2367
2384
|
return;
|
|
2368
2385
|
}
|
|
2369
2386
|
if (!harnesses.find(x => x.id === target)?.installed) {
|
|
2370
|
-
|
|
2387
|
+
const how = INSTALL[target];
|
|
2388
|
+
console.log('');
|
|
2389
|
+
console.log(` ${ember('!')} ${sage(h.label + ' isn\'t installed on this machine yet.')}`);
|
|
2390
|
+
if (how) {
|
|
2391
|
+
const isCmd = !how.startsWith('see ');
|
|
2392
|
+
console.log(` ${sage('Install:')} ${isCmd ? cream(how) : slate(how)}`);
|
|
2393
|
+
}
|
|
2394
|
+
if (h.auth) console.log(` ${sage('Sign in:')} ${slate(h.auth + ' (the tool prompts you on first run)')}`);
|
|
2395
|
+
console.log(` ${sage('Then run:')} ${cream('phewsh ' + target)} ${slate('— phewsh briefs it and hands off')}`);
|
|
2396
|
+
console.log('');
|
|
2371
2397
|
rl.prompt();
|
|
2372
2398
|
return;
|
|
2373
2399
|
}
|
|
@@ -2375,9 +2401,13 @@ async function main() {
|
|
|
2375
2401
|
const preflightTruth = await auditTruth();
|
|
2376
2402
|
const before = captureSnapshot(preflightTruth);
|
|
2377
2403
|
const generatedBrief = await generateBrief({ report: preflightTruth });
|
|
2378
|
-
const launchBrief = `${generatedBrief.content}\n\
|
|
2404
|
+
const launchBrief = `${generatedBrief.content}\n\nYou are receiving a PHEWSH handoff brief — acknowledge in one short line that you've loaded it, then proceed. Use it as project context, preserve native tool behavior, and verify claims against the repository before acting. When you finish, exit this tool — PHEWSH resumes, runs an automatic postflight comparing what changed against this brief, and offers reconciliation.`;
|
|
2379
2405
|
const savedBrief = persistBrief(generatedBrief.content, { project: projectName, route: target });
|
|
2380
2406
|
const launch = interactiveLaunchArgs(target, launchBrief, { model: harnessModel });
|
|
2407
|
+
// Foolproof fallback: the brief on the clipboard survives the native
|
|
2408
|
+
// tool taking over the terminal and any trust/permission gate. If the
|
|
2409
|
+
// tool ignores or never received it, the human just pastes it in.
|
|
2410
|
+
const briefOnClipboard = copyToClipboard(launchBrief);
|
|
2381
2411
|
const decisionId = recordDecision({
|
|
2382
2412
|
project: projectName,
|
|
2383
2413
|
route: target,
|
|
@@ -2391,8 +2421,13 @@ async function main() {
|
|
|
2391
2421
|
console.log(` ${sage('Route:')} ${cream(h.label)}${harnessModel ? slate(' · model ' + harnessModel) : slate(' · native default model')}`);
|
|
2392
2422
|
console.log(` ${sage('Git:')} ${cream(preflightTruth.git.shortHead || 'unknown')} ${slate('· ' + before.dirty.length + ' uncommitted path(s) before work')}`);
|
|
2393
2423
|
console.log(` ${sage('Truth:')} ${preflightTruth.conflicts.length ? peach(preflightTruth.conflicts.length + ' conflict(s) carried explicitly') : green('no explicit conflicts')}`);
|
|
2394
|
-
console.log(` ${sage('Brief:')} ${launch.briefingPassed ? green('
|
|
2395
|
-
console.log(` ${sage('Record:')} ${savedBrief.written ? slate('exact briefing saved locally
|
|
2424
|
+
console.log(` ${sage('Brief:')} ${launch.briefingPassed ? green('auto-attached to ' + h.label) : peach('not auto-injectable for this tool')}${briefOnClipboard ? slate(' · copied to your clipboard') : ''}`);
|
|
2425
|
+
console.log(` ${sage('Record:')} ${savedBrief.written ? slate('exact briefing saved locally' + (savedBrief.file ? ' → ' + savedBrief.file : '')) : peach('briefing persistence unavailable; launch continues')}`);
|
|
2426
|
+
if (briefOnClipboard) {
|
|
2427
|
+
console.log(` ${slate('Foolproof:')} ${sage('if ' + h.label + ' shows a trust prompt or doesn\'t mention the brief, just paste it in')} ${slate('(⌘V / Ctrl+V)')}`);
|
|
2428
|
+
} else if (savedBrief.file) {
|
|
2429
|
+
console.log(` ${slate('Foolproof:')} ${sage('if ' + h.label + ' doesn\'t pick up the brief, paste from')} ${cream(savedBrief.file)}`);
|
|
2430
|
+
}
|
|
2396
2431
|
console.log(` ${slate('After exit PHEWSH will compare Git, files, intent claims, generated drift, and contradictions.')}`);
|
|
2397
2432
|
ui.divider('line');
|
|
2398
2433
|
console.log(` ${teal('●')} ${sage('Handing the terminal to')} ${cream(h.label)} ${slate('— exit to return for postflight')}`);
|
package/lib/harnesses.js
CHANGED
|
@@ -36,6 +36,27 @@ const HARNESSES = {
|
|
|
36
36
|
'droid': { bin: 'droid', label: 'Droid', role: 'agentic coding', bestFor: 'agentic coding', auth: 'Factory account', args: (p) => ['exec', p] },
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
// Best-effort install hints, shown when a harness isn't on the machine yet.
|
|
40
|
+
// Commands where the install path is well-known and stable; a docs pointer
|
|
41
|
+
// otherwise. We never auto-run these — phewsh shows them; the human decides.
|
|
42
|
+
// (Install paths and auth change upstream; treat as a guidepost, not gospel.)
|
|
43
|
+
const INSTALL = {
|
|
44
|
+
'claude-code': 'npm i -g @anthropic-ai/claude-code',
|
|
45
|
+
'codex': 'npm i -g @openai/codex',
|
|
46
|
+
'gemini': 'npm i -g @google/gemini-cli',
|
|
47
|
+
'cursor': 'curl https://cursor.com/install -fsS | bash',
|
|
48
|
+
'opencode': 'npm i -g opencode-ai',
|
|
49
|
+
'grok': 'npm i -g @vibe-kit/grok-cli',
|
|
50
|
+
'copilot': 'npm i -g @github/copilot',
|
|
51
|
+
'kiro': 'see kiro.dev/downloads',
|
|
52
|
+
'hermes': 'see nousresearch.com',
|
|
53
|
+
'pi': 'see pi.ai',
|
|
54
|
+
'aider': 'python -m pip install aider-chat',
|
|
55
|
+
'goose': 'see block.github.io/goose',
|
|
56
|
+
'amp': 'npm i -g @sourcegraph/amp',
|
|
57
|
+
'droid': 'see factory.ai',
|
|
58
|
+
};
|
|
59
|
+
|
|
39
60
|
// In-flight harness children — so ESC in the session can cancel a turn.
|
|
40
61
|
const ACTIVE_CHILDREN = new Set();
|
|
41
62
|
|
|
@@ -199,6 +220,7 @@ function runViaHarness(id, systemPrompt, userPrompt, opts = {}) {
|
|
|
199
220
|
|
|
200
221
|
module.exports = {
|
|
201
222
|
HARNESSES,
|
|
223
|
+
INSTALL,
|
|
202
224
|
isInstalled,
|
|
203
225
|
detectInstalled,
|
|
204
226
|
interactiveLaunchArgs,
|