phewsh 0.1.0 → 0.2.1
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 +43 -14
- package/commands/ai.js +149 -0
- package/commands/login.js +104 -0
- package/package.json +3 -3
package/bin/phewsh.js
CHANGED
|
@@ -5,48 +5,77 @@ const command = args[0];
|
|
|
5
5
|
|
|
6
6
|
const COMMANDS = {
|
|
7
7
|
intent: () => require('../commands/intent'),
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
login: () => require('../commands/login'),
|
|
9
|
+
ai: () => require('../commands/ai'),
|
|
10
|
+
music: () => require('../commands/music'),
|
|
11
|
+
sap: () => require('../commands/sap'),
|
|
12
|
+
help: showHelp,
|
|
13
|
+
version: showVersion,
|
|
12
14
|
};
|
|
13
15
|
|
|
16
|
+
function showVersion() {
|
|
17
|
+
console.log(`phewsh v${require('../package.json').version}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
function showHelp() {
|
|
21
|
+
const pkg = require('../package.json');
|
|
15
22
|
console.log(`
|
|
16
|
-
😮💨🤫 phewsh
|
|
23
|
+
😮💨🤫 phewsh v${pkg.version}
|
|
17
24
|
|
|
18
25
|
Usage:
|
|
19
26
|
phewsh <command> [options]
|
|
20
27
|
|
|
21
28
|
Commands:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
login Set up your local identity and API key
|
|
30
|
+
ai Run context-aware AI prompts (reads .intent/)
|
|
31
|
+
intent Structure your thinking into vision, plan, and next actions
|
|
32
|
+
sap Sustainable AI Protocol — usage and accountability
|
|
33
|
+
music MBHD music engine
|
|
26
34
|
version Show version
|
|
27
35
|
|
|
28
36
|
Quick start:
|
|
29
|
-
phewsh
|
|
30
|
-
phewsh intent --
|
|
31
|
-
phewsh
|
|
37
|
+
phewsh login Set up identity and API key
|
|
38
|
+
phewsh intent --init Create .intent/ in any project
|
|
39
|
+
phewsh ai run "what's next?" AI with your project context
|
|
40
|
+
phewsh ai run "write a release checklist"
|
|
32
41
|
|
|
33
42
|
Learn more: https://phewsh.com
|
|
34
43
|
`);
|
|
35
44
|
}
|
|
36
45
|
|
|
46
|
+
// Non-blocking update check
|
|
47
|
+
function checkForUpdates() {
|
|
48
|
+
const pkg = require('../package.json');
|
|
49
|
+
fetch(`https://registry.npmjs.org/${pkg.name}/latest`, { signal: AbortSignal.timeout(3000) })
|
|
50
|
+
.then(r => r.json())
|
|
51
|
+
.then(data => {
|
|
52
|
+
if (data.version && data.version !== pkg.version) {
|
|
53
|
+
const newer = data.version.split('.').map(Number);
|
|
54
|
+
const current = pkg.version.split('.').map(Number);
|
|
55
|
+
const isNewer = newer[0] > current[0] || newer[1] > current[1] || newer[2] > current[2];
|
|
56
|
+
if (isNewer) {
|
|
57
|
+
console.log(`\n Update available: ${pkg.version} → ${data.version}`);
|
|
58
|
+
console.log(` Run: npm install -g phewsh\n`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
.catch(() => {}); // silently ignore — never block execution
|
|
63
|
+
}
|
|
64
|
+
|
|
37
65
|
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
38
66
|
showHelp();
|
|
39
67
|
process.exit(0);
|
|
40
68
|
}
|
|
41
69
|
|
|
42
70
|
if (command === 'version' || command === '--version' || command === '-v') {
|
|
43
|
-
|
|
71
|
+
showVersion();
|
|
44
72
|
process.exit(0);
|
|
45
73
|
}
|
|
46
74
|
|
|
47
75
|
if (COMMANDS[command]) {
|
|
76
|
+
checkForUpdates();
|
|
48
77
|
COMMANDS[command]();
|
|
49
78
|
} else {
|
|
50
|
-
console.error(
|
|
79
|
+
console.error(`\n Unknown command: ${command}\n Run 'phewsh help' for available commands.\n`);
|
|
51
80
|
process.exit(1);
|
|
52
81
|
}
|
package/commands/ai.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
const CONFIG_PATH = path.join(os.homedir(), '.phewsh', 'config.json');
|
|
6
|
+
const INTENT_DIR = path.join(process.cwd(), '.intent');
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(3);
|
|
9
|
+
const subcommand = args[0];
|
|
10
|
+
|
|
11
|
+
function loadConfig() {
|
|
12
|
+
if (!fs.existsSync(CONFIG_PATH)) return null;
|
|
13
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); } catch { return null; }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function loadIntentContext() {
|
|
17
|
+
const files = ['vision.md', 'plan.md', 'next.md'];
|
|
18
|
+
const loaded = [];
|
|
19
|
+
for (const file of files) {
|
|
20
|
+
const p = path.join(INTENT_DIR, file);
|
|
21
|
+
if (fs.existsSync(p)) {
|
|
22
|
+
loaded.push({ file, content: fs.readFileSync(p, 'utf-8') });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return loaded;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildSystemPrompt(intentFiles) {
|
|
29
|
+
if (intentFiles.length === 0) return null;
|
|
30
|
+
|
|
31
|
+
const sections = intentFiles.map(({ file, content }) =>
|
|
32
|
+
`## ${file}\n\n${content.trim()}`
|
|
33
|
+
).join('\n\n---\n\n');
|
|
34
|
+
|
|
35
|
+
return `You are a focused execution assistant. The user has structured intent artifacts that define what they are building. Use these as your primary context for every response — stay aligned with their vision, plan, and next actions.\n\n${sections}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function streamResponse(apiKey, systemPrompt, userPrompt) {
|
|
39
|
+
const messages = [{ role: 'user', content: userPrompt }];
|
|
40
|
+
const body = {
|
|
41
|
+
model: 'claude-sonnet-4-6',
|
|
42
|
+
max_tokens: 1024,
|
|
43
|
+
messages,
|
|
44
|
+
stream: true,
|
|
45
|
+
};
|
|
46
|
+
if (systemPrompt) body.system = systemPrompt;
|
|
47
|
+
|
|
48
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
headers: {
|
|
51
|
+
'x-api-key': apiKey,
|
|
52
|
+
'anthropic-version': '2023-06-01',
|
|
53
|
+
'content-type': 'application/json',
|
|
54
|
+
},
|
|
55
|
+
body: JSON.stringify(body),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
const err = await response.json().catch(() => ({}));
|
|
60
|
+
throw new Error(err.error?.message || `API error ${response.status}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
process.stdout.write('\n');
|
|
64
|
+
|
|
65
|
+
for await (const chunk of response.body) {
|
|
66
|
+
const text = Buffer.from(chunk).toString('utf-8');
|
|
67
|
+
const lines = text.split('\n').filter(l => l.startsWith('data: '));
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
const data = line.slice(6);
|
|
70
|
+
if (data === '[DONE]') continue;
|
|
71
|
+
try {
|
|
72
|
+
const parsed = JSON.parse(data);
|
|
73
|
+
if (parsed.type === 'content_block_delta' && parsed.delta?.text) {
|
|
74
|
+
process.stdout.write(parsed.delta.text);
|
|
75
|
+
}
|
|
76
|
+
} catch { /* skip malformed chunks */ }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
process.stdout.write('\n\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function main() {
|
|
84
|
+
if (!subcommand || subcommand === '--help' || subcommand === '-h') {
|
|
85
|
+
console.log(`
|
|
86
|
+
😮💨🤫 phewsh ai
|
|
87
|
+
|
|
88
|
+
Usage:
|
|
89
|
+
phewsh ai run "<prompt>" Run a prompt with .intent/ context injected
|
|
90
|
+
phewsh ai status Show current config and context
|
|
91
|
+
|
|
92
|
+
Examples:
|
|
93
|
+
phewsh ai run "what should I build next?"
|
|
94
|
+
phewsh ai run "write a release checklist for the current plan"
|
|
95
|
+
phewsh ai run "summarize where I am and what's blocked"
|
|
96
|
+
`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (subcommand === 'status') {
|
|
101
|
+
const config = loadConfig();
|
|
102
|
+
const intentFiles = loadIntentContext();
|
|
103
|
+
console.log('\n phewsh ai — status\n');
|
|
104
|
+
console.log(` Config ${config ? '✓ found' : '✗ not found — run `phewsh login`'}`);
|
|
105
|
+
console.log(` API key ${config?.apiKey ? '✓ set' : '✗ not set'}`);
|
|
106
|
+
console.log(` Provider ${config?.defaultProvider || 'anthropic'}`);
|
|
107
|
+
console.log(` .intent/ ${intentFiles.length > 0 ? `✓ ${intentFiles.map(f => f.file).join(', ')}` : '✗ none found'}`);
|
|
108
|
+
console.log('');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (subcommand === 'run') {
|
|
113
|
+
const prompt = args.slice(1).join(' ');
|
|
114
|
+
if (!prompt) {
|
|
115
|
+
console.error('\n Usage: phewsh ai run "<your prompt>"\n');
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const config = loadConfig();
|
|
120
|
+
if (!config) {
|
|
121
|
+
console.error('\n Not logged in. Run `phewsh login` first.\n');
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
if (!config.apiKey) {
|
|
125
|
+
console.error('\n No API key set. Run `phewsh login --set-key` to add one.\n');
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const intentFiles = loadIntentContext();
|
|
130
|
+
const systemPrompt = buildSystemPrompt(intentFiles);
|
|
131
|
+
|
|
132
|
+
if (intentFiles.length > 0) {
|
|
133
|
+
console.log(`\n Context: ${intentFiles.map(f => f.file).join(', ')}`);
|
|
134
|
+
} else {
|
|
135
|
+
console.log('\n No .intent/ found — running without project context');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await streamResponse(config.apiKey, systemPrompt, prompt);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.error(`\n Unknown subcommand: ${subcommand}\n Run 'phewsh ai --help' for usage.\n`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
main().catch(err => {
|
|
147
|
+
console.error('\n Error:', err.message);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
|
|
7
|
+
const CONFIG_DIR = path.join(os.homedir(), '.phewsh');
|
|
8
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
|
|
9
|
+
|
|
10
|
+
function loadConfig() {
|
|
11
|
+
if (!fs.existsSync(CONFIG_PATH)) return null;
|
|
12
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); } catch { return null; }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function saveConfig(config) {
|
|
16
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
17
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createPrompter() {
|
|
21
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
22
|
+
const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
|
|
23
|
+
const close = () => rl.close();
|
|
24
|
+
return { ask, close };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function main() {
|
|
28
|
+
const args = process.argv.slice(3);
|
|
29
|
+
|
|
30
|
+
if (args.includes('--status') || args.includes('-s')) {
|
|
31
|
+
const config = loadConfig();
|
|
32
|
+
if (!config) {
|
|
33
|
+
console.log('\n Not logged in. Run `phewsh login` to get started.\n');
|
|
34
|
+
} else {
|
|
35
|
+
console.log('\n Logged in\n');
|
|
36
|
+
console.log(` Email ${config.email || '(not set)'}`);
|
|
37
|
+
console.log(` User ID ${config.userId}`);
|
|
38
|
+
console.log(` Provider ${config.defaultProvider || 'anthropic'}`);
|
|
39
|
+
console.log(` API key ${config.apiKey ? config.apiKey.slice(0, 8) + '...' : '(not set)'}`);
|
|
40
|
+
console.log('');
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (args.includes('--logout')) {
|
|
46
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
47
|
+
fs.unlinkSync(CONFIG_PATH);
|
|
48
|
+
console.log('\n Logged out. Config removed.\n');
|
|
49
|
+
} else {
|
|
50
|
+
console.log('\n Not logged in.\n');
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const existing = loadConfig();
|
|
56
|
+
if (existing) {
|
|
57
|
+
console.log(`\n Already logged in as ${existing.email || existing.userId}`);
|
|
58
|
+
console.log(' Run `phewsh login --logout` to reset, or `phewsh login --status` to view.\n');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log('\n 😮💨🤫 phewsh login\n');
|
|
63
|
+
console.log(' Set up your local Phewsh identity.\n');
|
|
64
|
+
|
|
65
|
+
const { ask, close } = createPrompter();
|
|
66
|
+
|
|
67
|
+
const email = await ask(' Email address\n > ');
|
|
68
|
+
console.log('');
|
|
69
|
+
|
|
70
|
+
console.log(' To use `phewsh ai`, you need an Anthropic API key.');
|
|
71
|
+
console.log(' Get one at console.anthropic.com/settings/keys');
|
|
72
|
+
console.log(' (Leave blank to skip for now)\n');
|
|
73
|
+
const apiKey = await ask(' Anthropic API key\n > ');
|
|
74
|
+
console.log('');
|
|
75
|
+
|
|
76
|
+
close();
|
|
77
|
+
|
|
78
|
+
const userId = crypto.randomBytes(12).toString('hex');
|
|
79
|
+
|
|
80
|
+
const config = {
|
|
81
|
+
userId,
|
|
82
|
+
email: email || undefined,
|
|
83
|
+
apiKey: apiKey || undefined,
|
|
84
|
+
defaultProvider: 'anthropic',
|
|
85
|
+
createdAt: new Date().toISOString(),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Clean up undefined fields
|
|
89
|
+
Object.keys(config).forEach(k => config[k] === undefined && delete config[k]);
|
|
90
|
+
|
|
91
|
+
saveConfig(config);
|
|
92
|
+
|
|
93
|
+
console.log(` Logged in as ${email || userId}`);
|
|
94
|
+
if (apiKey) {
|
|
95
|
+
console.log(' API key saved. Run `phewsh ai run "..."` to start.\n');
|
|
96
|
+
} else {
|
|
97
|
+
console.log(' No API key set. Add one later with `phewsh login --set-key`.\n');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
main().catch(err => {
|
|
102
|
+
console.error('\n Error:', err.message);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
});
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phewsh",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Turn intent into action. Structure your thinking, execute your next step.",
|
|
5
5
|
"bin": {
|
|
6
|
-
"phewsh": "
|
|
6
|
+
"phewsh": "bin/phewsh.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"bin/",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
|
-
"url": "https://github.com/cleverIdeaz/phewsh"
|
|
28
|
+
"url": "git+https://github.com/cleverIdeaz/phewsh.git"
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://phewsh.com",
|
|
31
31
|
"engines": {
|