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 CHANGED
@@ -5,48 +5,77 @@ const command = args[0];
5
5
 
6
6
  const COMMANDS = {
7
7
  intent: () => require('../commands/intent'),
8
- music: () => require('../commands/music'),
9
- sap: () => require('../commands/sap'),
10
- help: () => showHelp(),
11
- version: () => console.log(`phewsh v${require('../package.json').version}`),
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 — turn intent into action
23
+ 😮‍💨🤫 phewsh v${pkg.version}
17
24
 
18
25
  Usage:
19
26
  phewsh <command> [options]
20
27
 
21
28
  Commands:
22
- intent Capture structured intent generate vision, plan, and next actions
23
- music Open the MBHD music engine
24
- sap View Sustainable AI Protocol usage stats
25
- help Show this help message
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 intent Start a new intent capture session
30
- phewsh intent --evolve Evolve existing .intent/ artifacts
31
- phewsh intent --open Open phewsh.com/intent in your browser
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
- COMMANDS.version();
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(` Unknown command: ${command}\n Run 'phewsh help' for available commands.`);
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.0",
3
+ "version": "0.2.1",
4
4
  "description": "Turn intent into action. Structure your thinking, execute your next step.",
5
5
  "bin": {
6
- "phewsh": "./bin/phewsh.js"
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": {