wayfind 2.0.30 → 2.0.32

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/digest.js CHANGED
@@ -755,12 +755,12 @@ async function generateDigest(config, personaIds, sinceDate, onProgress) {
755
755
  signalsDir: config.signals_dir,
756
756
  };
757
757
  const storeResult = collectFromStore(sinceDate, storeOpts);
758
- if (storeResult.entryCount > 0) {
758
+ if (storeResult.entryCount > 0 && storeResult.journals) {
759
759
  journalContent = storeResult.journals;
760
760
  // Use store signals if available, otherwise fall back to direct file scan
761
761
  signalContent = storeResult.signals || collectSignals(sinceDate, config.signals_dir);
762
762
  } else {
763
- // Fallback: direct file scan (store not indexed)
763
+ // Fallback: direct file scan (store not indexed or content retrieval failed)
764
764
  signalContent = collectSignals(sinceDate, config.signals_dir);
765
765
  journalContent = collectJournals(sinceDate, config.journal_dir);
766
766
  }
@@ -3283,9 +3283,12 @@ async function runContext(args) {
3283
3283
  case 'default':
3284
3284
  contextSetDefault(subArgs);
3285
3285
  break;
3286
+ case 'pull':
3287
+ contextPull(subArgs);
3288
+ break;
3286
3289
  default:
3287
3290
  console.error(`Unknown context subcommand: ${sub}`);
3288
- console.error('Available: init, sync, show, add, bind, list, default');
3291
+ console.error('Available: init, sync, show, add, bind, list, default, pull');
3289
3292
  process.exit(1);
3290
3293
  }
3291
3294
  }
@@ -3428,6 +3431,61 @@ function contextSync() {
3428
3431
  console.log(`\nSynced ${synced} file(s) to .claude/context/`);
3429
3432
  }
3430
3433
 
