phewsh 0.15.47 → 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 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')}`);
@@ -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/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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phewsh",
3
- "version": "0.15.47",
3
+ "version": "0.15.48",
4
4
  "description": "Turn intent into action. Structure your thinking, execute your next step.",
5
5
  "bin": {
6
6
  "phewsh": "bin/phewsh.js"