wayfind 2.0.47 → 2.0.48

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.
Files changed (2) hide show
  1. package/bin/team-context.js +147 -13
  2. package/package.json +1 -1
@@ -327,28 +327,134 @@ async function teamCreate() {
327
327
  }
328
328
 
329
329
  async function teamJoin(args) {
330
- const teamId = args[0];
330
+ const input = args[0];
331
+ if (!input) {
332
+ console.error('Error: repo URL or path is required.');
333
+ console.error('Usage: wayfind team join <repo-url-or-path>');
334
+ console.error('Example: wayfind team join https://github.com/acme/team-context');
335
+ process.exit(1);
336
+ }
337
+
338
+ // Determine if input is a URL to clone or a local path
339
+ const isUrl = /^https?:\/\/|^git@|^github\.com\//.test(input);
340
+ let repoPath;
341
+
342
+ if (isUrl) {
343
+ // Parse org/repo from URL for clone destination suggestion
344
+ const urlMatch = input.match(/[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/);
345
+ if (!urlMatch) {
346
+ console.error(`Could not parse org/repo from URL: ${input}`);
347
+ process.exit(1);
348
+ }
349
+ const [, org, repo] = urlMatch;
350
+ const orgDir = path.join(HOME, 'repos', org);
351
+ const suggested = fs.existsSync(orgDir)
352
+ ? path.join(orgDir, repo)
353
+ : path.join(HOME, '.claude', 'team-context', repo);
354
+
355
+ let dest = suggested;
356
+ if (fs.existsSync(suggested)) {
357
+ console.log(`\nRepo already cloned at: ${suggested}`);
358
+ const useExisting = await ask(`Use existing clone? [Y/n]: `);
359
+ if (useExisting.toLowerCase() === 'n') {
360
+ const custom = await ask(`Clone to [${suggested}]: `);
361
+ dest = custom ? path.resolve(custom.replace(/^~/, HOME)) : suggested;
362
+ }
363
+ } else {
364
+ const confirm = await ask(`\nClone to ${suggested}? [Y/n]: `);
365
+ if (confirm.toLowerCase() === 'n') {
366
+ const custom = await ask(`Clone to: `);
367
+ if (!custom) { console.error('Destination required.'); process.exit(1); }
368
+ dest = path.resolve(custom.replace(/^~/, HOME));
369
+ }
370
+ console.log(`Cloning ${input}...`);
371
+ const cloneUrl = /^https?:\/\//.test(input) ? input : `https://${input}`;
372
+ const result = spawnSync('git', ['clone', cloneUrl, dest], { stdio: 'inherit' });
373
+ if (result.status !== 0) {
374
+ console.error('Clone failed.');
375
+ process.exit(1);
376
+ }
377
+ }
378
+ repoPath = dest;
379
+ } else {
380
+ repoPath = path.resolve(input.replace(/^~/, HOME));
381
+ if (!fs.existsSync(repoPath)) {
382
+ console.error(`Directory not found: ${repoPath}`);
383
+ process.exit(1);
384
+ }
385
+ }
386
+
387
+ // Read wayfind.json from the repo
388
+ const sharedConfig = readJSONFile(path.join(repoPath, 'wayfind.json')) || {};
389
+ const teamId = sharedConfig.team_id;
331
390
  if (!teamId) {
332
- console.error('Error: team ID is required.');
333
- console.error('Usage: wayfind team join <team-id>');
391
+ console.error('');
392
+ console.error(`Error: wayfind.json in that repo has no team_id.`);
393
+ console.error('Ask your team admin to run:');
394
+ console.error(` wayfind context add <team-id> ${repoPath}`);
334
395
  process.exit(1);
335
396
  }
397
+ const teamName = sharedConfig.team_name || teamId;
398
+ const containerEndpoint = sharedConfig.container_endpoint || null;
336
399
 
337
- const team = {
338
- teamId,
339
- joined: new Date().toISOString(),
400
+ // Register in local context.json
401
+ const config = readContextConfig();
402
+ if (!config.teams) config.teams = {};
403
+ const existing = config.teams[teamId];
404
+ config.teams[teamId] = {
405
+ path: repoPath,
406
+ name: teamName,
407
+ configured_at: new Date().toISOString(),
408
+ ...(containerEndpoint ? { container_endpoint: containerEndpoint } : {}),
409
+ ...(existing && existing.bound_repos ? { bound_repos: existing.bound_repos } : {}),
340
410
  };
411
+ if (!config.default) config.default = teamId;
412
+ writeContextConfig(config);
341
413
 
342
- writeJSONFile(TEAM_FILE, team);
414
+ // Check API key status
415
+ const keyFile = path.join(repoPath, '.wayfind-api-key');
416
+ const keyReady = fs.existsSync(keyFile) && (() => {
417
+ try { return fs.readFileSync(keyFile, 'utf8').trim().length >= 32; } catch { return false; }
418
+ })();
419
+
420
+ // Print confirmation
421
+ console.log('');
422
+ console.log(`Joined team '${teamName}' (${teamId})`);
423
+ console.log(` Repo: ${repoPath}`);
424
+ if (containerEndpoint) {
425
+ console.log(` Semantic search: available | ${containerEndpoint}`);
426
+ } else {
427
+ console.log(` Semantic search: not configured`);
428
+ console.log(` Ask your team admin: wayfind deploy set-endpoint ${teamId} <url>`);
429
+ }
430
+ if (keyReady) {
431
+ console.log(` Search API key: ready — rotates daily, committed to team repo`);
432
+ } else {
433
+ console.log(` Search API key: pending — will appear after the container's first key rotation`);
434
+ console.log(` Run \`git pull\` in ${repoPath} after the container starts`);
435
+ }
436
+ console.log('');
437
+ console.log('How the search key works:');
438
+ console.log(' The container rotates this key every 24 hours and commits it to the team repo.');
439
+ console.log(' Your Claude Code sessions read the latest key automatically from the cloned repo.');
440
+ console.log(' You never need to manage it — just keep the repo up to date (git pull).');
343
441
  console.log('');
344
- console.log(`Joined team ${teamId}.`);
442
+ console.log('Next: bind your repos to this team with:');
443
+ console.log(` wayfind context bind ${teamId} (run from each repo you work in)`);
345
444
 
445
+ if (existing) {
446
+ console.log('');
447
+ console.log(` (Updated existing registration for team ${teamId})`);
448
+ }
449
+
450
+ // Register profile in team directory
346
451
  const profile = readJSONFile(PROFILE_FILE);
347
452
  if (profile) {
348
453
  syncMemberToRegistry(profile, teamId);
349
454
  await announceToSlack(profile, teamId);
350
455
  } else {
351
- console.log(" Run 'wayfind whoami --setup' to register in the team directory.");
456
+ console.log('');
457
+ console.log("Run 'wayfind whoami --setup' to register your profile in the team directory.");
352
458
  }
353
459
  console.log('');
354
460
  }
@@ -3549,9 +3655,20 @@ function contextAdd(args) {
3549
3655
  const config = readContextConfig();
3550
3656
  if (!config.teams) config.teams = {};
3551
3657
 
3552
- // Try to read team name from the repo's wayfind.json
3553
- const sharedConfig = readJSONFile(path.join(resolved, 'wayfind.json')) || {};
3658
+ // Read and update wayfind.json in the repo — write team_id/team_name so joiners
3659
+ // can read them without needing to know the ID out of band
3660
+ const sharedConfigPath = path.join(resolved, 'wayfind.json');
3661
+ const sharedConfig = readJSONFile(sharedConfigPath) || {};
3554
3662
  const teamName = sharedConfig.team_name || teamId;
3663
+ if (!sharedConfig.team_id || !sharedConfig.team_name) {
3664
+ sharedConfig.team_id = teamId;
3665
+ sharedConfig.team_name = teamName;
3666
+ try {
3667
+ fs.writeFileSync(sharedConfigPath, JSON.stringify(sharedConfig, null, 2) + '\n');
3668
+ } catch (err) {
3669
+ console.error(`Warning: could not write wayfind.json: ${err.message}`);
3670
+ }
3671
+ }
3555
3672
 
3556
3673
  config.teams[teamId] = {
3557
3674
  path: resolved,
@@ -3566,6 +3683,23 @@ function contextAdd(args) {
3566
3683
  if (Object.keys(config.teams).length === 1) {
3567
3684
  console.log(' Set as default (only team).');
3568
3685
  }
3686
+
3687
+ // Generate first search API key if one doesn't exist yet
3688
+ const keyFile = path.join(resolved, '.wayfind-api-key');
3689
+ if (!fs.existsSync(keyFile)) {
3690
+ const key = crypto.randomBytes(32).toString('hex');
3691
+ try {
3692
+ fs.writeFileSync(keyFile, key + '\n', 'utf8');
3693
+ // Resolve token for git push (CLI context — use gh CLI)
3694
+ const token = detectGitHubToken();
3695
+ if (token && !process.env.GITHUB_TOKEN) process.env.GITHUB_TOKEN = token;
3696
+ pushApiKey(resolved);
3697
+ console.log(' Generated initial search API key → committed to team repo.');
3698
+ console.log(' Teammates who join will read it automatically. It rotates daily.');
3699
+ } catch (err) {
3700
+ console.error(` Warning: could not generate API key: ${err.message}`);
3701
+ }
3702
+ }
3569
3703
  }
3570
3704
 
3571
3705
  function contextBind(args) {
@@ -5932,8 +6066,8 @@ function showHelp() {
5932
6066
  console.log(' /doctor Check installation health');
5933
6067
  console.log('');
5934
6068
  console.log('Team setup:');
5935
- console.log(' wayfind team create Create a new team');
5936
- console.log(' wayfind team join <id> Join an existing team');
6069
+ console.log(' wayfind team create Create a new team');
6070
+ console.log(' wayfind team join <repo-url-or-path> Join an existing team');
5937
6071
  console.log(' wayfind team status Show current team info');
5938
6072
  console.log(' wayfind whoami Show your profile');
5939
6073
  console.log(' wayfind whoami --setup Set up your profile and personas');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wayfind",
3
- "version": "2.0.47",
3
+ "version": "2.0.48",
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",