3434
+ /**
3435
+ * Pull latest from the team-context repo so session state reflects
3436
+ * other engineers' recent work. Called by the session-start hook.
3437
+ *
3438
+ * Behavior:
3439
+ * - Pulls the team-context repo (journals, signals, shared context)
3440
+ * - Skips gracefully if offline, if pull fails, or if repo is dirty
3441
+ * - Does NOT touch the current working repo — devs handle that themselves
3442
+ * - Designed to run within the session-start hook's timeout
3443
+ *
3444
+ * @param {string[]} args - CLI arguments (--quiet suppresses output)
3445
+ */
3446
+ function contextPull(args) {
3447
+ const quiet = args.includes('--quiet');
3448
+ const background = args.includes('--background');
3449
+ const log = quiet ? () => {} : console.log;
3450
+
3451
+ const teamPath = getTeamContextPath();
3452
+ if (!teamPath || !fs.existsSync(path.join(teamPath, '.git'))) {
3453
+ if (!quiet) log('[wayfind] No team-context repo configured — skipping pull');
3454
+ return;
3455
+ }
3456
+
3457
+ const markerFile = path.join(teamPath, '.last-pull');
3458
+
3459
+ if (background) {
3460
+ // Fire-and-forget — don't block session start
3461
+ const child = require('child_process').spawn(
3462
+ process.execPath,
3463
+ [__filename, 'context', 'pull', '--quiet'],
3464
+ { stdio: 'ignore', detached: true, env: { ...process.env } }
3465
+ );
3466
+ child.unref();
3467
+ return;
3468
+ }
3469
+
3470
+ const result = spawnSync('git', ['-C', teamPath, 'pull', '--rebase', '--autostash', '--quiet'], {
3471
+ stdio: 'pipe',
3472
+ timeout: 10000,
3473
+ });
3474
+
3475
+ if (result.status === 0) {
3476
+ log('[wayfind] Pulled latest team-context');
3477
+ // Mark success — doctor checks this to warn on prolonged failures
3478
+ try { fs.writeFileSync(markerFile, new Date().toISOString()); } catch {}
3479
+ } else if (result.error && result.error.code === 'ETIMEDOUT') {
3480
+ log('[wayfind] Team-context pull timed out — using local state');
3481
+ } else {
3482
+ const stderr = (result.stderr || '').toString().trim();
3483
+ if (stderr && !quiet) {
3484
+ console.error(`[wayfind] Team-context pull skipped: ${stderr.split('\n')[0]}`);
3485
+ }
3486
+ }
3487
+ }
3488
+
3431
3489
  function contextShow() {
3432
3490
  const config = readContextConfig();
3433
3491
  const repoBinding = readRepoTeamBinding();
@@ -4264,6 +4322,23 @@ function ensureContainerConfig() {
4264
4322
  changed = true;
4265
4323
  }
4266
4324
 
4325
+ // Backfill container-specific paths into existing digest config (fixes configs
4326
+ // created before store_path/journal_dir/signals_dir were added)
4327
+ if (config.digest) {
4328
+ const defaults = {
4329
+ store_path: process.env.TEAM_CONTEXT_STORE_PATH || contentStore.DEFAULT_STORE_PATH,
4330
+ journal_dir: process.env.TEAM_CONTEXT_JOURNALS_DIR || '/data/journals',
4331
+ signals_dir: process.env.TEAM_CONTEXT_SIGNALS_DIR || contentStore.DEFAULT_SIGNALS_DIR,
4332
+ team_context_dir: process.env.TEAM_CONTEXT_TEAM_CONTEXT_DIR || '',
4333
+ };
4334
+ for (const [key, val] of Object.entries(defaults)) {
4335
+ if (!config.digest[key]) {
4336
+ config.digest[key] = val;
4337
+ changed = true;
4338
+ }
4339
+ }
4340
+ }
4341
+
4267
4342
  // Slack bot config
4268
4343
  if (!config.slack_bot && process.env.SLACK_BOT_TOKEN) {
4269
4344
  config.slack_bot = {
@@ -4281,6 +4356,20 @@ function ensureContainerConfig() {
4281
4356
  changed = true;
4282
4357
  }
4283
4358
 
4359
+ // Backfill container-specific paths into existing bot config
4360
+ if (config.slack_bot) {
4361
+ const botDefaults = {
4362
+ store_path: process.env.TEAM_CONTEXT_STORE_PATH || contentStore.DEFAULT_STORE_PATH,
4363
+ journal_dir: process.env.TEAM_CONTEXT_JOURNALS_DIR || '/data/journals',
4364
+ };
4365
+ for (const [key, val] of Object.entries(botDefaults)) {
4366
+ if (!config.slack_bot[key]) {
4367
+ config.slack_bot[key] = val;
4368
+ changed = true;
4369
+ }
4370
+ }
4371
+ }
4372
+
4284
4373
  // GitHub connector
4285
4374
  if (!config.github && process.env.GITHUB_TOKEN) {
4286
4375
  const repos = process.env.TEAM_CONTEXT_GITHUB_REPOS;
@@ -4970,8 +5059,9 @@ const COMMANDS = {
4970
5059
  }
4971
5060
  console.log('Sanitization passed — no proprietary content detected.');
4972
5061
 
4973
- // Show what changed
4974
- const diffResult = spawnSync('git', ['status', '--short'], { cwd: tmpDir, stdio: 'pipe' });
5062
+ // Refresh git index so status detects copied files reliably
5063
+ spawnSync('git', ['add', '-A'], { cwd: tmpDir, stdio: 'pipe' });
5064
+ const diffResult = spawnSync('git', ['diff', '--cached', '--name-only'], { cwd: tmpDir, stdio: 'pipe' });
4975
5065
  const changes = (diffResult.stdout || '').toString().trim();
4976
5066
 
4977
5067
  if (!changes) {
package/doctor.sh CHANGED
@@ -269,6 +269,54 @@ check_memory_files() {
269
269
  [ "$COUNT" -gt 0 ] && ok "$COUNT memory file(s) found" || info "No memory files yet"
270
270
  }
271
271
 
272
+ check_team_context_freshness() {
273
+ echo ""
274
+ echo "Team context sync"
275
+
276
+ # Find team-context path via wayfind CLI
277
+ local TEAM_PATH=""
278
+ if [ -f "$HOME/repos/greg/wayfind/bin/team-context.js" ]; then
279
+ TEAM_PATH=$(node "$HOME/repos/greg/wayfind/bin/team-context.js" context show 2>/dev/null | grep 'Path:' | head -1 | sed 's/.*Path: *//' || true)
280
+ elif command -v wayfind >/dev/null 2>&1; then
281
+ TEAM_PATH=$(wayfind context show 2>/dev/null | grep 'Path:' | head -1 | sed 's/.*Path: *//' || true)
282
+ fi
283
+
284
+ if [ -z "$TEAM_PATH" ] || [ ! -d "$TEAM_PATH" ]; then
285
+ info "No team-context repo configured (optional)"
286
+ return
287
+ fi
288
+
289
+ local MARKER="$TEAM_PATH/.last-pull"
290
+ if [ ! -f "$MARKER" ]; then
291
+ # No pull has ever succeeded — check if this is a fresh install
292
+ # by looking at the git log age instead
293
+ local LAST_COMMIT
294
+ LAST_COMMIT=$(git -C "$TEAM_PATH" log -1 --format='%ci' 2>/dev/null || echo "")
295
+ if [ -z "$LAST_COMMIT" ]; then
296
+ info "Team-context repo exists but has no commits"
297
+ else
298
+ ok "Team-context configured (pull marker will appear after next session start)"
299
+ fi
300
+ return
301
+ fi
302
+
303
+ local PULL_TIME
304
+ PULL_TIME=$(cat "$MARKER")
305
+ local PULL_EPOCH
306
+ PULL_EPOCH=$(date -d "$PULL_TIME" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%S" "${PULL_TIME%%.*}" +%s 2>/dev/null || echo 0)
307
+ local NOW_EPOCH
308
+ NOW_EPOCH=$(date +%s)
309
+ local AGE_HOURS=$(( (NOW_EPOCH - PULL_EPOCH) / 3600 ))
310
+
311
+ if [ "$AGE_HOURS" -gt 24 ]; then
312
+ warn "Team-context last pulled ${AGE_HOURS}h ago — other engineers' context may be stale"
313
+ info "Run: wayfind context pull"
314
+ ISSUES=$((ISSUES + 1))
315
+ else
316
+ ok "Team-context pulled ${AGE_HOURS}h ago"
317
+ fi
318
+ }
319
+
272
320
  check_team_versions() {
273
321
  echo ""
274
322
  echo "Team version compliance"
@@ -330,6 +378,7 @@ echo "══════════════════════"
330
378
  check_hook_registered
331
379
  check_global_state
332
380
  check_backup
381
+ check_team_context_freshness
333
382
  check_team_versions
334
383
  check_storage_backend
335
384
  check_memory_files
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wayfind",
3
- "version": "2.0.30",
3
+ "version": "2.0.32",
4
4
  "description": "Team decision trail for AI-assisted development. The connective tissue between product, engineering, and strategy.",
5
5
  "bin": {
6
6
  "wayfind": "./bin/team-context.js"
@@ -25,6 +25,10 @@ if [ -z "$WAYFIND" ]; then
25
25
  exit 0
26
26
  fi
27
27
 
28
+ # Pull latest team-context in the background so this session sees
29
+ # other engineers' recent work without blocking session start
30
+ $WAYFIND context pull --quiet --background 2>/dev/null || true
31
+
28
32
  # Rebuild Active Projects table (idempotent, concurrent-safe)
29
33
  $WAYFIND status --write --quiet 2>/dev/null || true
30
34