phewsh 0.15.72 → 0.15.74

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.
@@ -262,6 +262,10 @@ async function turnOn(skipConfirm) {
262
262
 
263
263
  function turnOff() {
264
264
  const removed = removeClaudeHooks();
265
+ // Full reversibility: ambient off takes the lifecycle hooks with it too.
266
+ let gateRemoved = false;
267
+ try { gateRemoved = disablePreToolGate(); } catch { /* best-effort */ }
268
+ if (gateRemoved) removed.push('lifecycle hooks (PreToolUse gate + PostToolUse receipt)');
265
269
  const { removed: removedGlobal } = selfheal.removeGlobalBaseFiles();
266
270
  const { removed: removedProject } = selfheal.removeProjectContextFiles();
267
271
  const { removed: removedSlash } = slash.removeSlashCommands();
@@ -269,6 +273,7 @@ function turnOff() {
269
273
  delete ledger.applied['claude-code'];
270
274
  delete ledger.applied.globalBase;
271
275
  delete ledger.applied.slashCommands;
276
+ delete ledger.applied.lifecycle;
272
277
  delete ledger.autoEnabledAt;
273
278
  ledger.disabled = true; // sticky opt-out — first-run auto-enable must respect this
274
279
  saveLedger(ledger);
@@ -365,6 +370,11 @@ async function ensureAuto() {
365
370
 
366
371
  const hasClaude = installed.some(h => h.id === 'claude-code');
367
372
  const changes = hasClaude ? applyClaudeHooks() : [];
373
+ // Neal's ruling (Jul 5): the lifecycle hooks (PreToolUse gate + PostToolUse
374
+ // receipt) install with first-run auto-enable too — they're fail-open,
375
+ // redacted, and removed by the same `phewsh ambient off` / `gate enforce off`.
376
+ let gateApplied = false;
377
+ if (hasClaude) { try { gateApplied = enablePreToolGate(); } catch { /* best-effort */ } }
368
378
  const { written } = selfheal.syncGlobalBaseFiles();
369
379
  const slashWritten = slash.installSlashCommands().written;
370
380
  // Refresh existing project files only — first-run auto-enable must not dump
@@ -378,6 +388,13 @@ async function ensureAuto() {
378
388
  captures: '~/.phewsh/ambient-sessions.jsonl — timestamp, project, cwd only',
379
389
  undo: 'phewsh ambient off',
380
390
  };
391
+ if (gateApplied) {
392
+ ledger.applied.lifecycle = {
393
+ at: now, file: CLAUDE_SETTINGS,
394
+ what: 'PreToolUse gate (deny protected paths, ask on high blast radius) + PostToolUse redacted receipt',
395
+ undo: 'phewsh gate enforce off (or phewsh ambient off)',
396
+ };
397
+ }
381
398
  }
382
399
  if (written.length > 0) {
383
400
  ledger.applied.globalBase = { at: now, files: written, undo: 'phewsh ambient off' };
@@ -394,6 +411,7 @@ async function ensureAuto() {
394
411
  console.log('');
395
412
  console.log(` ${b(cream('phewsh set itself up across your AI tools'))} ${sage('— so they stay in sync with your project intent.')}`);
396
413
  if (hasClaude) console.log(` ${teal('+')} ${slate('Claude Code gets a brief from a project\'s .intent/ at session start')}`);
414
+ if (gateApplied) console.log(` ${teal('+')} ${slate('guardrails: protected-path writes gated + a redacted receipt of what ran')}`);
397
415
  written.forEach(f => console.log(` ${teal('+')} ${slate('environment note added to ' + f)}`));
398
416
  if (slashWritten.length) console.log(` ${teal('+')} ${cream('/intent')} ${slate('command added to ' + slashWritten.join(', '))}`);
399
417
  console.log(` ${sage('In any project with')} ${cream('.intent/')}${sage(', your tools now read its real intent. Reversible:')} ${cream('phewsh ambient off')}`);
@@ -81,6 +81,26 @@ function assembleRaw(answers) {
81
81
  return answers.map((a) => `${a.title} (${a.directive}): ${a.answer}`).join('\n');
82
82
  }
83
83
 
84
+ // No-AI spec from the user's own words: the first answer line becomes the
85
+ // goal (node label stripped), the raw text survives verbatim in pps.intent.raw,
86
+ // and the first task points back at the AI compile.
87
+ function fallbackSpec(raw) {
88
+ const first = String(raw).split('\n').find(l => l.trim()) || "Capture this project's intent";
89
+ const goal = first.replace(/^[A-Za-z]+ \([^)]*\):\s*/, '').trim().slice(0, 200);
90
+ return {
91
+ goal,
92
+ success_criteria: [],
93
+ constraints: [],
94
+ inputs: [],
95
+ outputs: [],
96
+ tasks: [
97
+ { text: 'Re-run `phewsh clarify --update` to compile this spec with AI', type: 'copy' },
98
+ { text: 'Refine the vision — complete vision.md', type: 'do' },
99
+ { text: 'Define Phase 1 — what is the smallest thing to ship?', type: 'do' },
100
+ ],
101
+ };
102
+ }
103
+
84
104
  function buildClarifySystemPrompt(existing) {
85
105
  const isRefine = !!(existing?.intent?.goal);
86
106
  return `You are a project compiler. Your job is to extract clean, structured intent from messy human input.
@@ -264,8 +284,12 @@ async function main() {
264
284
  ? await callClarifyViaHarness(harnessId, raw, existing)
265
285
  : await callClarifyAPI(config.apiKey, raw, existing);
266
286
  } catch (err) {
267
- console.error('\n Clarify failed:', err.message, '\n');
268
- process.exit(1);
287
+ // A failed compile must NEVER eat the user's answers. They just walked
288
+ // the compass — write a plain no-AI spec from what they typed, and let
289
+ // `clarify --update` enrich it when a route works again.
290
+ console.log(`\n AI compile unavailable (${err.message}).`);
291
+ console.log(' Saving your answers as a plain spec instead — nothing you typed is lost.');
292
+ extracted = fallbackSpec(raw);
269
293
  }
270
294
 
271
295
  const entity = getProjectName();
@@ -333,4 +357,4 @@ if (require.main === module) {
333
357
  });
334
358
  }
335
359
 
336
- module.exports = { run: main, GUIDE_NODES, INTENT_NODES, assembleRaw, askGuided, extractJson };
360
+ module.exports = { run: main, GUIDE_NODES, INTENT_NODES, assembleRaw, askGuided, extractJson, fallbackSpec };
package/commands/hook.js CHANGED
@@ -74,10 +74,39 @@ function firstLines(text, n) {
74
74
  .join('\n');
75
75
  }
76
76
 
77
+ // The accountability invitation. A real project (it has .git) with no
78
+ // .intent/ gets ONE gentle note injected — telling the agent to offer
79
+ // grounding once, and to drop it if declined. Once per project, ever
80
+ // (tracked in ~/.phewsh/intent-nudges.json); every later session is
81
+ // silent again. Invite, never nag — the recorded stance (decisions.md).
82
+ function maybeIntentInvitation() {
83
+ try {
84
+ if (!fs.existsSync(path.join(process.cwd(), '.git'))) return; // not a real project — cost nothing
85
+ const nudgeFile = path.join(PHEWSH_DIR, 'intent-nudges.json');
86
+ let seen = {};
87
+ try { seen = JSON.parse(fs.readFileSync(nudgeFile, 'utf-8')); } catch { /* first nudge ever */ }
88
+ if (seen && seen[process.cwd()]) return; // already invited here — never again
89
+ seen = (seen && typeof seen === 'object') ? seen : {};
90
+ seen[process.cwd()] = new Date().toISOString();
91
+ fs.mkdirSync(PHEWSH_DIR, { recursive: true });
92
+ fs.writeFileSync(nudgeFile, JSON.stringify(seen, null, 2));
93
+ process.stdout.write([
94
+ '# phewsh — one-time note for this project',
95
+ 'This repo has no `.intent/` yet — no durable project truth that travels between AI tools.',
96
+ 'If the user starts substantial (non-trivial) work here, offer ONCE, in plain language, to ground the project:',
97
+ '`phewsh clarify` (guided walk) or `phewsh init` (two questions) creates `.intent/` — vision, plan, and next steps that every AI tool reads, so the next session picks up where this one left off.',
98
+ 'If they decline, or the work is trivial/exploratory, drop it and never raise it again. Do not create `.intent/` yourself uninvited.',
99
+ ].join('\n') + '\n');
100
+ appendBreadcrumb('intent-invite');
101
+ } catch { /* the invitation is a nicety — never break the host */ }
102
+ }
103
+
77
104
  function sessionStart() {
78
105
  if (!fs.existsSync(path.join(INTENT_DIR, 'vision.md')) &&
79
106
  !fs.existsSync(path.join(INTENT_DIR, 'plan.md'))) {
80
- // No .intent/ here — stay silent, cost the host nothing.
107
+ // No .intent/ here — one gentle invitation if this is a real project,
108
+ // then silence forever.
109
+ maybeIntentInvitation();
81
110
  process.exit(0);
82
111
  }
83
112
 
@@ -6,7 +6,12 @@ const { createPPS, writeGuardedViews } = require('../lib/pps');
6
6
 
7
7
  const os = require('os');
8
8
  const configFile = require('../lib/config-file');
9
- const args = process.argv.slice(3);
9
+ // Flags may arrive as `phewsh intent --init` / `phewsh init --init` (flag at
10
+ // argv[3]) or as a direct `node intent.js --init` (flag at argv[2]) — the
11
+ // session used the direct form and every flag was silently missed, so /init
12
+ // printed help instead of initializing. Accept both shapes.
13
+ const rawArgs = process.argv.slice(2);
14
+ const args = (rawArgs[0] === 'intent' || rawArgs[0] === 'init') ? rawArgs.slice(1) : rawArgs;
10
15
  const INTENT_DIR = path.join(process.cwd(), '.intent');
11
16
  const CONFIG_PATH = path.join(os.homedir(), '.phewsh', 'config.json');
12
17
  const WEB_URL = 'https://phewsh.com/intent';
package/commands/pack.js CHANGED
@@ -36,7 +36,39 @@ function list() {
36
36
  console.log(` ${slate('source: ' + p.source)}`);
37
37
  }
38
38
  console.log('');
39
- console.log(` ${sage('Install:')} ${cream('phewsh pack install <name>')} ${sage('Remove:')} ${cream('phewsh pack remove <name>')}`);
39
+ console.log(` ${sage('Install:')} ${cream('phewsh pack install <name>')} ${sage('All official packs at once:')} ${cream('phewsh pack install all')}`);
40
+ console.log(` ${sage('Remove:')} ${cream('phewsh pack remove <name>')} ${sage('Read about every pack:')} ${cream('phewsh.com/cli#packs')}`);
41
+ console.log('');
42
+ }
43
+
44
+ // `phewsh pack install all` — every official (vendored) pack in one confirmed
45
+ // pass. Linked packs stay pointers to their upstream source; we list them so
46
+ // nothing external ever installs silently.
47
+ async function installAll() {
48
+ const vendored = Object.entries(packs.PACKS).filter(([, p]) => p.kind !== 'linked');
49
+ const linked = Object.entries(packs.PACKS).filter(([, p]) => p.kind === 'linked');
50
+ const pending = vendored.filter(([name]) => !packs.isInstalled(name));
51
+
52
+ console.log('');
53
+ if (pending.length === 0) {
54
+ console.log(` ${sage('All official packs are already installed here.')}`);
55
+ } else {
56
+ console.log(` ${b(cream('Official phewsh packs'))} ${sage('— ' + pending.length + ' to install:')}`);
57
+ pending.forEach(([name, p]) => console.log(` ${cream(name.padEnd(16))} ${slate(p.desc)}`));
58
+ console.log('');
59
+ const ok = await confirm(` ${b('Install ' + (pending.length === 1 ? 'it' : 'all ' + pending.length) + ' here?')} ${slate('[y/N] ')}`);
60
+ if (!ok) { console.log(` ${sage('Nothing changed.')}\n`); return; }
61
+ for (const [name] of pending) {
62
+ const { written } = packs.install(name);
63
+ console.log(` ${green('●')} ${cream(name)} ${slate('→ ' + written.join(', '))}`);
64
+ }
65
+ console.log(` ${sage('Remove any:')} ${cream('phewsh pack remove <name>')}`);
66
+ }
67
+ if (linked.length > 0) {
68
+ console.log('');
69
+ console.log(` ${sage(linked.length + ' more are linked packs — separate tools phewsh points at but never auto-installs:')}`);
70
+ console.log(` ${cream('phewsh pack')} ${sage('lists them ·')} ${cream('phewsh.com/cli#packs')} ${sage('tells their stories')}`);
71
+ }
40
72
  console.log('');
41
73
  }
42
74
 
@@ -89,6 +121,7 @@ function remove(name) {
89
121
  async function main() {
90
122
  const sub = process.argv[3];
91
123
  const name = process.argv[4];
124
+ if (sub === 'install' && name === 'all') return installAll();
92
125
  if (sub === 'install' && name) return install(name);
93
126
  if (sub === 'remove' && name) return remove(name);
94
127
  return list();
@@ -733,6 +733,39 @@ async function main() {
733
733
  console.log('');
734
734
  }
735
735
 
736
+ // The project scanner — bootstrap option AND /scan slash command. Lists
737
+ // .intent/ projects plus likely candidates (git, no .intent yet, reason
738
+ // shown) from the usual folders, numbered so a bare digit opens one.
739
+ function runScanMenu() {
740
+ const spin = ui.spinner('scanning your usual folders');
741
+ const found = scanForProjects();
742
+ let candidates = [];
743
+ try { candidates = scanForCandidates(); } catch { /* advisory — scan still useful without it */ }
744
+ spin.stop();
745
+ if (found.length === 0 && candidates.length === 0) {
746
+ bootstrapChoices = null;
747
+ console.log(` ${sage('No .intent/ projects found in the usual folders.')}`);
748
+ console.log(` ${slate('cd into a project and run phewsh, or /init to start one here.')}`);
749
+ return;
750
+ }
751
+ bootstrapChoices = [];
752
+ if (found.length > 0) {
753
+ console.log(` ${teal('●')} ${sage(`Found ${found.length} project${found.length !== 1 ? 's' : ''} with .intent/:`)}`);
754
+ for (const p of found) {
755
+ bootstrapChoices.push({ kind: 'open', path: p.path });
756
+ console.log(` ${teal(String(bootstrapChoices.length))} ${cream(p.name)} ${slate('· ' + tildify(p.path))}`);
757
+ }
758
+ }
759
+ if (candidates.length > 0) {
760
+ console.log(` ${teal('●')} ${sage(`${candidates.length} likely candidate${candidates.length !== 1 ? 's' : ''} — no shared memory yet:`)}`);
761
+ for (const p of candidates) {
762
+ bootstrapChoices.push({ kind: 'open', path: p.path });
763
+ console.log(` ${teal(String(bootstrapChoices.length))} ${cream(p.name)} ${slate('· ' + tildify(p.path) + ' · ' + p.reason)}`);
764
+ }
765
+ }
766
+ console.log(` ${slate('pick a number to open it')}`);
767
+ }
768
+
736
769
  function showBootstrapMenu(projects) {
737
770
  console.log(` ${b(cream('Where do you want to work?'))}`);
738
771
  bootstrapChoices = [];
@@ -976,7 +1009,7 @@ async function main() {
976
1009
  // RECOGNIZED leading /command (or @harness) token turns teal (peach for @)
977
1010
  // so you know it registered. Arguments stay plain. TTY-only, fail-soft.
978
1011
  const KNOWN_COMMANDS = new Set([
979
- 'quit', 'exit', 'q', 'help', 'h', 'init', 'intent', 'clarify', 'model',
1012
+ 'quit', 'exit', 'q', 'help', 'h', 'init', 'intent', 'clarify', 'scan', 'model',
980
1013
  'models', 'council', 'all', 'provider', 'route', 'use', 'work', 'switch', 'run',
981
1014
  'clear', 'status', 'key', 'login', 'export', 'push', 'pull', 'serve',
982
1015
  'sync', 'harnesses', 'fallback', 'outcomes', 'tour', 'update', 'upgrade',
@@ -1277,8 +1310,11 @@ async function main() {
1277
1310
  return;
1278
1311
  }
1279
1312
 
1280
- // Root bootstrap: a bare number opens a project, inits, or scans
1281
- if (bootstrapChoices && messages.length === 0 && /^[0-9]{1,2}$/.test(input)) {
1313
+ // Scan/bootstrap menu: a bare number opens a project, inits, or scans.
1314
+ // Any other input means the user moved on — drop the menu so it never
1315
+ // shadows a later digit (mirrors the nextChoices rule above).
1316
+ if (bootstrapChoices && !/^[0-9]{1,2}$/.test(input)) bootstrapChoices = null;
1317
+ if (bootstrapChoices && /^[0-9]{1,2}$/.test(input)) {
1282
1318
  const choice = bootstrapChoices[parseInt(input, 10) - 1];
1283
1319
  if (!choice) {
1284
1320
  console.log(` ${sage('Pick 1-' + bootstrapChoices.length)}`);
@@ -1293,8 +1329,8 @@ async function main() {
1293
1329
  if (choice.kind === 'init') {
1294
1330
  bootstrapChoices = null;
1295
1331
  try {
1296
- const { execSync } = require('child_process');
1297
- execSync('node ' + path.join(__dirname, 'intent.js') + ' --init', { stdio: 'inherit' });
1332
+ const { execFileSync } = require('child_process');
1333
+ execFileSync(process.execPath, [path.join(__dirname, '..', 'bin', 'phewsh.js'), 'intent', '--init'], { stdio: 'inherit' });
1298
1334
  intentFiles = loadIntentContext();
1299
1335
  systemPrompt = buildSystemPrompt(intentFiles);
1300
1336
  if (intentFiles.length > 0) {
@@ -1309,35 +1345,7 @@ async function main() {
1309
1345
  return;
1310
1346
  }
1311
1347
  if (choice.kind === 'scan') {
1312
- const spin = ui.spinner('scanning your usual folders');
1313
- const found = scanForProjects();
1314
- // Likely candidates: real repos with no .intent/ yet — the projects
1315
- // most worth grounding. Same shallow scan; shown with their reason.
1316
- let candidates = [];
1317
- try { candidates = scanForCandidates(); } catch { /* advisory — scan still useful without it */ }
1318
- spin.stop();
1319
- if (found.length === 0 && candidates.length === 0) {
1320
- bootstrapChoices = null;
1321
- console.log(` ${sage('No .intent/ projects found in the usual folders.')}`);
1322
- console.log(` ${slate('cd into a project and run phewsh, or /init to start one here.')}`);
1323
- } else {
1324
- bootstrapChoices = [];
1325
- if (found.length > 0) {
1326
- console.log(` ${teal('●')} ${sage(`Found ${found.length} project${found.length !== 1 ? 's' : ''} with .intent/:`)}`);
1327
- for (const p of found) {
1328
- bootstrapChoices.push({ kind: 'open', path: p.path });
1329
- console.log(` ${teal(String(bootstrapChoices.length))} ${cream(p.name)} ${slate('· ' + tildify(p.path))}`);
1330
- }
1331
- }
1332
- if (candidates.length > 0) {
1333
- console.log(` ${teal('●')} ${sage(`${candidates.length} likely candidate${candidates.length !== 1 ? 's' : ''} — no shared memory yet:`)}`);
1334
- for (const p of candidates) {
1335
- bootstrapChoices.push({ kind: 'open', path: p.path });
1336
- console.log(` ${teal(String(bootstrapChoices.length))} ${cream(p.name)} ${slate('· ' + tildify(p.path) + ' · ' + p.reason)}`);
1337
- }
1338
- }
1339
- console.log(` ${slate('pick a number to open it')}`);
1340
- }
1348
+ runScanMenu();
1341
1349
  console.log('');
1342
1350
  rl.prompt();
1343
1351
  return;
@@ -1671,6 +1679,7 @@ async function main() {
1671
1679
  console.log(` ${teal('@name')} ${slate('<msg>')} ${sage('one message to one tool — @codex review this')}`);
1672
1680
  console.log(` ${teal('/work')} ${slate('[tool]')} ${sage('hand off to the full interactive tool, outcome on return')}`);
1673
1681
  console.log(` ${teal('/clarify')} ${sage('turn messy thoughts into .intent/ artifacts')}`);
1682
+ console.log(` ${teal('/scan')} ${sage('find your projects — and repos that need shared memory')}`);
1674
1683
  console.log(` ${teal('/outcomes')} ${sage('label what you kept — the record that gets smarter')}`);
1675
1684
  console.log('');
1676
1685
  console.log(` ${slate('/help all')} ${sage('everything')} ${slate('·')} ${slate('/tour')} ${sage('walkthrough')} ${slate('·')} ${slate('/quit')} ${sage('exit')}`);
@@ -1688,6 +1697,7 @@ async function main() {
1688
1697
  console.log(` ${teal('/learn')} ${sage('what your record taught — which tool keeps best, by kind of work')}`);
1689
1698
  console.log('');
1690
1699
  console.log(` ${cream('author .intent/')}`);
1700
+ console.log(` ${teal('/scan')} ${sage('Find your projects — and likely candidates with no .intent/ yet')}`);
1691
1701
  console.log(` ${teal('/init')} ${sage('Create .intent/ for this project')}`);
1692
1702
  console.log(` ${teal('/intent')} ${sage('Pause and reflect — view or update .intent/ before moving on')}`);
1693
1703
  console.log(` ${teal('/remember')} ${sage('Jot a decision to .intent/decisions.md — every tool inherits it')}`);
@@ -1906,8 +1916,8 @@ async function main() {
1906
1916
  console.log(` ${sage('Use /reload to refresh context')}\n`);
1907
1917
  } else {
1908
1918
  try {
1909
- const { execSync } = require('child_process');
1910
- execSync('node ' + path.join(__dirname, 'intent.js') + ' --init', { stdio: 'inherit' });
1919
+ const { execFileSync } = require('child_process');
1920
+ execFileSync(process.execPath, [path.join(__dirname, '..', 'bin', 'phewsh.js'), 'intent', '--init'], { stdio: 'inherit' });
1911
1921
  intentFiles = loadIntentContext();
1912
1922
  systemPrompt = buildSystemPrompt(intentFiles);
1913
1923
  if (intentFiles.length > 0) {
@@ -1922,6 +1932,13 @@ async function main() {
1922
1932
  return;
1923
1933
  }
1924
1934
 
1935
+ if (cmd === 'scan') {
1936
+ runScanMenu();
1937
+ console.log('');
1938
+ rl.prompt();
1939
+ return;
1940
+ }
1941
+
1925
1942
  if (cmd === 'clarify') {
1926
1943
  try {
1927
1944
  const { spawnSync } = require('child_process');
package/lib/cors.js CHANGED
@@ -37,6 +37,12 @@ function corsHeaders(req) {
37
37
  'Access-Control-Allow-Origin': origin,
38
38
  'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
39
39
  'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Phewsh-Runtime',
40
+ // Chrome Local/Private Network Access: an HTTPS page (phewsh.com) fetching
41
+ // plain-http loopback gets a preflight carrying Access-Control-Request-
42
+ // Private-Network; without this answer Chrome kills the request before it
43
+ // is ever sent — the cockpit shows "bridge offline" while serve is
44
+ // demonstrably up. Loopback-only server, allowlisted origins only.
45
+ 'Access-Control-Allow-Private-Network': 'true',
40
46
  'Access-Control-Max-Age': '600',
41
47
  Vary: 'Origin',
42
48
  };
package/lib/packs.js CHANGED
@@ -54,11 +54,11 @@ const PACKS = {
54
54
  },
55
55
  'loop-library': {
56
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',
57
+ title: 'Loop Library / Loopy',
58
+ desc: 'Forward Future\'s practical agent-loop library and Loopy skill, with checks and stopping conditions. Good Work-layer inspiration; phewsh links to it without becoming a scheduler or runner.',
59
+ source: 'signals.forwardfuture.com/loop-library · github.com/Forward-Future/loop-library',
60
60
  license: 'MIT',
61
- install: 'npx skills add Forward-Future/loop-library --skill loop-library -g # install through upstream skills tooling',
61
+ install: 'npx skills add Forward-Future/loop-library --skill loopy -g # install Loopy through upstream skills tooling',
62
62
  },
63
63
  'matt-skills': {
64
64
  kind: 'linked',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phewsh",
3
- "version": "0.15.72",
3
+ "version": "0.15.74",
4
4
  "description": "Turn intent into action. Structure your thinking, execute your next step.",
5
5
  "bin": {
6
6
  "phewsh": "bin/phewsh.js"