phewsh 0.15.19 → 0.15.20

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
@@ -53,6 +53,7 @@ const COMMANDS = {
53
53
  push: () => require('../commands/push'),
54
54
  pull: () => require('../commands/pull'),
55
55
  link: () => require('../commands/link'),
56
+ sync: () => require('../commands/sync').main('status'),
56
57
  login: () => require('../commands/login'),
57
58
  ai: () => require('../commands/ai'),
58
59
  style: () => require('../commands/style'),
package/commands/style.js CHANGED
@@ -174,6 +174,15 @@ async function main() {
174
174
  const args = process.argv.slice(3);
175
175
  const config = loadConfig();
176
176
 
177
+ if (args.includes('--help') || args.includes('-h')) {
178
+ console.log('\n phewsh style — your creative identity (StyleTree)');
179
+ console.log('\n Usage:');
180
+ console.log(' phewsh style show your style profile');
181
+ console.log(' phewsh style --ingest <file> add an artifact to learn from');
182
+ console.log(' phewsh style --status same as bare (profile + sync tip)\n');
183
+ return;
184
+ }
185
+
177
186
  // --status
178
187
  if (args.includes('--status') || args.length === 0) {
179
188
  const entries = loadLocal();
package/commands/sync.js CHANGED
@@ -261,11 +261,63 @@ function isAuthError(err) {
261
261
  return m.includes('jwt') || m.includes('expired') || m.includes('401') || m.includes('unauthorized');
262
262
  }
263
263
 
264
+ function agoMs(ms) {
265
+ const mins = Math.floor(ms / 60000);
266
+ if (mins < 1) return 'just now';
267
+ if (mins < 60) return `${mins}m ago`;
268
+ const hrs = Math.floor(mins / 60);
269
+ if (hrs < 24) return `${hrs}h ago`;
270
+ return `${Math.floor(hrs / 24)}d ago`;
271
+ }
272
+
273
+ // Compare local .intent/ against the cloud copy. Read-only — tells you which
274
+ // way to sync, without doing it.
275
+ async function status(config, token) {
276
+ if (!fs.existsSync(INTENT_DIR)) {
277
+ console.log('\n No .intent/ here. Run `phewsh clarify` or `phewsh intent --init` first.\n');
278
+ return;
279
+ }
280
+ const pps = readPPS(INTENT_DIR);
281
+ const cloudId = pps?.adapters?.phewsh?.cloud_id;
282
+ const projectName = getProjectName();
283
+ const query = cloudId
284
+ ? `id=eq.${cloudId}&user_id=eq.${config.supabaseUserId}&select=id,updated_at`
285
+ : `name=eq.${encodeURIComponent(projectName)}&user_id=eq.${config.supabaseUserId}&select=id,updated_at`;
286
+
287
+ const projects = await select('projects', query, token);
288
+ if (projects.length === 0) {
289
+ console.log(`\n ↕ "${projectName}" isn't in the cloud yet — run \`phewsh push\` to sync.\n`);
290
+ return;
291
+ }
292
+ const project = projects[0];
293
+ const artifacts = await select(
294
+ 'artifacts',
295
+ `project_id=eq.${project.id}&user_id=eq.${config.supabaseUserId}&select=updated_at&order=updated_at.desc&limit=1`,
296
+ token
297
+ );
298
+ const cloudTime = artifacts.length > 0
299
+ ? new Date(artifacts[0].updated_at).getTime()
300
+ : new Date(project.updated_at).getTime();
301
+
302
+ let latestLocal = 0;
303
+ for (const f of ['vision.md', 'plan.md', 'next.md']) {
304
+ const p = path.join(INTENT_DIR, f);
305
+ if (fs.existsSync(p)) latestLocal = Math.max(latestLocal, fs.statSync(p).mtimeMs);
306
+ }
307
+ if (latestLocal === 0) { console.log('\n ↕ Not linked to cloud — run `phewsh push`.\n'); return; }
308
+
309
+ const drift = Math.abs(cloudTime - latestLocal);
310
+ if (drift < 60000) console.log('\n ↕ In sync — local and cloud match.\n');
311
+ else if (cloudTime > latestLocal) console.log(`\n ↓ Cloud is newer (${agoMs(Date.now() - cloudTime)}) — run \`phewsh pull\`.\n`);
312
+ else console.log(`\n ↑ Local changes not pushed (${agoMs(Date.now() - latestLocal)}) — run \`phewsh push\`.\n`);
313
+ }
314
+
264
315
  async function main(direction = 'push') {
265
316
  const argv = process.argv.slice(3);
266
317
  if (argv.includes('--help') || argv.includes('-h')) {
267
- console.log(`\n phewsh ${direction === 'pull' ? 'pull' : direction === 'link' ? 'link <cloud-id>' : 'push'} — sync .intent/ with phewsh.com/intent`);
268
- console.log(` push .intent/ cloud pull cloud → .intent/ link adopt a cloud project\n`);
318
+ console.log(`\n phewsh ${direction} — sync .intent/ with phewsh.com/intent`);
319
+ console.log(` sync show which way to sync push .intent/ cloud`);
320
+ console.log(` pull cloud → .intent/ link adopt a cloud project\n`);
269
321
  return;
270
322
  }
271
323
 
@@ -284,7 +336,9 @@ async function main(direction = 'push') {
284
336
  // The token can still be rejected server-side (refresh token also expired).
285
337
  // Convert that into the same friendly nudge instead of a raw stack trace.
286
338
  try {
287
- if (direction === 'pull') {
339
+ if (direction === 'status') {
340
+ await status(config, token);
341
+ } else if (direction === 'pull') {
288
342
  await pull(config, token);
289
343
  } else if (direction === 'link') {
290
344
  const cloudId = argv[0] && !argv[0].startsWith('-') ? argv[0] : process.argv[4];
@@ -306,4 +360,4 @@ async function main(direction = 'push') {
306
360
  }
307
361
  }
308
362
 
309
- module.exports = { main, push, pull, link, ensureValidToken, loadConfig };
363
+ module.exports = { main, push, pull, link, status, ensureValidToken, loadConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phewsh",
3
- "version": "0.15.19",
3
+ "version": "0.15.20",
4
4
  "description": "Turn intent into action. Structure your thinking, execute your next step.",
5
5
  "bin": {
6
6
  "phewsh": "bin/phewsh.js"