wayfind 2.0.32 → 2.0.34

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.
@@ -569,8 +569,38 @@ function searchText(query, options = {}) {
569
569
  ...(entry.tags || []),
570
570
  ].filter(Boolean).join(' ').toLowerCase().replace(/[-_]/g, ' ');
571
571
 
572
+ // For signal entries, read content directly from the signal file
573
+ if (entry.source === 'signal') {
574
+ const signalsDir = options.signalsDir || DEFAULT_SIGNALS_DIR;
575
+ if (signalsDir) {
576
+ // Signal files live at signalsDir/<channel>/<date>.md or signalsDir/<channel>/<owner>/<repo>/<date>.md
577
+ // The repo field tells us the path: "signals/<channel>" or "<owner>/<repo>"
578
+ const repo = entry.repo || '';
579
+ const candidates = [];
580
+ if (repo.startsWith('signals/')) {
581
+ const channel = repo.replace('signals/', '');
582
+ candidates.push(path.join(signalsDir, channel, `${entry.date}.md`));
583
+ candidates.push(path.join(signalsDir, channel, `${entry.date}-summary.md`));
584
+ } else if (repo.includes('/')) {
585
+ // owner/repo format — find which channel it's under
586
+ for (const channel of ['github', 'intercom', 'notion']) {
587
+ candidates.push(path.join(signalsDir, channel, repo, `${entry.date}.md`));
588
+ }
589
+ }
590
+ for (const fp of candidates) {
591
+ try {
592
+ const content = fs.readFileSync(fp, 'utf8').toLowerCase();
593
+ searchable += ' ' + content.replace(/[-_]/g, ' ');
594
+ break;
595
+ } catch {
596
+ // Try next candidate
597
+ }
598
+ }
599
+ }
600
+ }
601
+
572
602
  // Also include the full journal entry content if available
