phewsh 0.15.46 → 0.15.48
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/phewsh.js +8 -5
- package/commands/pack.js +97 -0
- package/lib/harnesses.js +23 -0
- package/lib/packs.js +127 -0
- package/package.json +1 -1
package/bin/phewsh.js
CHANGED
|
@@ -88,6 +88,7 @@ const COMMANDS = {
|
|
|
88
88
|
ambient: () => require('../commands/ambient')(),
|
|
89
89
|
shim: () => require('../commands/shim')(),
|
|
90
90
|
status: () => require('../commands/status')(),
|
|
91
|
+
pack: () => require('../commands/pack')(),
|
|
91
92
|
hook: () => require('../commands/hook')(),
|
|
92
93
|
welcome: () => require('../commands/welcome')(),
|
|
93
94
|
intro: () => require('../commands/welcome')(),
|
|
@@ -128,6 +129,7 @@ function showHelp() {
|
|
|
128
129
|
console.log(` ${cyan('mcp')} ${g('Connect AI agents via MCP protocol')}`);
|
|
129
130
|
console.log(` ${cyan('ambient')} ${g('Continuity without launching phewsh — enhance your other tools')}`);
|
|
130
131
|
console.log(` ${cyan('shim')} ${g('Guaranteed launch banner — phewsh prints status before each tool')}`);
|
|
132
|
+
console.log(` ${cyan('pack')} ${g('Opt-in workflow packs (Karpathy guidelines, GSD…) — attributed, reversible')}`);
|
|
131
133
|
console.log(` ${cyan('receipts')} ${g('Proof trail — what agents actually did, with evidence')}`);
|
|
132
134
|
console.log(` ${cyan('outcomes')} ${g('Decision record — what was kept, reverted, or failed')}`);
|
|
133
135
|
console.log(` ${cyan('bypass')} ${g('Went around phewsh? Record why — 10 seconds, no guilt')}`);
|
|
@@ -228,11 +230,12 @@ if (!command) {
|
|
|
228
230
|
exitAfterUpdate(0);
|
|
229
231
|
} else if (COMMANDS[command]) {
|
|
230
232
|
COMMANDS[command]();
|
|
231
|
-
} else if (require('../lib/harnesses').
|
|
232
|
-
// `phewsh <harness>` — a doorway shortcut.
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
|
|
233
|
+
} else if (require('../lib/harnesses').resolveHarness(command)) {
|
|
234
|
+
// `phewsh <harness>` — a doorway shortcut. Accepts the id OR the tool's real
|
|
235
|
+
// binary (so `phewsh claude` works, since that's the Claude Code binary).
|
|
236
|
+
// Launch the session and auto-run /work for that harness (preflight → brief →
|
|
237
|
+
// native handoff → postflight). This is what the phewsh.com doorways copy.
|
|
238
|
+
process.env.PHEWSH_AUTOWORK = require('../lib/harnesses').resolveHarness(command);
|
|
236
239
|
ambientSelfHeal().finally(() => maybeFirstRunIntro().then(() => COMMANDS.session()));
|
|
237
240
|
} else {
|
|
238
241
|
console.error(`\n Unknown command: ${command}\n Run 'phewsh help' for available commands.\n`);
|
package/commands/pack.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// phewsh pack — opt-in gateway to AI-workflow enhancements (Karpathy-style
|
|
2
|
+
// guidelines, GSD, …). Attributed, previewed, confirmed, reversible. phewsh's
|
|
3
|
+
// core stays continuity; packs are an optional layer you choose.
|
|
4
|
+
//
|
|
5
|
+
// phewsh pack list packs (available + installed)
|
|
6
|
+
// phewsh pack install <name> show source/license + diff, confirm, install
|
|
7
|
+
// phewsh pack remove <name> remove a vendored pack's marked block
|
|
8
|
+
|
|
9
|
+
const readline = require('readline');
|
|
10
|
+
const packs = require('../lib/packs');
|
|
11
|
+
|
|
12
|
+
const b = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
13
|
+
const teal = (s) => `\x1b[38;5;79m${s}\x1b[0m`;
|
|
14
|
+
const sage = (s) => `\x1b[38;5;151m${s}\x1b[0m`;
|
|
15
|
+
const slate = (s) => `\x1b[38;5;247m${s}\x1b[0m`;
|
|
16
|
+
const cream = (s) => `\x1b[38;5;230m${s}\x1b[0m`;
|
|
17
|
+
const peach = (s) => `\x1b[38;5;216m${s}\x1b[0m`;
|
|
18
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
19
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
20
|
+
|
|
21
|
+
async function confirm(q) {
|
|
22
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
23
|
+
return new Promise(res => rl.question(q, a => { rl.close(); res(/^y(es)?$/i.test(a.trim())); }));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function list() {
|
|
27
|
+
console.log('');
|
|
28
|
+
console.log(` ${b(cream('phewsh packs'))} ${sage('— optional, attributed, reversible enhancements')}`);
|
|
29
|
+
console.log(` ${slate('phewsh\'s core is continuity; these are extras you opt into.')}`);
|
|
30
|
+
console.log('');
|
|
31
|
+
for (const [name, p] of Object.entries(packs.PACKS)) {
|
|
32
|
+
const on = packs.isInstalled(name);
|
|
33
|
+
const tag = p.kind === 'linked' ? slate('[linked]') : (on ? green('[installed]') : slate('[available]'));
|
|
34
|
+
console.log(` ${cream(name.padEnd(16))} ${tag}`);
|
|
35
|
+
console.log(` ${slate(p.desc)}`);
|
|
36
|
+
console.log(` ${slate('source: ' + p.source)}`);
|
|
37
|
+
}
|
|
38
|
+
console.log('');
|
|
39
|
+
console.log(` ${sage('Install:')} ${cream('phewsh pack install <name>')} ${sage('Remove:')} ${cream('phewsh pack remove <name>')}`);
|
|
40
|
+
console.log('');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function install(name) {
|
|
44
|
+
const p = packs.PACKS[name];
|
|
45
|
+
if (!p) { console.log(`\n ${yellow('Unknown pack:')} ${name}. See ${cream('phewsh pack')}.\n`); return; }
|
|
46
|
+
|
|
47
|
+
console.log('');
|
|
48
|
+
console.log(` ${b(cream(p.title))} ${slate('(pack: ' + name + ')')}`);
|
|
49
|
+
console.log(` ${slate(p.desc)}`);
|
|
50
|
+
console.log(` ${peach('source:')} ${slate(p.source)}`);
|
|
51
|
+
console.log(` ${peach('license:')} ${slate(p.license)}`);
|
|
52
|
+
console.log('');
|
|
53
|
+
|
|
54
|
+
if (p.kind === 'linked') {
|
|
55
|
+
console.log(` ${sage('This is a separate tool — phewsh doesn\'t vendor it. Install it with:')}`);
|
|
56
|
+
console.log(` ${cream(p.install)}`);
|
|
57
|
+
console.log('');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const preview = packs.previewInstall(name);
|
|
62
|
+
console.log(` ${sage('Writes a clearly-marked, removable block into:')} ${cream(preview.files.join(', '))}`);
|
|
63
|
+
console.log(` ${slate('Preview:')}`);
|
|
64
|
+
preview.block.split('\n').slice(0, 8).forEach(l => console.log(` ${slate('│ ' + l)}`));
|
|
65
|
+
console.log(` ${slate('│ … (' + preview.block.split('\n').length + ' lines total)')}`);
|
|
66
|
+
console.log('');
|
|
67
|
+
|
|
68
|
+
const ok = await confirm(` ${b('Install this pack here?')} ${slate('[y/N] ')}`);
|
|
69
|
+
if (!ok) { console.log(` ${sage('Nothing changed.')}\n`); return; }
|
|
70
|
+
|
|
71
|
+
const { written } = packs.install(name);
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log(` ${green('●')} ${b('Installed.')} ${slate('→ ' + written.join(', '))}`);
|
|
74
|
+
console.log(` ${sage('Remove anytime:')} ${cream('phewsh pack remove ' + name)}`);
|
|
75
|
+
console.log('');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function remove(name) {
|
|
79
|
+
const p = packs.PACKS[name];
|
|
80
|
+
if (!p) { console.log(`\n ${yellow('Unknown pack:')} ${name}.\n`); return; }
|
|
81
|
+
if (p.kind === 'linked') { console.log(`\n ${sage(p.title + ' is a separate tool — uninstall it the way you installed it.')}\n`); return; }
|
|
82
|
+
const { removed } = packs.remove(name);
|
|
83
|
+
console.log('');
|
|
84
|
+
if (removed.length) console.log(` ${green('●')} ${b('Removed.')} ${slate('← ' + removed.join(', '))}`);
|
|
85
|
+
else console.log(` ${sage('Not installed here — nothing to remove.')}`);
|
|
86
|
+
console.log('');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function main() {
|
|
90
|
+
const sub = process.argv[3];
|
|
91
|
+
const name = process.argv[4];
|
|
92
|
+
if (sub === 'install' && name) return install(name);
|
|
93
|
+
if (sub === 'remove' && name) return remove(name);
|
|
94
|
+
return list();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = main;
|
package/lib/harnesses.js
CHANGED
|
@@ -218,9 +218,32 @@ function runViaHarness(id, systemPrompt, userPrompt, opts = {}) {
|
|
|
218
218
|
});
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
// Resolve a user-typed token to a harness id. Accepts the canonical id, the
|
|
222
|
+
// tool's actual binary (so `phewsh claude` → claude-code, since `claude` is the
|
|
223
|
+
// Claude Code binary; `phewsh cursor-agent` → cursor), or a friendly alias.
|
|
224
|
+
// Returns the harness id, or null if nothing matches.
|
|
225
|
+
const HARNESS_ALIASES = {
|
|
226
|
+
claude: 'claude-code',
|
|
227
|
+
cc: 'claude-code',
|
|
228
|
+
'cursor-agent': 'cursor',
|
|
229
|
+
'kiro-cli': 'kiro',
|
|
230
|
+
copilot: 'copilot',
|
|
231
|
+
};
|
|
232
|
+
function resolveHarness(token) {
|
|
233
|
+
if (!token) return null;
|
|
234
|
+
const t = String(token).toLowerCase();
|
|
235
|
+
if (HARNESSES[t]) return t;
|
|
236
|
+
if (HARNESS_ALIASES[t]) return HARNESS_ALIASES[t];
|
|
237
|
+
for (const [id, h] of Object.entries(HARNESSES)) {
|
|
238
|
+
if (h.bin && h.bin.toLowerCase() === t) return id;
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
|
|
221
243
|
module.exports = {
|
|
222
244
|
HARNESSES,
|
|
223
245
|
INSTALL,
|
|
246
|
+
resolveHarness,
|
|
224
247
|
isInstalled,
|
|
225
248
|
detectInstalled,
|
|
226
249
|
interactiveLaunchArgs,
|
package/lib/packs.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// phewsh packs — an opt-in gateway to proven AI-workflow enhancements.
|
|
2
|
+
//
|
|
3
|
+
// phewsh's core is project truth + continuity + record. It is NOT a grab-bag of
|
|
4
|
+
// every viral CLAUDE.md. But good ideas exist out there, and phewsh is a natural
|
|
5
|
+
// front door to them — IF they stay optional, attributed, and reversible. So:
|
|
6
|
+
// • Nothing is vendored or injected by default (never in ambient/first-run).
|
|
7
|
+
// • `phewsh pack install <name>` shows the source + license + a diff preview
|
|
8
|
+
// and asks before writing.
|
|
9
|
+
// • `phewsh pack remove <name>` cleanly reverses it (marked block only).
|
|
10
|
+
//
|
|
11
|
+
// Two kinds of pack:
|
|
12
|
+
// • vendored — phewsh writes a clearly-marked block into the project's agent
|
|
13
|
+
// files (e.g. Karpathy's coding guidelines into CLAUDE.md/AGENTS.md).
|
|
14
|
+
// • linked — phewsh doesn't vendor it; it points you at the upstream
|
|
15
|
+
// installer (e.g. GSD is its own tool — we hand you its command).
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
const KARPATHY_GUIDELINES = `## Coding guidelines
|
|
21
|
+
**Tradeoff:** these bias toward caution over speed. For trivial tasks, use judgment.
|
|
22
|
+
|
|
23
|
+
### 1. Think before coding
|
|
24
|
+
State assumptions explicitly; if uncertain, ask. If multiple interpretations exist, present them — don't pick silently. If a simpler approach exists, say so. If something's unclear, stop and name it.
|
|
25
|
+
|
|
26
|
+
### 2. Simplicity first
|
|
27
|
+
Minimum code that solves the problem, nothing speculative. No features beyond what was asked, no abstractions for single-use code, no error handling for impossible cases. If 200 lines could be 50, rewrite. Ask: "would a senior engineer call this overcomplicated?"
|
|
28
|
+
|
|
29
|
+
### 3. Surgical changes
|
|
30
|
+
Touch only what you must. Don't "improve" adjacent code, don't refactor what isn't broken, match existing style. Remove only the orphans YOUR change created; mention pre-existing dead code, don't delete it. Every changed line should trace to the request.
|
|
31
|
+
|
|
32
|
+
### 4. Goal-driven execution
|
|
33
|
+
Turn tasks into verifiable goals ("add validation" → "write tests for invalid inputs, then make them pass"). For multi-step work, state a brief plan with a verify step each. Strong success criteria let you loop independently.
|
|
34
|
+
|
|
35
|
+
**Working if:** fewer unnecessary diffs, fewer rewrites from overcomplication, and clarifying questions come before mistakes, not after.`;
|
|
36
|
+
|
|
37
|
+
const PACKS = {
|
|
38
|
+
'karpathy-style': {
|
|
39
|
+
kind: 'vendored',
|
|
40
|
+
title: 'Karpathy-style coding guidelines',
|
|
41
|
+
desc: 'Behavioral guidelines that reduce common LLM coding mistakes (think-before-coding, simplicity, surgical changes, goal-driven loops).',
|
|
42
|
+
source: "Andrej Karpathy's CLAUDE.md · github.com/multica-ai/andrej-karpathy-skills",
|
|
43
|
+
license: 'review the source repo for license before redistributing',
|
|
44
|
+
targets: ['CLAUDE.md', 'AGENTS.md'],
|
|
45
|
+
content: KARPATHY_GUIDELINES,
|
|
46
|
+
},
|
|
47
|
+
gsd: {
|
|
48
|
+
kind: 'linked',
|
|
49
|
+
title: 'GSD — Get Shit Done',
|
|
50
|
+
desc: 'A Claude-centric workflow augmentation (hooks, commands, planning). Its own tool — phewsh just points you at it; it is not vendored.',
|
|
51
|
+
source: 'github.com/gsd-build/get-shit-done',
|
|
52
|
+
license: 'see the GSD repo',
|
|
53
|
+
install: 'npx @gsd-build/get-shit-done init # follow the GSD repo for current instructions',
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function markers(name) {
|
|
58
|
+
return { start: `<!-- phewsh-pack:${name} START -->`, end: `<!-- phewsh-pack:${name} END -->` };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function blockFor(name, pack) {
|
|
62
|
+
const m = markers(name);
|
|
63
|
+
const header = `# ${pack.title} (phewsh pack: ${name})\n`
|
|
64
|
+
+ `> Source: ${pack.source}\n`
|
|
65
|
+
+ `> Opt-in via \`phewsh pack install ${name}\` · remove: \`phewsh pack remove ${name}\`\n\n`;
|
|
66
|
+
return `${m.start}\n${header}${pack.content}\n${m.end}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isInstalled(name, cwd = process.cwd()) {
|
|
70
|
+
const pack = PACKS[name];
|
|
71
|
+
if (!pack || pack.kind !== 'vendored') return false;
|
|
72
|
+
return pack.targets.some(f => {
|
|
73
|
+
try { return fs.readFileSync(path.join(cwd, f), 'utf-8').includes(markers(name).start); }
|
|
74
|
+
catch { return false; }
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Returns the files it WOULD write to + the block, without writing — for preview.
|
|
79
|
+
function previewInstall(name, cwd = process.cwd()) {
|
|
80
|
+
const pack = PACKS[name];
|
|
81
|
+
if (!pack || pack.kind !== 'vendored') return null;
|
|
82
|
+
return { files: pack.targets, block: blockFor(name, pack) };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function install(name, cwd = process.cwd()) {
|
|
86
|
+
const pack = PACKS[name];
|
|
87
|
+
if (!pack || pack.kind !== 'vendored') return { written: [] };
|
|
88
|
+
const block = blockFor(name, pack);
|
|
89
|
+
const m = markers(name);
|
|
90
|
+
const written = [];
|
|
91
|
+
for (const f of pack.targets) {
|
|
92
|
+
const fp = path.join(cwd, f);
|
|
93
|
+
let existing = '';
|
|
94
|
+
try { existing = fs.readFileSync(fp, 'utf-8'); } catch { /* new file */ }
|
|
95
|
+
if (existing.includes(m.start)) {
|
|
96
|
+
// refresh in place
|
|
97
|
+
const re = new RegExp(escapeRe(m.start) + '[\\s\\S]*?' + escapeRe(m.end));
|
|
98
|
+
fs.writeFileSync(fp, existing.replace(re, block));
|
|
99
|
+
} else {
|
|
100
|
+
fs.writeFileSync(fp, (existing ? existing.replace(/\s*$/, '') + '\n\n' : '') + block + '\n');
|
|
101
|
+
}
|
|
102
|
+
written.push(f);
|
|
103
|
+
}
|
|
104
|
+
return { written };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function remove(name, cwd = process.cwd()) {
|
|
108
|
+
const pack = PACKS[name];
|
|
109
|
+
if (!pack || pack.kind !== 'vendored') return { removed: [] };
|
|
110
|
+
const m = markers(name);
|
|
111
|
+
const removed = [];
|
|
112
|
+
for (const f of pack.targets) {
|
|
113
|
+
const fp = path.join(cwd, f);
|
|
114
|
+
try {
|
|
115
|
+
const existing = fs.readFileSync(fp, 'utf-8');
|
|
116
|
+
if (!existing.includes(m.start)) continue;
|
|
117
|
+
const re = new RegExp('\\n*' + escapeRe(m.start) + '[\\s\\S]*?' + escapeRe(m.end) + '\\n*', 'g');
|
|
118
|
+
fs.writeFileSync(fp, existing.replace(re, '\n').replace(/\n{3,}/g, '\n\n').replace(/^\n+/, ''));
|
|
119
|
+
removed.push(f);
|
|
120
|
+
} catch { /* not there */ }
|
|
121
|
+
}
|
|
122
|
+
return { removed };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function escapeRe(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
|
|
126
|
+
|
|
127
|
+
module.exports = { PACKS, install, remove, isInstalled, previewInstall, blockFor };
|