phewsh 0.11.1 → 0.11.8

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
@@ -47,7 +47,10 @@ const COMMANDS = {
47
47
  ai: () => require('../commands/ai'),
48
48
  style: () => require('../commands/style'),
49
49
  mbhd: () => require('../commands/mbhd'),
50
+ context: () => require('../commands/context'),
51
+ gate: () => require('../commands/gate'),
50
52
  sap: () => require('../commands/sap'),
53
+ watch: () => require('../commands/watch')(),
51
54
  mcp: () => require('../commands/mcp')(),
52
55
  serve: () => require('../commands/serve')(),
53
56
  help: showHelp,
@@ -73,7 +76,10 @@ function showHelp() {
73
76
  console.log(` ${w('link')} Link local .intent/ to a cloud project`);
74
77
  console.log(` ${w('intent')} Manage .intent/ artifacts — status, open, evolve`);
75
78
  console.log(` ${w('ai')} One-shot AI prompt (reads .intent/)`);
79
+ console.log(` ${w('gate')} Declare operational constraints (budget, time, skill)`);
80
+ console.log(` ${w('context')} Export portable context for any AI tool`);
76
81
  console.log(` ${w('login')} Set up identity, API key, and cloud sync`);
82
+ console.log(` ${w('watch')} Live sync — .intent/ changes push to cloud + CLAUDE.md`);
77
83
  console.log(` ${w('serve')} Start live execution bridge for the web app`);
78
84
  console.log(` ${w('mcp')} Connect AI agents — setup, sync, status`);
79
85
  console.log(` ${w('sap')} Sustainable AI Protocol — usage and accountability`);
@@ -0,0 +1,359 @@
1
+ // phewsh context
2
+ // Export portable adaptive context for any AI tool.
3
+ // Reads .intent/ artifacts + gate.json and generates an operational briefing.
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const INTENT_DIR = path.join(process.cwd(), '.intent');
9
+
10
+ const args = process.argv.slice(3);
11
+ const flags = {
12
+ full: args.includes('--full') || args.includes('-f'),
13
+ claude: args.includes('--claude'),
14
+ clipboard: args.includes('--copy') || args.includes('-c'),
15
+ file: args.includes('--file') || args.includes('-o'),
16
+ help: args.includes('--help') || args.includes('-h'),
17
+ };
18
+
19
+ function loadArtifact(name) {
20
+ const p = path.join(INTENT_DIR, name);
21
+ if (!fs.existsSync(p)) return null;
22
+ return fs.readFileSync(p, 'utf-8');
23
+ }
24
+
25
+ function loadGate() {
26
+ const p = path.join(INTENT_DIR, 'gate.json');
27
+ if (!fs.existsSync(p)) return null;
28
+ try { return JSON.parse(fs.readFileSync(p, 'utf-8')); } catch { return null; }
29
+ }
30
+
31
+ function extractExecutionState(nextContent) {
32
+ const items = [];
33
+ const stateMap = { ' ': 'todo', '-': 'in_progress', 'x': 'done', '!': 'blocked', '~': 'skipped' };
34
+
35
+ for (const line of nextContent.split('\n')) {
36
+ const match = line.match(/^[-*]\s*\[([ x!\-~])\]\s*\*?\*?(.+?)\*?\*?\s*$/);
37
+ if (match) {
38
+ const char = match[1] === '-' ? '-' : match[1];
39
+ items.push({ title: match[2].replace(/\*\*/g, '').trim(), state: stateMap[char] || 'todo' });
40
+ }
41
+ }
42
+
43
+ return {
44
+ total: items.length,
45
+ done: items.filter(i => i.state === 'done').length,
46
+ doing: items.filter(i => i.state === 'in_progress').length,
47
+ blocked: items.filter(i => i.state === 'blocked').length,
48
+ skipped: items.filter(i => i.state === 'skipped').length,
49
+ items,
50
+ };
51
+ }
52
+
53
+ function buildConstraintInstructions(c) {
54
+ const lines = [];
55
+
56
+ if (c.budget > 0) {
57
+ lines.push(`- Budget: $${c.budget} total. ${
58
+ c.budget < 100 ? 'Extremely tight — free/open-source only.'
59
+ : c.budget < 500 ? 'Limited — justify any paid tool.'
60
+ : c.budget < 2000 ? 'Moderate — strategic spending OK.'
61
+ : 'Substantial — professional tools welcome.'
62
+ }`);
63
+ }
64
+
65
+ if (c.timeHoursPerWeek > 0) {
66
+ lines.push(`- Time: ${c.timeHoursPerWeek} hrs/week. ${
67
+ c.timeHoursPerWeek <= 5 ? 'Micro-steps only. Each task < 2 hours.'
68
+ : c.timeHoursPerWeek <= 15 ? 'Part-time. Clear stopping points.'
69
+ : c.timeHoursPerWeek <= 30 ? 'Near full-time. Sustained work OK.'
70
+ : 'Full-time+. Ambitious scope OK.'
71
+ }`);
72
+ }
73
+
74
+ lines.push(`- Skill: ${c.skillLevel}. ${
75
+ c.skillLevel === 'beginner' ? 'Explain everything. Simplest architecture wins.'
76
+ : c.skillLevel === 'intermediate' ? 'Reference docs, flag gotchas.'
77
+ : c.skillLevel === 'advanced' ? 'Focus on strategic decisions.'
78
+ : 'Deep expertise. Advanced patterns welcome.'
79
+ }`);
80
+
81
+ lines.push(`- Urgency: ${c.urgency}. ${
82
+ c.urgency === 'relaxed' ? 'Thoroughness over speed.'
83
+ : c.urgency === 'moderate' ? 'Weeks not months. Proven approaches.'
84
+ : c.urgency === 'urgent' ? 'Days matter. Cut scope aggressively.'
85
+ : 'BLOCKING. Strip to essentials.'
86
+ }`);
87
+
88
+ lines.push(`- Autonomy: ${c.autonomy}. ${
89
+ c.autonomy === 'hands-on' ? 'Present options, not recommendations.'
90
+ : c.autonomy === 'guided' ? 'Opinionated suggestions with reasoning.'
91
+ : c.autonomy === 'delegated' ? 'Be decisive. Only escalate irreversible decisions.'
92
+ : 'Maximum automation.'
93
+ }`);
94
+
95
+ return lines.join('\n');
96
+ }
97
+
98
+ function generateContext(includeArtifacts = false) {
99
+ const vision = loadArtifact('vision.md');
100
+ const plan = loadArtifact('plan.md');
101
+ const next = loadArtifact('next.md');
102
+ const gate = loadGate();
103
+ const projectName = path.basename(process.cwd());
104
+
105
+ if (!vision && !plan && !next) return null;
106
+
107
+ const sections = [];
108
+ const constraints = gate?.constraints;
109
+ const hasConstraints = constraints && (
110
+ constraints.budget > 0 ||
111
+ constraints.timeHoursPerWeek > 0 ||
112
+ constraints.skillLevel !== 'intermediate' ||
113
+ constraints.urgency !== 'moderate' ||
114
+ constraints.autonomy !== 'guided'
115
+ );
116
+
117
+ // Header
118
+ sections.push(`# PHEWSH Adaptive Context — ${projectName}`);
119
+ sections.push(`> Generated ${new Date().toISOString().split('T')[0]} | phewsh.com/intent`);
120
+ sections.push(`> Drop this into CLAUDE.md, .cursorrules, or agent config.`);
121
+ sections.push('');
122
+
123
+ // Project identity
124
+ sections.push('## Project');
125
+ sections.push(`- **Name**: ${projectName}`);
126
+ if (gate?.archetype) sections.push(`- **Type**: ${gate.archetype}`);
127
+
128
+ // Extract TLDR from vision first line
129
+ if (vision) {
130
+ const firstMeaningful = vision.split('\n').find(l => l.trim().length > 20 && !l.startsWith('#'));
131
+ if (firstMeaningful) sections.push(`- **TLDR**: ${firstMeaningful.trim()}`);
132
+ }
133
+ sections.push('');
134
+
135
+ // Operational constraints
136
+ if (hasConstraints) {
137
+ sections.push('## Operational Reality');
138
+ sections.push('These constraints MUST shape every suggestion and implementation decision:');
139
+ sections.push('');
140
+ sections.push(buildConstraintInstructions(constraints));
141
+ sections.push('');
142
+
143
+ // Execution Reality Map
144
+ if (gate?.executionMap) {
145
+ const map = gate.executionMap;
146
+ sections.push('### Execution Assessment');
147
+ sections.push(`- **Feasibility**: ${gate.feasibility}`);
148
+ if (map.recommendedMode) sections.push(`- **Recommended mode**: ${map.recommendedMode}`);
149
+ if (map.primaryBottleneck) sections.push(`- **Primary bottleneck**: ${map.primaryBottleneck}`);
150
+ if (map.highestLeveragePath) sections.push(`- **Highest leverage move**: ${map.highestLeveragePath}`);
151
+ if (map.estimatedTimeline) sections.push(`- **Timeline**: ${map.estimatedTimeline}`);
152
+ if (map.majorRisks?.length > 0) sections.push(`- **Risks**: ${map.majorRisks.join('; ')}`);
153
+ sections.push('');
154
+ }
155
+ }
156
+
157
+ // Execution state
158
+ if (next) {
159
+ const exec = extractExecutionState(next);
160
+ if (exec.total > 0) {
161
+ sections.push('## Current Execution State');
162
+ sections.push(`Progress: ${exec.done}/${exec.total} done${exec.doing ? `, ${exec.doing} in progress` : ''}${exec.blocked ? `, ${exec.blocked} blocked` : ''}`);
163
+ sections.push('');
164
+
165
+ const doing = exec.items.filter(i => i.state === 'in_progress');
166
+ if (doing.length > 0) {
167
+ sections.push('**Currently working on:**');
168
+ doing.forEach(i => sections.push(`- ${i.title}`));
169
+ sections.push('');
170
+ }
171
+
172
+ const blocked = exec.items.filter(i => i.state === 'blocked');
173
+ if (blocked.length > 0) {
174
+ sections.push('**Blocked:**');
175
+ blocked.forEach(i => sections.push(`- ${i.title}`));
176
+ sections.push('');
177
+ }
178
+
179
+ const todo = exec.items.filter(i => i.state === 'todo');
180
+ if (todo.length > 0) {
181
+ sections.push('**Remaining:**');
182
+ todo.forEach(i => sections.push(`- ${i.title}`));
183
+ sections.push('');
184
+ }
185
+ }
186
+ }
187
+
188
+ // Constraint drift
189
+ if (gate?.constraintHistory?.length > 0) {
190
+ sections.push('### Constraint Drift');
191
+ sections.push('Reality changed during this project:');
192
+ sections.push('');
193
+ for (const ch of gate.constraintHistory.slice(-10)) {
194
+ const date = ch.timestamp.split('T')[0];
195
+ sections.push(`- **${ch.field}**: ${ch.from} → ${ch.to}${ch.reason ? ` (${ch.reason})` : ''} — ${date}`);
196
+ }
197
+ sections.push('');
198
+ }
199
+
200
+ // Responsibility split
201
+ if (gate?.responsibilitySplit) {
202
+ sections.push('## Responsibility Split');
203
+ if (gate.responsibilitySplit.ai?.length > 0) {
204
+ sections.push('**AI/agents can handle:**');
205
+ gate.responsibilitySplit.ai.forEach(r => sections.push(`- ${r}`));
206
+ }
207
+ if (gate.responsibilitySplit.human?.length > 0) {
208
+ sections.push('**Requires human action:**');
209
+ gate.responsibilitySplit.human.forEach(r => sections.push(`- ${r}`));
210
+ }
211
+ sections.push('');
212
+ }
213
+
214
+ // AI instructions
215
+ sections.push('## Instructions for AI Tools');
216
+ sections.push('When working on this project:');
217
+ const instructions = [];
218
+
219
+ if (hasConstraints) {
220
+ instructions.push('- Adapt ALL suggestions to the operational constraints above.');
221
+ if (constraints.budget > 0 && constraints.budget < 500) {
222
+ instructions.push('- Never recommend paid services without noting cost and a free alternative.');
223
+ }
224
+ if (constraints.timeHoursPerWeek > 0 && constraints.timeHoursPerWeek <= 10) {
225
+ instructions.push('- Break every task into steps completable in a single sitting.');
226
+ }
227
+ if (constraints.skillLevel === 'beginner') {
228
+ instructions.push('- Explain every tool, concept, and command. No assumed knowledge.');
229
+ }
230
+ if (constraints.urgency === 'urgent' || constraints.urgency === 'critical') {
231
+ instructions.push('- Prioritize shipping over polish. Cut scope ruthlessly.');
232
+ }
233
+ }
234
+
235
+ if (next) {
236
+ const exec = extractExecutionState(next);
237
+ const blockedCount = exec.items.filter(i => i.state === 'blocked').length;
238
+ if (blockedCount > 0) {
239
+ instructions.push(`- ${blockedCount} task(s) are blocked. Suggest alternative approaches if relevant.`);
240
+ }
241
+ if (exec.done > 0 && exec.total > 0) {
242
+ const pct = Math.round((exec.done / exec.total) * 100);
243
+ instructions.push(`- Project is ${pct}% complete. Focus on remaining work.`);
244
+ }
245
+ }
246
+
247
+ if (instructions.length === 0) {
248
+ instructions.push('- Follow the project vision and plan. Check execution state for current progress.');
249
+ }
250
+ sections.push(instructions.join('\n'));
251
+ sections.push('');
252
+
253
+ // Full artifacts (optional)
254
+ if (includeArtifacts) {
255
+ if (vision) { sections.push('## Vision'); sections.push(vision); sections.push(''); }
256
+ if (plan) { sections.push('## Plan'); sections.push(plan); sections.push(''); }
257
+ if (next) { sections.push('## Next Actions'); sections.push(next); sections.push(''); }
258
+ }
259
+
260
+ sections.push('---');
261
+ sections.push('*Exported from [PHEWSH Intent](https://phewsh.com/intent) — software that adapts to you.*');
262
+
263
+ return sections.join('\n');
264
+ }
265
+
266
+ function showHelp() {
267
+ console.log(`
268
+ phewsh context — Export portable adaptive context
269
+
270
+ Usage:
271
+ phewsh context Generate context to stdout
272
+ phewsh context --full Include full artifact text
273
+ phewsh context --copy Copy to clipboard
274
+ phewsh context --file Write to .phewsh.context in current directory
275
+ phewsh context --claude Write to CLAUDE.md (for Claude Code)
276
+
277
+ What it does:
278
+ Reads .intent/ artifacts + gate.json and produces an operational
279
+ briefing that makes any AI tool constraint-aware.
280
+
281
+ Drop the output into CLAUDE.md, .cursorrules, or agent config.
282
+ `);
283
+ }
284
+
285
+ async function main() {
286
+ if (flags.help) { showHelp(); return; }
287
+
288
+ if (!fs.existsSync(INTENT_DIR)) {
289
+ console.log('\n No .intent/ found. Run `phewsh intent --init` first.\n');
290
+ process.exit(1);
291
+ }
292
+
293
+ const content = generateContext(flags.full);
294
+ if (!content) {
295
+ console.log('\n No artifacts found in .intent/\n');
296
+ process.exit(1);
297
+ }
298
+
299
+ if (flags.file) {
300
+ const outPath = path.join(process.cwd(), '.phewsh.context');
301
+ fs.writeFileSync(outPath, content);
302
+ console.log(`\n ✓ Written to ${outPath}\n`);
303
+ return;
304
+ }
305
+
306
+ if (flags.claude) {
307
+ const outPath = path.join(process.cwd(), 'CLAUDE.md');
308
+ const START_MARKER = '<!-- PHEWSH:START -->';
309
+ const END_MARKER = '<!-- PHEWSH:END -->';
310
+ const wrapped = `${START_MARKER}\n${content}\n${END_MARKER}`;
311
+
312
+ if (fs.existsSync(outPath)) {
313
+ let existing = fs.readFileSync(outPath, 'utf-8');
314
+ const startIdx = existing.indexOf(START_MARKER);
315
+ const endIdx = existing.indexOf(END_MARKER);
316
+ if (startIdx !== -1 && endIdx !== -1) {
317
+ existing = existing.slice(0, startIdx) + wrapped + existing.slice(endIdx + END_MARKER.length);
318
+ fs.writeFileSync(outPath, existing);
319
+ console.log('\n ✓ Updated PHEWSH section in CLAUDE.md\n');
320
+ } else {
321
+ fs.writeFileSync(outPath, existing.trimEnd() + '\n\n' + wrapped + '\n');
322
+ console.log('\n ✓ Appended to CLAUDE.md\n');
323
+ }
324
+ } else {
325
+ fs.writeFileSync(outPath, wrapped + '\n');
326
+ console.log('\n ✓ Created CLAUDE.md\n');
327
+ }
328
+ console.log(' Tip: Run `phewsh watch` to keep this section auto-synced.\n');
329
+ return;
330
+ }
331
+
332
+ if (flags.clipboard) {
333
+ try {
334
+ const { execSync } = require('child_process');
335
+ if (process.platform === 'darwin') {
336
+ execSync('pbcopy', { input: content });
337
+ } else if (process.platform === 'linux') {
338
+ execSync('xclip -selection clipboard', { input: content });
339
+ } else {
340
+ execSync('clip', { input: content });
341
+ }
342
+ console.log('\n ✓ Copied to clipboard\n');
343
+ } catch {
344
+ // Fallback: print to stdout
345
+ console.log(content);
346
+ }
347
+ return;
348
+ }
349
+
350
+ // Default: print to stdout
351
+ console.log(content);
352
+ }
353
+
354
+ module.exports = { main, generateContext };
355
+
356
+ main().catch(err => {
357
+ console.error('\n Error:', err.message);
358
+ process.exit(1);
359
+ });