573
- const journalContent = getJournalContent(entry.date, entry.user);
603
+ const journalContent = entry.source !== 'signal' ? getJournalContent(entry.date, entry.user) : null;
574
604
  if (journalContent) {
575
605
  // Find this entry's section in the journal file.
576
606
  // Try exact match first, then normalize hyphens/spaces for fuzzy match.
@@ -126,7 +126,9 @@ function getBackend(storePath) {
126
126
  migrateFromJson(backend, storePath);
127
127
  cache[storePath] = backend;
128
128
  return backend;
129
- } catch {
129
+ } catch (err) {
130
+ console.error('Warning: SQLite backend failed to initialize, falling back to JSON.', err.message);
131
+ cache[storePath + ':fallback'] = true;
130
132
  const backend = new JsonBackend(storePath);
131
133
  backend.open();
132
134
  cache[storePath] = backend;
@@ -164,8 +166,23 @@ function getBackendType(storePath) {
164
166
  return null;
165
167
  }
166
168
 
169
+ /**
170
+ * Returns detailed backend info including whether it was a silent fallback.
171
+ *
172
+ * @param {string} storePath
173
+ * @returns {{ type: 'sqlite'|'json'|'unknown', storePath: string, fallback: boolean }|null}
174
+ */
175
+ function getBackendInfo(storePath) {
176
+ const backend = cache[storePath];
177
+ if (!backend) return null;
178
+ const type = backend instanceof JsonBackend ? 'json' :
179
+ (backend.constructor && backend.constructor.name === 'SqliteBackend') ? 'sqlite' : 'unknown';
180
+ return { type, storePath, fallback: !!cache[storePath + ':fallback'] };
181
+ }
182
+
167
183
  module.exports = {
168
184
  getBackend,
169
185
  clearCache,
170
186
  getBackendType,
187
+ getBackendInfo,
171
188
  };
@@ -4370,10 +4370,10 @@ function ensureContainerConfig() {
4370
4370
  }
4371
4371
  }
4372
4372
 
4373
- // GitHub connector
4374
- if (!config.github && process.env.GITHUB_TOKEN) {
4373
+ // GitHub connector — create if missing, or fix transport if mounted config has gh-cli
4374
+ if (process.env.GITHUB_TOKEN) {
4375
4375
  const repos = process.env.TEAM_CONTEXT_GITHUB_REPOS;
4376
- if (repos) {
4376
+ if (!config.github && repos) {
4377
4377
  config.github = {
4378
4378
  transport: 'https',
4379
4379
  token: process.env.GITHUB_TOKEN,
@@ -4382,6 +4382,19 @@ function ensureContainerConfig() {
4382
4382
  last_pull: null,
4383
4383
  };
4384
4384
  changed = true;
4385
+ } else if (config.github) {
4386
+ // Override gh-cli transport in container context — gh binary isn't available
4387
+ if (config.github.transport === 'gh-cli') {
4388
+ config.github.transport = 'https';
4389
+ config.github.token = process.env.GITHUB_TOKEN;
4390
+ config.github.token_env = 'GITHUB_TOKEN';
4391
+ changed = true;
4392
+ }
4393
+ // Backfill repos from env if config has none or env specifies them
4394
+ if (repos && (!config.github.repos || config.github.repos.length === 0)) {
4395
+ config.github.repos = repos.split(',').map((r) => r.trim());
4396
+ changed = true;
4397
+ }
4385
4398
  }
4386
4399
  }
4387
4400
 
package/doctor.sh CHANGED
@@ -357,17 +357,116 @@ check_storage_backend() {
357
357
  echo ""
358
358
  echo "Storage backend"
359
359
 
360
+ set +e # Temporarily disable errexit — node calls may fail in test environments
360
361
  # Check for env var override
361
362
  if [ -n "${TEAM_CONTEXT_STORAGE_BACKEND:-}" ]; then
362
363
  info "TEAM_CONTEXT_STORAGE_BACKEND=$TEAM_CONTEXT_STORAGE_BACKEND (env override)"
363
364
  fi
364
365
 
365
- # Check if better-sqlite3 is available
366
- if node -e "require('better-sqlite3')" 2>/dev/null; then
367
- ok "Storage backend: sqlite (better-sqlite3 available)"
366
+ local WAYFIND_DIR="${WAYFIND_DIR:-$HOME/.claude/team-context}"
367
+ local SCRIPT_DIR
368
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
369
+
370
+ # ── 1. Which backend is active ───────────────────────────────────────────
371
+ local BACKEND_RESULT=""
372
+ BACKEND_RESULT=$(node -e "
373
+ try {
374
+ const storage = require('$SCRIPT_DIR/bin/storage/index.js');
375
+ const storePath = '$WAYFIND_DIR';
376
+ storage.getBackend(storePath);
377
+ const info = storage.getBackendInfo(storePath);
378
+ if (!info) { console.log('NONE'); process.exit(0); }
379
+ console.log(JSON.stringify(info));
380
+ } catch (e) {
381
+ console.log('SKIP:' + e.message.split('\n')[0]);
382
+ }
383
+ " 2>/dev/null) || BACKEND_RESULT="SKIP:storage module not available"
384
+
385
+ if [[ "$BACKEND_RESULT" == SKIP:* ]]; then
386
+ [ "$VERBOSE" = true ] && info "Storage check skipped: ${BACKEND_RESULT#SKIP:}"
387
+ elif [[ "$BACKEND_RESULT" == "NONE" ]]; then
388
+ info "No storage backend initialized (team-context not configured)"
389
+ else
390
+ local BACKEND_TYPE
391
+ BACKEND_TYPE=$(echo "$BACKEND_RESULT" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.type)" 2>/dev/null || echo "unknown")
392
+ local IS_FALLBACK
393
+ IS_FALLBACK=$(echo "$BACKEND_RESULT" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.fallback)" 2>/dev/null || echo "false")
394
+
395
+ if [ "$BACKEND_TYPE" = "sqlite" ]; then
396
+ ok "Active backend: sqlite"
397
+ elif [ "$BACKEND_TYPE" = "json" ]; then
398
+ ok "Active backend: json"
399
+ else
400
+ warn "Active backend: $BACKEND_TYPE (unexpected)"
401
+ fi
402
+
403
+ # ── 2. Fallback detection ────────────────────────────────────────────
404
+ if [ "$IS_FALLBACK" = "true" ]; then
405
+ warn "JSON backend is a fallback — better-sqlite3 was expected but failed to load"
406
+ info "Run: npm install -g better-sqlite3 (or reinstall wayfind)"
407
+ ISSUES=$((ISSUES + 1))
408
+ elif [ "$BACKEND_TYPE" = "json" ]; then
409
+ # Not a runtime fallback — check if sqlite3 is available but unused
410
+ if node -e "require('better-sqlite3')" 2>/dev/null; then
411
+ warn "better-sqlite3 is installed but backend is JSON — possible unexpected fallback"
412
+ info "Check TEAM_CONTEXT_STORAGE_BACKEND env var or re-run setup"
413
+ ISSUES=$((ISSUES + 1))
414
+ else
415
+ [ "$VERBOSE" = true ] && info "better-sqlite3 not installed — JSON is expected"
416
+ fi
417
+ fi
418
+ fi
419
+
420
+ # ── 3. Signal freshness (connectors.json last_pull) ──────────────────────
421
+ local CONNECTORS="$WAYFIND_DIR/connectors.json"
422
+ if [ -f "$CONNECTORS" ]; then
423
+ local FRESHNESS_RESULT
424
+ FRESHNESS_RESULT=$(node -e "
425
+ const fs = require('fs');
426
+ const config = JSON.parse(fs.readFileSync('$CONNECTORS', 'utf8'));
427
+ const connectors = ['github', 'intercom', 'notion'];
428
+ const now = Date.now();
429
+ const DAY = 24 * 60 * 60 * 1000;
430
+ const results = [];
431
+ for (const name of connectors) {
432
+ const c = config[name];
433
+ if (!c) continue;
434
+ const lp = c.last_pull;
435
+ if (!lp) {
436
+ results.push('WARN:' + name + ':never pulled');
437
+ } else {
438
+ const age = now - new Date(lp).getTime();
439
+ const hours = Math.floor(age / (60 * 60 * 1000));
440
+ if (age > DAY) {
441
+ results.push('WARN:' + name + ':' + hours + 'h ago');
442
+ } else {
443
+ results.push('OK:' + name + ':' + hours + 'h ago');
444
+ }
445
+ }
446
+ }
447
+ console.log(results.join('|'));
448
+ " 2>/dev/null) || FRESHNESS_RESULT=""
449
+
450
+ if [ -n "$FRESHNESS_RESULT" ]; then
451
+ IFS='|' read -ra ENTRIES <<< "$FRESHNESS_RESULT"
452
+ for entry in "${ENTRIES[@]}"; do
453
+ local STATUS="${entry%%:*}"
454
+ local REST="${entry#*:}"
455
+ local NAME="${REST%%:*}"
456
+ local DETAIL="${REST#*:}"
457
+ if [ "$STATUS" = "WARN" ]; then
458
+ warn "Signal $NAME: $DETAIL"
459
+ info "Run: wayfind pull $NAME"
460
+ ISSUES=$((ISSUES + 1))
461
+ else
462
+ ok "Signal $NAME: last pull $DETAIL"
463
+ fi
464
+ done
465
+ fi
368
466
  else
369
- ok "Storage backend: json (better-sqlite3 not installedusing JSON fallback)"
467
+ [ "$VERBOSE" = true ] && info "No connectors.json signal freshness check skipped"
370
468
  fi
469
+ set -e # Restore errexit
371
470
  }
372
471
 
373
472
  # Run checks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wayfind",
3
- "version": "2.0.32",
3
+ "version": "2.0.34",
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"