phewsh 0.15.64 → 0.15.66
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/ambient.js +37 -0
- package/commands/gate.js +29 -0
- package/commands/hook.js +53 -0
- package/commands/pack.js +1 -1
- package/lib/gate-policy.js +117 -0
- package/lib/packs.js +105 -1
- package/package.json +1 -1
package/commands/ambient.js
CHANGED
|
@@ -27,6 +27,11 @@ const CLAUDE_SETTINGS = path.join(os.homedir(), '.claude', 'settings.json');
|
|
|
27
27
|
|
|
28
28
|
const HOOK_START = { type: 'command', command: 'phewsh hook session-start' };
|
|
29
29
|
const HOOK_END = { type: 'command', command: 'phewsh hook session-end' };
|
|
30
|
+
// Opt-in Decision Gate enforcement (separate from ambient context sync). The
|
|
31
|
+
// matcher scopes the hook to the tools the policy actually judges, so it never
|
|
32
|
+
// fires on harmless reads.
|
|
33
|
+
const HOOK_PRETOOL = { type: 'command', command: 'phewsh hook pre-tool' };
|
|
34
|
+
const PRETOOL_MATCHER = 'Write|Edit|MultiEdit|NotebookEdit|Bash';
|
|
30
35
|
|
|
31
36
|
// ANSI helpers (256-color per cli/lib/ui.js palette rules)
|
|
32
37
|
const b = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
@@ -97,6 +102,35 @@ function removeClaudeHooks() {
|
|
|
97
102
|
return removed;
|
|
98
103
|
}
|
|
99
104
|
|
|
105
|
+
// ── Decision Gate enforcement (PreToolUse) — opt-in, reversible ──────────────
|
|
106
|
+
function preToolGateApplied() {
|
|
107
|
+
const s = loadClaudeSettings();
|
|
108
|
+
return !!s && hasHook(s, 'PreToolUse', HOOK_PRETOOL.command);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function enablePreToolGate() {
|
|
112
|
+
const settings = loadClaudeSettings() || {};
|
|
113
|
+
settings.hooks = settings.hooks || {};
|
|
114
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse || [];
|
|
115
|
+
if (hasHook(settings, 'PreToolUse', HOOK_PRETOOL.command)) return false;
|
|
116
|
+
settings.hooks.PreToolUse.push({ matcher: PRETOOL_MATCHER, hooks: [HOOK_PRETOOL] });
|
|
117
|
+
fs.writeFileSync(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2));
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function disablePreToolGate() {
|
|
122
|
+
const settings = loadClaudeSettings();
|
|
123
|
+
if (!settings?.hooks?.PreToolUse) return false;
|
|
124
|
+
const before = settings.hooks.PreToolUse.length;
|
|
125
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse
|
|
126
|
+
.map(e => ({ ...e, hooks: (e.hooks || []).filter(h => h.command !== HOOK_PRETOOL.command) }))
|
|
127
|
+
.filter(e => e.hooks.length > 0);
|
|
128
|
+
if ((settings.hooks.PreToolUse || []).length === 0) delete settings.hooks.PreToolUse;
|
|
129
|
+
const changed = before !== (settings.hooks.PreToolUse?.length ?? 0);
|
|
130
|
+
if (changed) fs.writeFileSync(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2));
|
|
131
|
+
return changed;
|
|
132
|
+
}
|
|
133
|
+
|
|
100
134
|
function showConsentScreen(harnesses) {
|
|
101
135
|
console.log('');
|
|
102
136
|
console.log(` ${b(cream('PHEWSH ambient'))} ${sage('— continuity without launching phewsh')}`);
|
|
@@ -365,3 +399,6 @@ async function main() {
|
|
|
365
399
|
|
|
366
400
|
module.exports = main;
|
|
367
401
|
module.exports.ensureAuto = ensureAuto;
|
|
402
|
+
module.exports.enablePreToolGate = enablePreToolGate;
|
|
403
|
+
module.exports.disablePreToolGate = disablePreToolGate;
|
|
404
|
+
module.exports.preToolGateApplied = preToolGateApplied;
|
package/commands/gate.js
CHANGED
|
@@ -373,6 +373,7 @@ async function main() {
|
|
|
373
373
|
if (subcommand === 'update' || subcommand === 'edit') { await update(); return; }
|
|
374
374
|
if (subcommand === 'pause') { pause(); return; }
|
|
375
375
|
if (subcommand === 'resume') { resume(); return; }
|
|
376
|
+
if (subcommand === 'enforce') { enforce(args[1]); return; }
|
|
376
377
|
if (subcommand === 'reset') {
|
|
377
378
|
let removed = false;
|
|
378
379
|
const project = loadProjectJson();
|
|
@@ -391,6 +392,34 @@ async function main() {
|
|
|
391
392
|
console.log(`\n Unknown: ${subcommand}. Run \`phewsh gate --help\`.\n`);
|
|
392
393
|
}
|
|
393
394
|
|
|
395
|
+
// Opt-in deterministic enforcement: register/unregister the Claude Code
|
|
396
|
+
// PreToolUse hook that makes the Decision Gate act BEFORE a tool runs. Reversible,
|
|
397
|
+
// Claude-Code-only for now (it's the one provider with a real veto hook).
|
|
398
|
+
function enforce(action) {
|
|
399
|
+
let amb;
|
|
400
|
+
try { amb = require('./ambient'); } catch { console.log('\n Enforcement unavailable.\n'); return; }
|
|
401
|
+
const on = (action || 'status').toLowerCase();
|
|
402
|
+
if (on === 'on' || on === 'enable') {
|
|
403
|
+
const changed = amb.enablePreToolGate();
|
|
404
|
+
console.log(changed
|
|
405
|
+
? `\n ${green('●')} Gate enforcement ${green('ON')} — Claude Code asks/denies on protected-path writes and high-blast-radius commands before they run.\n ${g('Reversible:')} phewsh gate enforce off\n`
|
|
406
|
+
: `\n Gate enforcement already on.\n`);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (on === 'off' || on === 'disable') {
|
|
410
|
+
const changed = amb.disablePreToolGate();
|
|
411
|
+
console.log(changed ? `\n Gate enforcement ${yellow('OFF')} — PreToolUse hook removed.\n` : `\n Gate enforcement was not on.\n`);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
// status
|
|
415
|
+
const applied = amb.preToolGateApplied();
|
|
416
|
+
console.log(`\n Gate enforcement: ${applied ? green('ON') : g('off')} ${g('(Claude Code PreToolUse)')}`);
|
|
417
|
+
console.log(` ${g('Turn on:')} phewsh gate enforce on ${g('· off:')} phewsh gate enforce off`);
|
|
418
|
+
console.log(` ${g('What it does: deny writes to protected paths (.env, keys, .git/…),')}`);
|
|
419
|
+
console.log(` ${g('ask before high-blast-radius shell (rm -rf, force-push, sudo…).')}`);
|
|
420
|
+
console.log(` ${g('Opt-in, local-only, fail-open. Other tools: advisory only for now.')}\n`);
|
|
421
|
+
}
|
|
422
|
+
|
|
394
423
|
module.exports = main;
|
|
395
424
|
|
|
396
425
|
main().catch(err => {
|
package/commands/hook.js
CHANGED
|
@@ -163,10 +163,63 @@ function sessionEnd() {
|
|
|
163
163
|
setTimeout(() => { appendBreadcrumb('session-end'); process.exit(0); }, 1500);
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
// Load the Decision Gate context (constraints + protected files) for the project
|
|
167
|
+
// being worked in. Best-effort: missing/garbled → empty context (gate allows).
|
|
168
|
+
function loadGateContext(cwd) {
|
|
169
|
+
try {
|
|
170
|
+
const pj = JSON.parse(fs.readFileSync(path.join(cwd, '.intent', 'project.json'), 'utf-8'));
|
|
171
|
+
const dg = pj.decisionGate || {};
|
|
172
|
+
return {
|
|
173
|
+
constraints: dg.constraints || {},
|
|
174
|
+
protectedFiles: dg.protectedFiles || pj.protectedFiles || [],
|
|
175
|
+
};
|
|
176
|
+
} catch { return { constraints: {}, protectedFiles: [] }; }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// PreToolUse adapter — the Decision Gate acting BEFORE a tool runs. Reads Claude
|
|
180
|
+
// Code's PreToolUse JSON from stdin, evaluates against the project's constraints
|
|
181
|
+
// + protected files, and emits a permission decision. FAIL-SAFE: any error, no
|
|
182
|
+
// stdin, or no policy hit → exit 0 (allow). A broken gate must never trap you.
|
|
183
|
+
// It logs only a redacted decision line (never the tool payload).
|
|
184
|
+
function preTool() {
|
|
185
|
+
try {
|
|
186
|
+
if (process.stdin.isTTY) process.exit(0); // not a real hook invocation
|
|
187
|
+
const { evaluateAction, auditLine } = require('../lib/gate-policy');
|
|
188
|
+
let raw = '';
|
|
189
|
+
try { raw = fs.readFileSync(0, 'utf-8'); } catch { process.exit(0); }
|
|
190
|
+
let payload = {};
|
|
191
|
+
try { payload = JSON.parse(raw || '{}'); } catch { process.exit(0); }
|
|
192
|
+
|
|
193
|
+
const cwd = payload.cwd || process.cwd();
|
|
194
|
+
const envelope = {
|
|
195
|
+
toolName: payload.tool_name || payload.toolName,
|
|
196
|
+
toolInput: payload.tool_input || payload.toolInput || {},
|
|
197
|
+
...loadGateContext(cwd),
|
|
198
|
+
};
|
|
199
|
+
let result = { decision: 'allow', reason: '' };
|
|
200
|
+
try { result = evaluateAction(envelope); } catch { process.exit(0); }
|
|
201
|
+
|
|
202
|
+
if (result.decision !== 'allow') {
|
|
203
|
+
try { appendBreadcrumb('pre-tool', { decision: result.decision, action: auditLine(envelope, result) }); } catch { /* audit is best-effort */ }
|
|
204
|
+
process.stdout.write(JSON.stringify({
|
|
205
|
+
hookSpecificOutput: {
|
|
206
|
+
hookEventName: 'PreToolUse',
|
|
207
|
+
permissionDecision: result.decision, // 'deny' | 'ask'
|
|
208
|
+
permissionDecisionReason: result.reason,
|
|
209
|
+
},
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
process.exit(0);
|
|
213
|
+
} catch {
|
|
214
|
+
process.exit(0); // fail open, always
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
166
218
|
function main() {
|
|
167
219
|
const event = process.argv[3];
|
|
168
220
|
if (event === 'session-start') return sessionStart();
|
|
169
221
|
if (event === 'session-end') return sessionEnd();
|
|
222
|
+
if (event === 'pre-tool') return preTool();
|
|
170
223
|
// Unknown event: exit silently — hooks must never error the host tool.
|
|
171
224
|
process.exit(0);
|
|
172
225
|
}
|
package/commands/pack.js
CHANGED
|
@@ -52,7 +52,7 @@ async function install(name) {
|
|
|
52
52
|
console.log('');
|
|
53
53
|
|
|
54
54
|
if (p.kind === 'linked') {
|
|
55
|
-
console.log(` ${sage('This is a separate tool — phewsh doesn\'t vendor it.
|
|
55
|
+
console.log(` ${sage('This is a separate tool — phewsh doesn\'t vendor it. Start here:')}`);
|
|
56
56
|
console.log(` ${cream(p.install)}`);
|
|
57
57
|
console.log('');
|
|
58
58
|
return;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Deterministic pre-action policy — the enforcement core of the Decision Gate.
|
|
4
|
+
//
|
|
5
|
+
// Pure and provider-neutral: given an action envelope it returns a decision.
|
|
6
|
+
// No I/O, no provider coupling — the hook adapter (commands/hook.js pre-tool)
|
|
7
|
+
// feeds it the action and renders the provider-specific response. This is the
|
|
8
|
+
// "deterministic enforcement" layer from cli/docs/pre-action-architecture.md;
|
|
9
|
+
// it does NOT replace the Decision Gate, it is how the gate acts before a tool
|
|
10
|
+
// runs. Decisions: 'allow' | 'ask' (require human) | 'deny' (block).
|
|
11
|
+
//
|
|
12
|
+
// Design rules: fail OPEN (an empty/garbled envelope returns allow — a policy
|
|
13
|
+
// must never trap the user), and never inspect file CONTENTS (we judge the
|
|
14
|
+
// action's target/shape, not your code or secrets).
|
|
15
|
+
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
// Tools that write/modify the filesystem — common names across providers.
|
|
19
|
+
const WRITE_TOOLS = new Set([
|
|
20
|
+
'Write', 'Edit', 'MultiEdit', 'NotebookEdit',
|
|
21
|
+
'create_file', 'apply_patch', 'str_replace_editor', 'edit_file',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
// Shell/command tools.
|
|
25
|
+
const SHELL_TOOLS = new Set(['Bash', 'shell', 'run_command', 'run_terminal_cmd']);
|
|
26
|
+
|
|
27
|
+
// Paths an agent should never silently write — credentials, keys, VCS internals.
|
|
28
|
+
const DEFAULT_PROTECTED = [
|
|
29
|
+
'.env', '.env.*', '*.pem', '*.key', 'id_rsa', 'id_ed25519',
|
|
30
|
+
'.git/', '.npmrc', '.aws/', '.ssh/', '.phewsh/config.json',
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
// High-blast-radius shell patterns that warrant a human OK.
|
|
34
|
+
const DESTRUCTIVE = [
|
|
35
|
+
{ re: /\brm\s+-[a-z]*r[a-z]*f|\brm\s+-[a-z]*f[a-z]*r/, label: 'recursive force delete' },
|
|
36
|
+
{ re: /\bgit\s+push\b[^\n]*--force/, label: 'git force-push' },
|
|
37
|
+
{ re: /\bgit\s+reset\s+--hard\b/, label: 'git hard reset' },
|
|
38
|
+
{ re: /\bDROP\s+(TABLE|DATABASE)\b/i, label: 'SQL DROP' },
|
|
39
|
+
{ re: /\bsudo\b/, label: 'sudo' },
|
|
40
|
+
{ re: /\bchmod\s+-R\b/, label: 'recursive chmod' },
|
|
41
|
+
{ re: /\bmkfs\b|\bdd\s+if=/, label: 'disk write' },
|
|
42
|
+
{ re: /\bcurl\b[^\n]*\|\s*(sh|bash)\b|\bwget\b[^\n]*\|\s*(sh|bash)\b/, label: 'pipe-to-shell' },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
function globToRegExp(glob) {
|
|
46
|
+
const esc = glob.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
|
|
47
|
+
return new RegExp('^' + esc + '$');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Does `target` match any protected entry? Directory entries (trailing '/')
|
|
51
|
+
// match anywhere in the path; file globs match the basename or full path.
|
|
52
|
+
function isProtected(target, protectedList) {
|
|
53
|
+
if (!target || typeof target !== 'string') return false;
|
|
54
|
+
const norm = target.replace(/^\.\//, '');
|
|
55
|
+
const base = path.basename(norm);
|
|
56
|
+
return protectedList.some(p => {
|
|
57
|
+
if (p.endsWith('/')) return norm.split('/').includes(p.slice(0, -1)) || norm.startsWith(p);
|
|
58
|
+
const re = globToRegExp(p);
|
|
59
|
+
return re.test(base) || re.test(norm);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function writeTarget(toolInput = {}) {
|
|
64
|
+
return toolInput.file_path || toolInput.path || toolInput.notebook_path
|
|
65
|
+
|| toolInput.filename || toolInput.target_file || null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// envelope: { toolName, toolInput, constraints, protectedFiles }
|
|
69
|
+
function evaluateAction(envelope = {}) {
|
|
70
|
+
try {
|
|
71
|
+
const { toolName, toolInput = {}, constraints = {}, protectedFiles = [] } = envelope;
|
|
72
|
+
if (!toolName) return { decision: 'allow', reason: '' };
|
|
73
|
+
const protectedList = [...DEFAULT_PROTECTED, ...(Array.isArray(protectedFiles) ? protectedFiles : [])];
|
|
74
|
+
|
|
75
|
+
// 1. Writes to a protected path → deny (only a deliberate human act should).
|
|
76
|
+
if (WRITE_TOOLS.has(toolName)) {
|
|
77
|
+
const target = writeTarget(toolInput);
|
|
78
|
+
if (isProtected(target, protectedList)) {
|
|
79
|
+
return { decision: 'deny', reason: `phewsh gate: ${toolName} targets a protected path (${target}). Credentials/keys/VCS internals are off-limits to automated edits.` };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 2. High-blast-radius shell → ask (require human confirmation).
|
|
84
|
+
if (SHELL_TOOLS.has(toolName)) {
|
|
85
|
+
const cmd = String(toolInput.command || toolInput.cmd || '');
|
|
86
|
+
const hit = DESTRUCTIVE.find(d => d.re.test(cmd));
|
|
87
|
+
if (hit) {
|
|
88
|
+
return { decision: 'ask', reason: `phewsh gate: high-blast-radius command (${hit.label}) — confirm before it runs.` };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 3. Strict autonomy asks before any write. "delegated"/"guided" do not —
|
|
93
|
+
// that would turn every edit into ceremony.
|
|
94
|
+
const autonomy = String(constraints.autonomy || '').toLowerCase();
|
|
95
|
+
if ((autonomy === 'manual' || autonomy === 'review') && WRITE_TOOLS.has(toolName)) {
|
|
96
|
+
return { decision: 'ask', reason: `phewsh gate: your autonomy is "${autonomy}" — confirm this ${toolName} before it runs.` };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { decision: 'allow', reason: '' };
|
|
100
|
+
} catch {
|
|
101
|
+
return { decision: 'allow', reason: '' }; // fail OPEN: never trap the user
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// A redacted one-line audit record — decision + target shape only, never the
|
|
106
|
+
// tool payload (which may contain code or secrets).
|
|
107
|
+
function auditLine(envelope = {}, result = {}) {
|
|
108
|
+
const target = WRITE_TOOLS.has(envelope.toolName)
|
|
109
|
+
? writeTarget(envelope.toolInput || {})
|
|
110
|
+
: (SHELL_TOOLS.has(envelope.toolName) ? '<command>' : '');
|
|
111
|
+
return `${result.decision || 'allow'} ${envelope.toolName || '?'}${target ? ' ' + target : ''}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
evaluateAction, isProtected, auditLine, writeTarget,
|
|
116
|
+
WRITE_TOOLS, SHELL_TOOLS, DEFAULT_PROTECTED, DESTRUCTIVE,
|
|
117
|
+
};
|
package/lib/packs.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// • vendored — phewsh writes a clearly-marked block into the project's agent
|
|
13
13
|
// files (e.g. Karpathy's coding guidelines into CLAUDE.md/AGENTS.md).
|
|
14
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).
|
|
15
|
+
// installer or review page (e.g. GSD is its own tool — we hand you its command).
|
|
16
16
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
@@ -52,6 +52,110 @@ const PACKS = {
|
|
|
52
52
|
license: 'see the GSD repo',
|
|
53
53
|
install: 'npx @gsd-build/get-shit-done init # follow the GSD repo for current instructions',
|
|
54
54
|
},
|
|
55
|
+
'loop-library': {
|
|
56
|
+
kind: 'linked',
|
|
57
|
+
title: 'Loop Library',
|
|
58
|
+
desc: 'Copy-ready agent loops with checks and stopping conditions. Good Work-layer inspiration; phewsh links to it without becoming a scheduler or runner.',
|
|
59
|
+
source: 'github.com/Forward-Future/loop-library · signals.forwardfuture.ai/loop-library',
|
|
60
|
+
license: 'MIT',
|
|
61
|
+
install: 'npx skills add Forward-Future/loop-library --skill loop-library -g # install through upstream skills tooling',
|
|
62
|
+
},
|
|
63
|
+
'matt-skills': {
|
|
64
|
+
kind: 'linked',
|
|
65
|
+
title: 'Matt Pocock Skills',
|
|
66
|
+
desc: 'Engineering-focused skills for debugging, TDD, domain modeling, handoffs, git guardrails, and design reviews.',
|
|
67
|
+
source: 'github.com/mattpocock/skills',
|
|
68
|
+
license: 'MIT',
|
|
69
|
+
install: 'npx skills@latest add mattpocock/skills # install through upstream skills tooling',
|
|
70
|
+
},
|
|
71
|
+
'cybersecurity-skills': {
|
|
72
|
+
kind: 'linked',
|
|
73
|
+
title: 'Anthropic Cybersecurity Skills',
|
|
74
|
+
desc: 'Large cybersecurity skill library mapped to security frameworks. Review scope carefully before using on real targets.',
|
|
75
|
+
source: 'github.com/mukul975/Anthropic-Cybersecurity-Skills',
|
|
76
|
+
license: 'Apache-2.0',
|
|
77
|
+
install: 'review https://github.com/mukul975/Anthropic-Cybersecurity-Skills # choose upstream install path and scope deliberately',
|
|
78
|
+
},
|
|
79
|
+
skillspector: {
|
|
80
|
+
kind: 'linked',
|
|
81
|
+
title: 'NVIDIA SkillSpector',
|
|
82
|
+
desc: 'Security scanner for AI-agent skills. Useful candidate for future Phewsh pack vetting before install or distribution.',
|
|
83
|
+
source: 'github.com/nvidia/skillspector',
|
|
84
|
+
license: 'see the SkillSpector repo',
|
|
85
|
+
install: 'review https://github.com/nvidia/skillspector # run scanner only after reading upstream docs',
|
|
86
|
+
},
|
|
87
|
+
'codebase-memory-mcp': {
|
|
88
|
+
kind: 'linked',
|
|
89
|
+
title: 'Codebase Memory MCP',
|
|
90
|
+
desc: 'Persistent code-intelligence MCP server for repository memory and fast code search. Linked only; configure per harness.',
|
|
91
|
+
source: 'github.com/DeusData/codebase-memory-mcp',
|
|
92
|
+
license: 'see the codebase-memory-mcp repo',
|
|
93
|
+
install: 'review https://github.com/DeusData/codebase-memory-mcp # install/configure upstream MCP manually',
|
|
94
|
+
},
|
|
95
|
+
'unlimited-ocr': {
|
|
96
|
+
kind: 'linked',
|
|
97
|
+
title: 'Unlimited OCR',
|
|
98
|
+
desc: 'Local/GPU OCR model candidate for images and PDFs. Relevant to RecipeFlower-style recipe capture, but heavy and not auto-installed.',
|
|
99
|
+
source: 'github.com/baidu/Unlimited-OCR',
|
|
100
|
+
license: 'MIT',
|
|
101
|
+
install: 'review https://github.com/baidu/Unlimited-OCR # inspect CUDA/SGLang requirements before installing',
|
|
102
|
+
},
|
|
103
|
+
'palmier-pro': {
|
|
104
|
+
kind: 'linked',
|
|
105
|
+
title: 'Palmier Pro',
|
|
106
|
+
desc: 'Open-source macOS video editor with an MCP surface. Useful media-production inspiration; platform-gated and not bundled.',
|
|
107
|
+
source: 'github.com/palmier-io/palmier-pro',
|
|
108
|
+
license: 'GPLv3',
|
|
109
|
+
install: 'review https://github.com/palmier-io/palmier-pro # macOS/Apple Silicon requirements apply',
|
|
110
|
+
},
|
|
111
|
+
openmontage: {
|
|
112
|
+
kind: 'linked',
|
|
113
|
+
title: 'OpenMontage',
|
|
114
|
+
desc: 'Agentic video-production system with pipelines, tools, and skills. Treat as an upstream media lab, not a core Phewsh dependency.',
|
|
115
|
+
source: 'github.com/calesthio/OpenMontage',
|
|
116
|
+
license: 'see the OpenMontage repo',
|
|
117
|
+
install: 'review https://github.com/calesthio/OpenMontage # install only in a project that needs agentic video production',
|
|
118
|
+
},
|
|
119
|
+
hyperframes: {
|
|
120
|
+
kind: 'linked',
|
|
121
|
+
title: 'Hyperframes',
|
|
122
|
+
desc: 'HTML-to-video rendering for agents. Good fit for future product demos, walkthroughs, and generated media packs.',
|
|
123
|
+
source: 'github.com/heygen-com/hyperframes',
|
|
124
|
+
license: 'see the Hyperframes repo',
|
|
125
|
+
install: 'review https://github.com/heygen-com/hyperframes # inspect current renderer/runtime requirements',
|
|
126
|
+
},
|
|
127
|
+
'deer-flow': {
|
|
128
|
+
kind: 'linked',
|
|
129
|
+
title: 'DeerFlow',
|
|
130
|
+
desc: 'Open-source agent harness with sub-agents, memory, sandboxes, and skills. Compare as an executor/harness, not a Phewsh replacement.',
|
|
131
|
+
source: 'github.com/bytedance/deer-flow',
|
|
132
|
+
license: 'MIT',
|
|
133
|
+
install: 'review https://github.com/bytedance/deer-flow # evaluate as a separate harness before configuring',
|
|
134
|
+
},
|
|
135
|
+
'hermes-agent': {
|
|
136
|
+
kind: 'linked',
|
|
137
|
+
title: 'Hermes Agent',
|
|
138
|
+
desc: 'Nous Research agent project. Linked as a possible compatible harness to evaluate before any deeper integration.',
|
|
139
|
+
source: 'github.com/nousresearch/hermes-agent',
|
|
140
|
+
license: 'see the Hermes Agent repo',
|
|
141
|
+
install: 'review https://github.com/nousresearch/hermes-agent # evaluate upstream capabilities first',
|
|
142
|
+
},
|
|
143
|
+
voicebox: {
|
|
144
|
+
kind: 'linked',
|
|
145
|
+
title: 'Voicebox',
|
|
146
|
+
desc: 'Open-source AI voice studio. Candidate for future audio/voice workflows and Keylink-adjacent media experiments.',
|
|
147
|
+
source: 'github.com/jamiepine/voicebox',
|
|
148
|
+
license: 'see the Voicebox repo',
|
|
149
|
+
install: 'review https://github.com/jamiepine/voicebox # install only for voice workflows that need it',
|
|
150
|
+
},
|
|
151
|
+
gstack: {
|
|
152
|
+
kind: 'linked',
|
|
153
|
+
title: 'gstack',
|
|
154
|
+
desc: 'Opinionated Claude Code setup with role/tooling patterns. Useful reference, but Phewsh should preserve its own four-word model.',
|
|
155
|
+
source: 'github.com/garrytan/gstack',
|
|
156
|
+
license: 'see the gstack repo',
|
|
157
|
+
install: 'review https://github.com/garrytan/gstack # inspect before mixing with existing agent files',
|
|
158
|
+
},
|
|
55
159
|
};
|
|
56
160
|
|
|
57
161
|
function markers(name) {
|