wayfind 2.0.44 → 2.0.45

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.
@@ -3812,17 +3812,25 @@ async function runDeploy(args) {
3812
3812
  }
3813
3813
 
3814
3814
  /**
3815
- * Scaffold a per-team container config at ~/.claude/team-context/teams/<teamId>/deploy/
3815
+ * Scaffold a per-team container config in the team's registered repo (deploy/ subdir).
3816
+ * Falls back to ~/.claude/team-context/teams/<teamId>/deploy/ if no repo is registered.
3816
3817
  */
3817
3818
  function deployTeamInit(teamId, { port } = {}) {
3818
- const teamsBaseDir = HOME ? path.join(HOME, '.claude', 'team-context', 'teams') : null;
3819
- if (!teamsBaseDir) {
3819
+ if (!HOME) {
3820
3820
  console.error('Cannot resolve home directory.');
3821
3821
  process.exit(1);
3822
3822
  }
3823
3823
 
3824
- const deployDir = path.join(teamsBaseDir, teamId, 'deploy');
3825
- const storeDir = path.join(teamsBaseDir, teamId, 'content-store');
3824
+ // Resolve deploy dir: team repo path first, fallback to store-adjacent
3825
+ const config = readContextConfig();
3826
+ const teamEntry = config.teams && config.teams[teamId];
3827
+ const teamContextPath = teamEntry ? teamEntry.path : null;
3828
+ const deployDir = teamContextPath
3829
+ ? path.join(teamContextPath, 'deploy')
3830
+ : path.join(HOME, '.claude', 'team-context', 'teams', teamId, 'deploy');
3831
+
3832
+ // Ensure the per-team store dir exists
3833
+ const storeDir = path.join(HOME, '.claude', 'team-context', 'teams', teamId, 'content-store');
3826
3834
 
3827
3835
  // Check for duplicate running container
3828
3836
  const psResult = spawnSync('docker', ['ps', '--filter', `label=com.wayfind.team=${teamId}`, '--format', '{{.Names}}'], { stdio: 'pipe' });
@@ -3838,22 +3846,42 @@ function deployTeamInit(teamId, { port } = {}) {
3838
3846
  console.log(`Scaffolding deploy config for team: ${teamId}`);
3839
3847
  console.log(`Deploy dir: ${deployDir}`);
3840
3848
 
3841
- // Resolve team-context repo path for volume mount
3842
- const config = readContextConfig();
3843
- const teamEntry = config.teams && config.teams[teamId];
3844
- const teamContextPath = teamEntry ? teamEntry.path : null;
3845
-
3846
- // Assign port (default 3141; if taken, user should pass --port)
3847
- const assignedPort = port || 3141;
3849
+ // Auto-detect port: find all wayfind containers, pick next available
3848
3850
  const containerName = `wayfind-${teamId}`;
3851
+ let assignedPort = port;
3852
+ if (!assignedPort) {
3853
+ const usedPorts = new Set();
3854
+ // Check labeled containers
3855
+ const portsResult = spawnSync('docker', [
3856
+ 'ps', '--filter', 'label=com.wayfind.team',
3857
+ '--format', '{{.Ports}}',
3858
+ ], { stdio: 'pipe' });
3859
+ // Also check legacy container named "wayfind"
3860
+ const legacyPortsResult = spawnSync('docker', [
3861
+ 'ps', '--filter', 'name=^wayfind',
3862
+ '--format', '{{.Ports}}',
3863
+ ], { stdio: 'pipe' });
3864
+ const allPortOutput = [
3865
+ (portsResult.stdout || '').toString(),
3866
+ (legacyPortsResult.stdout || '').toString(),
3867
+ ].join('\n');
3868
+ // Extract host ports from "0.0.0.0:3141->3141/tcp" patterns
3869
+ for (const match of allPortOutput.matchAll(/:(\d+)->/g)) {
3870
+ usedPorts.add(parseInt(match[1], 10));
3871
+ }
3872
+ assignedPort = 3141;
3873
+ while (usedPorts.has(assignedPort)) assignedPort++;
3874
+ if (assignedPort !== 3141) {
3875
+ console.log(`Port 3141 in use — assigning port ${assignedPort}`);
3876
+ }
3877
+ }
3849
3878
 
3850
3879
  // Build docker-compose.yml content with per-team overrides
3851
3880
  const templatePath = path.join(DEPLOY_TEMPLATES_DIR, 'docker-compose.yml');
3852
3881
  let composeContent = fs.readFileSync(templatePath, 'utf8');
3853
3882
  composeContent = composeContent
3854
3883
  .replace(/container_name: wayfind/, `container_name: ${containerName}`)
3855
- .replace(/- "3141:3141"/, `- "${assignedPort}:3141"`)
3856
- .replace(/(TEAM_CONTEXT_TENANT_ID:.*$)/m, `TEAM_CONTEXT_TENANT_ID: \${TEAM_CONTEXT_TENANT_ID:-${teamId}}`);
3884
+ .replace(/- "3141:3141"/, `- "${assignedPort}:3141"`);
3857
3885
 
3858
3886
  // Inject Docker label for discovery
3859
3887
  composeContent = composeContent.replace(
@@ -3869,42 +3897,63 @@ function deployTeamInit(teamId, { port } = {}) {
3869
3897
  console.log(' docker-compose.yml — already exists, skipping');
3870
3898
  }
3871
3899
 
3872
- // .env.example
3900
+ // .env.example — full reference with all options
3873
3901
  const envExampleSrc = path.join(DEPLOY_TEMPLATES_DIR, '.env.example');
3874
3902
  const envExampleDst = path.join(deployDir, '.env.example');
3875
3903
  if (!fs.existsSync(envExampleDst) && fs.existsSync(envExampleSrc)) {
3876
- let envContent = fs.readFileSync(envExampleSrc, 'utf8');
3877
- if (teamContextPath) {
3878
- envContent += `\nTEAM_CONTEXT_TEAM_CONTEXT_PATH=${teamContextPath}\n`;
3879
- }
3880
- fs.writeFileSync(envExampleDst, envContent, 'utf8');
3881
- console.log(' .env.example — created');
3904
+ fs.copyFileSync(envExampleSrc, envExampleDst);
3905
+ console.log(' .env.example — created (full reference)');
3882
3906
  }
3883
3907
 
3884
- // .env from .env.example
3908
+ // .env minimal seed with only required keys
3885
3909
  const envPath = path.join(deployDir, '.env');
3886
- if (!fs.existsSync(envPath) && fs.existsSync(envExampleDst)) {
3887
- fs.copyFileSync(envExampleDst, envPath);
3888
- console.log(' .env created from .env.example (fill in your tokens)');
3889
- }
3890
-
3891
- const ghToken = detectGitHubToken();
3892
- if (ghToken && fs.existsSync(envPath)) {
3893
- let envContent = fs.readFileSync(envPath, 'utf8');
3894
- if (!envContent.match(/^GITHUB_TOKEN=.+/m)) {
3895
- envContent = envContent.replace(/^GITHUB_TOKEN=.*$/m, `GITHUB_TOKEN=${ghToken}`);
3896
- if (!envContent.includes('GITHUB_TOKEN=')) envContent += `\nGITHUB_TOKEN=${ghToken}\n`;
3897
- fs.writeFileSync(envPath, envContent, 'utf8');
3910
+ if (!fs.existsSync(envPath)) {
3911
+ const ghToken = detectGitHubToken();
3912
+ const lines = [
3913
+ '# Wayfind — required configuration',
3914
+ '# See .env.example for all available options.',
3915
+ '',
3916
+ '# Anthropic API key (for digests and bot answers)',
3917
+ 'ANTHROPIC_API_KEY=sk-ant-your-key',
3918
+ '',
3919
+ '# GitHub token (for pulling team journals and signals)',
3920
+ `GITHUB_TOKEN=${ghToken || ''}`,
3921
+ '',
3922
+ `TEAM_CONTEXT_TENANT_ID=${teamId}`,
3923
+ ];
3924
+ // Set volume mount path so docker-compose.yml resolves correctly
3925
+ if (teamContextPath) {
3926
+ lines.push(`TEAM_CONTEXT_TEAM_CONTEXT_PATH=${teamContextPath}`);
3927
+ }
3928
+ fs.writeFileSync(envPath, lines.join('\n') + '\n', 'utf8');
3929
+ console.log(' .env — created (fill in ANTHROPIC_API_KEY)');
3930
+ if (ghToken) {
3898
3931
  console.log(' GITHUB_TOKEN — auto-detected from gh CLI');
3899
3932
  }
3933
+ } else {
3934
+ console.log(' .env — already exists, skipping');
3935
+ }
3936
+
3937
+ // Ensure deploy/.env is gitignored if we're in a repo
3938
+ if (teamContextPath) {
3939
+ const gitignorePath = path.join(teamContextPath, '.gitignore');
3940
+ const gitignoreEntry = 'deploy/.env';
3941
+ if (fs.existsSync(gitignorePath)) {
3942
+ const content = fs.readFileSync(gitignorePath, 'utf8');
3943
+ if (!content.includes(gitignoreEntry)) {
3944
+ fs.appendFileSync(gitignorePath, `\n${gitignoreEntry}\n`);
3945
+ console.log(' .gitignore — added deploy/.env');
3946
+ }
3947
+ }
3900
3948
  }
3901
3949
 
3902
3950
  console.log('');
3903
3951
  console.log('Next steps:');
3904
- console.log(` 1. Fill in ${deployDir}/.env with your tokens`);
3952
+ console.log(` 1. Set ANTHROPIC_API_KEY in ${deployDir}/.env`);
3905
3953
  console.log(` 2. cd "${deployDir}" && docker compose up -d`);
3906
3954
  console.log(` 3. Verify: curl http://localhost:${assignedPort}/healthz`);
3907
3955
  console.log('');
3956
+ console.log('See .env.example for optional config (Slack, embeddings, signals, schedules).');
3908
3957
  console.log(`Tip: run "wayfind deploy list" to see all running team containers.`);
3909
3958
 
3910
3959
  telemetry.capture('deploy_team_init', { teamId }, CLI_USER);
@@ -3928,7 +3977,7 @@ function deployList() {
3928
3977
  const rows = (psResult.stdout || '').toString().trim();
3929
3978
  if (!rows) {
3930
3979
  console.log('No Wayfind team containers running.');
3931
- console.log('Start one with: wayfind deploy --team <teamId> && cd ~/.claude/team-context/teams/<teamId>/deploy && docker compose up -d');
3980
+ console.log('Start one with: wayfind deploy --team <teamId>');
3932
3981
  return;
3933
3982
  }
3934
3983
 
@@ -4170,8 +4219,10 @@ async function runStart() {
4170
4219
 
4171
4220
  // Validate required env vars before proceeding
4172
4221
  const missing = [];
4173
- if (!process.env.SLACK_BOT_TOKEN) missing.push('SLACK_BOT_TOKEN');
4174
- if (!process.env.SLACK_APP_TOKEN) missing.push('SLACK_APP_TOKEN');
4222
+ // Slack tokens are only required for modes that run the bot
4223
+ const needsSlack = ['bot', 'all-in-one'].includes(mode) && !process.env.TEAM_CONTEXT_NO_SLACK;
4224
+ if (needsSlack && !process.env.SLACK_BOT_TOKEN) missing.push('SLACK_BOT_TOKEN');
4225
+ if (needsSlack && !process.env.SLACK_APP_TOKEN) missing.push('SLACK_APP_TOKEN');
4175
4226
  if (!process.env.ANTHROPIC_API_KEY) missing.push('ANTHROPIC_API_KEY');
4176
4227
  if (missing.length > 0) {
4177
4228
  console.error('');
@@ -4181,6 +4232,8 @@ async function runStart() {
4181
4232
  console.error(' cp deploy/.env.example deploy/.env');
4182
4233
  console.error(' # Fill in your tokens, then: docker compose up -d');
4183
4234
  console.error('');
4235
+ console.error('Tip: set TEAM_CONTEXT_NO_SLACK=1 to run without Slack integration.');
4236
+ console.error('');
4184
4237
  process.exit(1);
4185
4238
  }
4186
4239
 
@@ -5181,13 +5234,13 @@ const COMMANDS = {
5181
5234
  if (labelDir && fs.existsSync(path.join(labelDir, 'docker-compose.yml'))) {
5182
5235
  composeDir = labelDir;
5183
5236
  } else {
5184
- // For per-team containers, check teams dir
5185
- const teamsBase = HOME ? path.join(HOME, '.claude', 'team-context', 'teams') : '';
5186
- if (teamsBase && fs.existsSync(teamsBase)) {
5187
- for (const tid of fs.readdirSync(teamsBase)) {
5188
- const candidate = path.join(teamsBase, tid, 'deploy');
5237
+ // Check team repo paths from context.json first
5238
+ const updateConfig = readContextConfig();
5239
+ if (updateConfig.teams) {
5240
+ for (const [tid, entry] of Object.entries(updateConfig.teams)) {
5241
+ if (!entry.path) continue;
5242
+ const candidate = path.join(entry.path, 'deploy');
5189
5243
  if (fs.existsSync(path.join(candidate, 'docker-compose.yml'))) {
5190
- // Check if this compose file manages this container
5191
5244
  const checkResult = spawnSync('docker', ['compose', 'ps', '--format', '{{.Name}}'], { cwd: candidate, stdio: 'pipe' });
5192
5245
  const composeContainers = (checkResult.stdout || '').toString();
5193
5246
  if (composeContainers.includes(containerName)) {
@@ -5197,6 +5250,23 @@ const COMMANDS = {
5197
5250
  }
5198
5251
  }
5199
5252
  }
5253
+ // Fallback: check store-adjacent deploy dirs
5254
+ if (!composeDir) {
5255
+ const teamsBase = HOME ? path.join(HOME, '.claude', 'team-context', 'teams') : '';
5256
+ if (teamsBase && fs.existsSync(teamsBase)) {
5257
+ for (const tid of fs.readdirSync(teamsBase)) {
5258
+ const candidate = path.join(teamsBase, tid, 'deploy');
5259
+ if (fs.existsSync(path.join(candidate, 'docker-compose.yml'))) {
5260
+ const checkResult = spawnSync('docker', ['compose', 'ps', '--format', '{{.Name}}'], { cwd: candidate, stdio: 'pipe' });
5261
+ const composeContainers = (checkResult.stdout || '').toString();
5262
+ if (composeContainers.includes(containerName)) {
5263
+ composeDir = candidate;
5264
+ break;
5265
+ }
5266
+ }
5267
+ }
5268
+ }
5269
+ }
5200
5270
  // Legacy fallback
5201
5271
  if (!composeDir) {
5202
5272
  const legacyCandidates = [process.cwd(), path.join(HOME || '', 'team-context', 'deploy')];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wayfind",
3
- "version": "2.0.44",
3
+ "version": "2.0.45",
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",
@@ -1,14 +1,11 @@
1
1
  # Wayfind — Docker environment configuration
2
2
  # Copy to .env and fill in your values: cp .env.example .env
3
+ #
4
+ # Only ANTHROPIC_API_KEY and GITHUB_TOKEN are required.
5
+ # Everything else is optional — add as needed.
3
6
 
4
7
  # ── Required ──────────────────────────────────────────────────────────────────
5
8
 
6
- # Slack bot tokens — from the Slack app you created using slack-app-manifest.json.
7
- # Go to api.slack.com/apps → your Wayfind app → OAuth & Permissions for the bot token,
8
- # and Basic Information → App-Level Tokens for the app token.
9
- SLACK_BOT_TOKEN=xoxb-your-bot-token
10
- SLACK_APP_TOKEN=xapp-your-app-token
11
-
12
9
  # Anthropic API key (for digests and bot answers)
13
10
  # Get one at console.anthropic.com
14
11
  ANTHROPIC_API_KEY=sk-ant-your-key
@@ -18,73 +15,33 @@ ANTHROPIC_API_KEY=sk-ant-your-key
18
15
  # Needs read access to the team-context repo.
19
16
  GITHUB_TOKEN=
20
17
 
21
- # ── Digest delivery ───────────────────────────────────────────────────────────
22
- # Primary: bot token + channel. The bot posts digests via chat.postMessage,
23
- # which enables reaction tracking and threaded feedback.
24
- # Fallback: webhook. Used only if bot delivery fails.
18
+ # ── Slack (for digest delivery and bot) ──────────────────────────────────────
19
+ # Without these, set TEAM_CONTEXT_NO_SLACK=1 and the container runs in
20
+ # scheduler/worker-only mode (no digest posting, no bot answers).
25
21
 
26
- # Slack channel for digest delivery (channel ID, not name)
27
- # Right-click channel in Slack → View channel details → copy the ID at bottom
22
+ # SLACK_BOT_TOKEN=xoxb-your-bot-token
23
+ # SLACK_APP_TOKEN=xapp-your-app-token
28
24
  # SLACK_DIGEST_CHANNEL=C0123456789
29
-
30
- # Slack webhook — fallback for digest delivery if bot token is unavailable
31
25
  # TEAM_CONTEXT_SLACK_WEBHOOK=https://hooks.slack.com/services/T.../B.../...
32
26
 
33
- # ── Optional ──────────────────────────────────────────────────────────────────
34
-
35
- # Tenant identifier (prefixes storage paths)
36
- # TEAM_CONTEXT_TENANT_ID=my-team
37
-
38
- # Team repo allowlist — only journals from these repos appear in digests and queries.
39
- # Supports org/* wildcards. This is the recommended way to scope a container to one team.
40
- # TEAM_CONTEXT_INCLUDE_REPOS=MyOrg/*,MyOrg-Libs/*
41
-
42
- # DEPRECATED: Blocklist approach. Use INCLUDE_REPOS instead.
43
- # TEAM_CONTEXT_EXCLUDE_REPOS=wayfind,personal-project
44
-
45
- # Encryption key — generate with: openssl rand -base64 32
46
- # TEAM_CONTEXT_ENCRYPTION_KEY=
47
-
48
- # Author slug for CLI telemetry attribution
49
- # TEAM_CONTEXT_AUTHOR=greg
50
-
51
- # ── LLM models ───────────────────────────────────────────────────────────────
52
-
53
- # Model for digest generation and onboarding packs (default: claude-sonnet-4-5-20250929)
54
- # TEAM_CONTEXT_LLM_MODEL=claude-sonnet-4-5-20250929
55
-
56
- # Model for conversation transcript extraction (default: claude-sonnet-4-5-20250929)
57
- # TEAM_CONTEXT_EXTRACTION_MODEL=claude-sonnet-4-5-20250929
58
-
59
- # ── Embeddings (without these, bot uses keyword search only) ─────────────────
27
+ # ── Embeddings (for semantic search — falls back to keyword search without) ──
60
28
  # Option A: OpenAI
61
29
  # OPENAI_API_KEY=sk-your-openai-key
62
- # Option B: Azure OpenAI (if you have an AOAI deployment)
30
+ # Option B: Azure OpenAI
63
31
  # AZURE_OPENAI_EMBEDDING_ENDPOINT=https://your-resource.openai.azure.com/
64
32
  # AZURE_OPENAI_EMBEDDING_KEY=your-key
65
33
  # AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small
66
34
 
67
- # ── Path overrides ───────────────────────────────────────────────────────────
68
-
69
- # Override content store path (default: ~/.claude/team-context/content-store)
70
- # TEAM_CONTEXT_STORE_PATH=/data/content-store
71
-
72
- # Override signals directory (default: ~/.claude/team-context/signals)
73
- # TEAM_CONTEXT_SIGNALS_DIR=/data/signals
35
+ # ── Team scoping ─────────────────────────────────────────────────────────────
74
36
 
75
- # Override conversations directory (default: ~/.claude/projects)
76
- # TEAM_CONTEXT_CONVERSATIONS_DIR=/data/conversations
77
-
78
- # ── Schedules ────────────────────────────────────────────────────────────────
79
-
80
- # Digest schedule (cron, default: daily 12pm UTC / 7am ET)
81
- # TEAM_CONTEXT_DIGEST_SCHEDULE=0 12 * * *
37
+ # Tenant identifier (prefixes storage paths)
38
+ # TEAM_CONTEXT_TENANT_ID=my-team
82
39
 
83
- # Signal pull schedule (cron, default: daily 6am UTC)
84
- # TEAM_CONTEXT_SIGNAL_SCHEDULE=0 6 * * *
40
+ # Team repo allowlist only journals from these repos appear in digests/queries.
41
+ # TEAM_CONTEXT_INCLUDE_REPOS=MyOrg/*,MyOrg-Libs/*
85
42
 
86
- # Reindex schedule (cron, default: hourly)
87
- # TEAM_CONTEXT_REINDEX_SCHEDULE=0 * * * *
43
+ # Author slug for telemetry attribution
44
+ # TEAM_CONTEXT_AUTHOR=greg
88
45
 
89
46
  # ── Signal sources ───────────────────────────────────────────────────────────
90
47
 
@@ -93,21 +50,28 @@ GITHUB_TOKEN=
93
50
 
94
51
  # Intercom API token for support signal ingestion
95
52
  # INTERCOM_TOKEN=your-intercom-token
96
-
97
- # Filter Intercom conversations by tags (comma-separated)
98
53
  # TEAM_CONTEXT_INTERCOM_TAGS=bug,feature-request
99
54
 
100
55
  # Notion integration token for page/database signal ingestion
101
- # Create at: https://www.notion.so/my-integrations
102
56
  # NOTION_TOKEN=ntn_your-integration-token
103
-
104
- # Specific Notion database IDs to monitor (comma-separated, or blank for all shared pages)
105
57
  # TEAM_CONTEXT_NOTION_DATABASES=db_id1,db_id2
106
58
 
107
- # ── Monitoring ───────────────────────────────────────────────────────────────
59
+ # ── Schedules ────────────────────────────────────────────────────────────────
108
60
 
109
- # Health check port (default: 3141)
110
- # TEAM_CONTEXT_HEALTH_PORT=3141
61
+ # TEAM_CONTEXT_DIGEST_SCHEDULE=0 12 * * *
62
+ # TEAM_CONTEXT_SIGNAL_SCHEDULE=0 6 * * *
63
+ # TEAM_CONTEXT_REINDEX_SCHEDULE=0 * * * *
111
64
 
112
- # Telemetry opt-in anonymous usage data to help improve Wayfind
65
+ # ── Advanced ─────────────────────────────────────────────────────────────────
66
+
67
+ # Set to 1 to run without Slack (scheduler + worker only, no bot)
68
+ # TEAM_CONTEXT_NO_SLACK=1
69
+
70
+ # TEAM_CONTEXT_LLM_MODEL=claude-sonnet-4-5-20250929
71
+ # TEAM_CONTEXT_EXTRACTION_MODEL=claude-sonnet-4-5-20250929
72
+ # TEAM_CONTEXT_ENCRYPTION_KEY=
73
+ # TEAM_CONTEXT_HEALTH_PORT=3141
113
74
  # TEAM_CONTEXT_TELEMETRY=true
75
+ # TEAM_CONTEXT_STORE_PATH=/data/content-store
76
+ # TEAM_CONTEXT_SIGNALS_DIR=/data/signals
77
+ # TEAM_CONTEXT_CONVERSATIONS_DIR=/data/conversations
@@ -7,52 +7,9 @@ services:
7
7
  image: ghcr.io/usewayfind/wayfind:latest
8
8
  container_name: wayfind
9
9
  restart: unless-stopped
10
+ env_file: .env
10
11
  environment:
11
12
  TEAM_CONTEXT_MODE: all-in-one
12
- TEAM_CONTEXT_TENANT_ID: ${TEAM_CONTEXT_TENANT_ID:-my-team}
13
- TEAM_CONTEXT_AUTHOR: ${TEAM_CONTEXT_AUTHOR:-}
14
-
15
- # Slack
16
- SLACK_BOT_TOKEN: ${SLACK_BOT_TOKEN}
17
- SLACK_APP_TOKEN: ${SLACK_APP_TOKEN}
18
- SLACK_DIGEST_CHANNEL: ${SLACK_DIGEST_CHANNEL:-}
19
- TEAM_CONTEXT_SLACK_WEBHOOK: ${TEAM_CONTEXT_SLACK_WEBHOOK:-}
20
-
21
- # Team repo allowlist (recommended over EXCLUDE_REPOS)
22
- TEAM_CONTEXT_INCLUDE_REPOS: ${TEAM_CONTEXT_INCLUDE_REPOS:-}
23
- # DEPRECATED: Blocklist approach — use INCLUDE_REPOS instead
24
- TEAM_CONTEXT_EXCLUDE_REPOS: ${TEAM_CONTEXT_EXCLUDE_REPOS:-}
25
-
26
- # Telemetry (opt-in, sends anonymous usage data to improve Wayfind)
27
- TEAM_CONTEXT_TELEMETRY: ${TEAM_CONTEXT_TELEMETRY:-false}
28
-
29
- # GitHub signals
30
- GITHUB_TOKEN: ${GITHUB_TOKEN:-}
31
-
32
- # Intercom signals
33
- INTERCOM_TOKEN: ${INTERCOM_TOKEN:-}
34
- TEAM_CONTEXT_INTERCOM_TAGS: ${TEAM_CONTEXT_INTERCOM_TAGS:-}
35
-
36
- # LLM
37
- ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
38
- TEAM_CONTEXT_LLM_MODEL: ${TEAM_CONTEXT_LLM_MODEL:-claude-sonnet-4-5-20250929}
39
-
40
- # Embeddings (for semantic search — falls back to keyword search without these)
41
- # Option A: OpenAI
42
- OPENAI_API_KEY: ${OPENAI_API_KEY:-}
43
- # Option B: Azure OpenAI
44
- AZURE_OPENAI_EMBEDDING_ENDPOINT: ${AZURE_OPENAI_EMBEDDING_ENDPOINT:-}
45
- AZURE_OPENAI_EMBEDDING_KEY: ${AZURE_OPENAI_EMBEDDING_KEY:-}
46
- AZURE_OPENAI_EMBEDDING_DEPLOYMENT: ${AZURE_OPENAI_EMBEDDING_DEPLOYMENT:-text-embedding-3-small}
47
-
48
- # Encryption — generate with: openssl rand -base64 32
49
- TEAM_CONTEXT_ENCRYPTION_KEY: ${TEAM_CONTEXT_ENCRYPTION_KEY:-}
50
-
51
- # Scheduling
52
- TEAM_CONTEXT_DIGEST_SCHEDULE: ${TEAM_CONTEXT_DIGEST_SCHEDULE:-0 8 * * 1}
53
- TEAM_CONTEXT_SIGNAL_SCHEDULE: ${TEAM_CONTEXT_SIGNAL_SCHEDULE:-0 6 * * *}
54
-
55
- # Team context repo (mounted at /data/team-context for git pull)
56
13
  TEAM_CONTEXT_TEAM_CONTEXT_DIR: /data/team-context
57
14
  TEAM_CONTEXT_JOURNALS_DIR: /data/team-context/journals
58
15
  volumes: