wayfind 2.0.29 → 2.0.31
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 +2 -2
- package/bin/distill.js +4 -5
- package/bin/memory-compare.js +171 -0
- package/bin/memory-report.sh +41 -0
- package/bin/team-context.js +38 -39
- package/package.json +1 -1
- package/plugin/scripts/session-end.sh +1 -1
- package/plugin/skills/init-folder/SKILL.md +93 -0
- package/plugin/skills/init-team/SKILL.md +8 -0
- package/plugin/skills/session-protocol/SKILL.md +2 -0
- package/specializations/claude-code/CLAUDE.md-global-fragment.md +6 -2
- package/specializations/claude-code/CLAUDE.md-repo-fragment.md +2 -0
- package/specializations/claude-code/commands/init-folder.md +91 -0
- package/specializations/claude-code/commands/init-team.md +8 -0
- package/specializations/claude-code/hooks/session-end.sh +1 -1
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
|
}
|
package/bin/distill.js
CHANGED
|
@@ -164,14 +164,13 @@ async function mergeEntries(entries, llmConfig, tier) {
|
|
|
164
164
|
const systemPrompt = MERGE_PROMPTS[tier] || MERGE_PROMPTS.daily;
|
|
165
165
|
const userContent = parts.join('\n\n---\n\n');
|
|
166
166
|
|
|
167
|
-
const
|
|
167
|
+
const config = {
|
|
168
168
|
provider: llmConfig.provider || 'anthropic',
|
|
169
169
|
model: llmConfig.model || 'claude-haiku-4-5-20251001',
|
|
170
|
-
|
|
171
|
-
system: systemPrompt,
|
|
172
|
-
messages: [{ role: 'user', content: userContent }],
|
|
170
|
+
api_key_env: llmConfig.api_key_env || 'ANTHROPIC_API_KEY',
|
|
173
171
|
max_tokens: 2000,
|
|
174
|
-
}
|
|
172
|
+
};
|
|
173
|
+
const result = await llm.call(config, systemPrompt, userContent);
|
|
175
174
|
|
|
176
175
|
// Extract title from first line or generate one
|
|
177
176
|
const firstEntry = entries[0].entry;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const HOME = process.env.HOME || process.env.USERPROFILE;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Compare Claude Code auto-memory vs Wayfind global memory.
|
|
9
|
+
* Shows overlap, unique content in each, and freshness.
|
|
10
|
+
*/
|
|
11
|
+
function compare() {
|
|
12
|
+
const autoMemoryRoot = path.join(HOME, '.claude', 'projects');
|
|
13
|
+
const wayfindMemory = path.join(HOME, '.claude', 'memory');
|
|
14
|
+
|
|
15
|
+
// Collect auto-memory entries
|
|
16
|
+
const autoEntries = [];
|
|
17
|
+
if (fs.existsSync(autoMemoryRoot)) {
|
|
18
|
+
for (const proj of fs.readdirSync(autoMemoryRoot)) {
|
|
19
|
+
const memDir = path.join(autoMemoryRoot, proj, 'memory');
|
|
20
|
+
if (!fs.existsSync(memDir)) continue;
|
|
21
|
+
for (const file of fs.readdirSync(memDir).filter(f => f.endsWith('.md') && f !== 'MEMORY.md')) {
|
|
22
|
+
const fp = path.join(memDir, file);
|
|
23
|
+
const content = fs.readFileSync(fp, 'utf8');
|
|
24
|
+
const typeMatch = content.match(/^type:\s*(.+)$/m);
|
|
25
|
+
const descMatch = content.match(/^description:\s*(.+)$/m);
|
|
26
|
+
const stat = fs.statSync(fp);
|
|
27
|
+
autoEntries.push({
|
|
28
|
+
project: proj,
|
|
29
|
+
file,
|
|
30
|
+
type: typeMatch ? typeMatch[1].trim() : 'unknown',
|
|
31
|
+
description: descMatch ? descMatch[1].trim() : '',
|
|
32
|
+
size: content.length,
|
|
33
|
+
modified: stat.mtime,
|
|
34
|
+
daysAgo: Math.floor((Date.now() - stat.mtime) / (1000 * 60 * 60 * 24)),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Collect Wayfind global memory entries
|
|
41
|
+
const wayfindEntries = [];
|
|
42
|
+
if (fs.existsSync(wayfindMemory)) {
|
|
43
|
+
for (const file of fs.readdirSync(wayfindMemory).filter(f => f.endsWith('.md'))) {
|
|
44
|
+
const fp = path.join(wayfindMemory, file);
|
|
45
|
+
const content = fs.readFileSync(fp, 'utf8');
|
|
46
|
+
const stat = fs.statSync(fp);
|
|
47
|
+
wayfindEntries.push({
|
|
48
|
+
file,
|
|
49
|
+
size: content.length,
|
|
50
|
+
modified: stat.mtime,
|
|
51
|
+
daysAgo: Math.floor((Date.now() - stat.mtime) / (1000 * 60 * 60 * 24)),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Journal stats
|
|
57
|
+
const journalDir = path.join(wayfindMemory, 'journal');
|
|
58
|
+
let journalCount = 0;
|
|
59
|
+
let journalSize = 0;
|
|
60
|
+
let latestJournal = null;
|
|
61
|
+
if (fs.existsSync(journalDir)) {
|
|
62
|
+
const journals = fs.readdirSync(journalDir).filter(f => f.endsWith('.md'));
|
|
63
|
+
journalCount = journals.length;
|
|
64
|
+
for (const j of journals) {
|
|
65
|
+
const fp = path.join(journalDir, j);
|
|
66
|
+
journalSize += fs.statSync(fp).size;
|
|
67
|
+
const stat = fs.statSync(fp);
|
|
68
|
+
if (!latestJournal || stat.mtime > latestJournal) latestJournal = stat.mtime;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Report
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log('=== Memory Systems Comparison ===');
|
|
75
|
+
console.log('');
|
|
76
|
+
|
|
77
|
+
console.log('Claude Code Auto-Memory (per-project):');
|
|
78
|
+
const byType = {};
|
|
79
|
+
for (const e of autoEntries) {
|
|
80
|
+
byType[e.type] = (byType[e.type] || 0) + 1;
|
|
81
|
+
}
|
|
82
|
+
console.log(` ${autoEntries.length} entries across ${new Set(autoEntries.map(e => e.project)).size} projects`);
|
|
83
|
+
for (const [type, count] of Object.entries(byType).sort((a, b) => b[1] - a[1])) {
|
|
84
|
+
console.log(` ${type}: ${count}`);
|
|
85
|
+
}
|
|
86
|
+
const recentAuto = autoEntries.filter(e => e.daysAgo <= 7);
|
|
87
|
+
const staleAuto = autoEntries.filter(e => e.daysAgo > 30);
|
|
88
|
+
console.log(` Fresh (≤7d): ${recentAuto.length} | Stale (>30d): ${staleAuto.length}`);
|
|
89
|
+
console.log('');
|
|
90
|
+
|
|
91
|
+
console.log('Wayfind Global Memory:');
|
|
92
|
+
console.log(` ${wayfindEntries.length} topic files`);
|
|
93
|
+
console.log(` ${journalCount} journal entries (${Math.round(journalSize / 1024)}KB)`);
|
|
94
|
+
const recentWf = wayfindEntries.filter(e => e.daysAgo <= 7);
|
|
95
|
+
const staleWf = wayfindEntries.filter(e => e.daysAgo > 30);
|
|
96
|
+
console.log(` Fresh (≤7d): ${recentWf.length} | Stale (>30d): ${staleWf.length}`);
|
|
97
|
+
if (latestJournal) {
|
|
98
|
+
const jDaysAgo = Math.floor((Date.now() - latestJournal) / (1000 * 60 * 60 * 24));
|
|
99
|
+
console.log(` Latest journal: ${jDaysAgo}d ago`);
|
|
100
|
+
}
|
|
101
|
+
console.log('');
|
|
102
|
+
|
|
103
|
+
// Overlap detection: look for similar topics
|
|
104
|
+
console.log('Potential Overlap (auto-memory projects with Wayfind topic files on same subject):');
|
|
105
|
+
let overlapCount = 0;
|
|
106
|
+
for (const auto of autoEntries) {
|
|
107
|
+
const keywords = auto.file.replace(/\.md$/, '').replace(/^(feedback|project|reference|user)_/, '').split(/[-_]/).filter(w => w.length > 2);
|
|
108
|
+
for (const wf of wayfindEntries) {
|
|
109
|
+
const wfWords = wf.file.replace(/\.md$/, '').split(/[-_]/).filter(w => w.length > 2);
|
|
110
|
+
const overlap = keywords.filter(k => wfWords.some(w => w.includes(k) || k.includes(w)));
|
|
111
|
+
if (overlap.length >= 2) {
|
|
112
|
+
console.log(` AUTO: ${auto.file} (${auto.project.slice(-30)}) <-> WF: ${wf.file}`);
|
|
113
|
+
overlapCount++;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (overlapCount === 0) console.log(' None detected');
|
|
118
|
+
console.log('');
|
|
119
|
+
|
|
120
|
+
// Secret scan across both memory systems
|
|
121
|
+
const SECRET_PATTERNS = [
|
|
122
|
+
/xoxb-[0-9A-Za-z-]+/, // Slack bot token
|
|
123
|
+
/xapp-[0-9A-Za-z-]+/, // Slack app token
|
|
124
|
+
/ghp_[0-9A-Za-z]+/, // GitHub PAT
|
|
125
|
+
/gho_[0-9A-Za-z]+/, // GitHub OAuth
|
|
126
|
+
/sk-[0-9A-Za-z]{20,}/, // OpenAI / generic secret key
|
|
127
|
+
/pat-na1-[0-9a-f-]+/, // HubSpot PAT
|
|
128
|
+
/ntn_[0-9A-Za-z]+/, // Notion token
|
|
129
|
+
/dG9r[0-9A-Za-z+/=]{20,}/, // Base64 "tok:" prefix (Intercom-style)
|
|
130
|
+
/AKIA[0-9A-Z]{16}/, // AWS access key
|
|
131
|
+
/\b[A-Za-z0-9_]{10,}\.[A-Za-z0-9_-]{20,}/, // Azure function key pattern (xxx.yyy)
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
console.log('Secret Scan:');
|
|
135
|
+
let secretsFound = 0;
|
|
136
|
+
const allFiles = [
|
|
137
|
+
...autoEntries.map(e => ({
|
|
138
|
+
path: path.join(autoMemoryRoot, e.project, 'memory', e.file),
|
|
139
|
+
label: `auto:${e.project.slice(-25)}/${e.file}`,
|
|
140
|
+
})),
|
|
141
|
+
...wayfindEntries.map(e => ({
|
|
142
|
+
path: path.join(wayfindMemory, e.file),
|
|
143
|
+
label: `wayfind:${e.file}`,
|
|
144
|
+
})),
|
|
145
|
+
];
|
|
146
|
+
for (const { path: fp, label } of allFiles) {
|
|
147
|
+
try {
|
|
148
|
+
const content = fs.readFileSync(fp, 'utf8');
|
|
149
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
150
|
+
const match = content.match(pattern);
|
|
151
|
+
if (match) {
|
|
152
|
+
// Skip if it's inside a code example or command template (has $ prefix or is in backtick-quoted env var reference)
|
|
153
|
+
const line = content.split('\n').find(l => l.includes(match[0])) || '';
|
|
154
|
+
if (line.includes('$') && !line.includes('`' + match[0])) continue;
|
|
155
|
+
console.log(` ⚠ ${label} — matches ${pattern.source.slice(0, 20)}...`);
|
|
156
|
+
secretsFound++;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch { /* skip unreadable */ }
|
|
161
|
+
}
|
|
162
|
+
if (secretsFound === 0) {
|
|
163
|
+
console.log(' Clean — no secrets detected in memory files');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (require.main === module) {
|
|
168
|
+
compare();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = { compare };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Daily memory systems comparison report — posts to Slack via bot token.
|
|
3
|
+
# Add to crontab: 43 8 * * * /home/greg/repos/greg/wayfind/bin/memory-report.sh
|
|
4
|
+
#
|
|
5
|
+
# Runs on host (needs access to ~/.claude/projects for auto-memory).
|
|
6
|
+
# Pulls SLACK_BOT_TOKEN from the wayfind container if not set locally.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
CHANNEL="${SLACK_MEMORY_REPORT_CHANNEL:-C0AHV3UUW67}"
|
|
11
|
+
DATE=$(date +%Y-%m-%d)
|
|
12
|
+
PATH="$HOME/.npm-global/bin:$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin:$PATH"
|
|
13
|
+
|
|
14
|
+
# Get Slack token from container if not in environment
|
|
15
|
+
if [ -z "${SLACK_BOT_TOKEN:-}" ]; then
|
|
16
|
+
SLACK_BOT_TOKEN=$(docker exec wayfind printenv SLACK_BOT_TOKEN 2>/dev/null || true)
|
|
17
|
+
fi
|
|
18
|
+
if [ -z "${SLACK_BOT_TOKEN:-}" ]; then
|
|
19
|
+
echo "[$DATE] No SLACK_BOT_TOKEN available — skipping report"
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Generate comparison
|
|
24
|
+
REPORT=$(node "$SCRIPT_DIR/memory-compare.js" 2>&1)
|
|
25
|
+
|
|
26
|
+
# Format for Slack
|
|
27
|
+
PAYLOAD=$(node -e "
|
|
28
|
+
const report = process.argv[1];
|
|
29
|
+
const date = process.argv[2];
|
|
30
|
+
const msg = ':brain: *Daily Memory Systems Report — ' + date + '*\n\`\`\`\n' + report.replace(/=== Memory Systems Comparison ===\n\n/, '') + '\n\`\`\`';
|
|
31
|
+
const body = JSON.stringify({ channel: process.argv[3], text: msg });
|
|
32
|
+
process.stdout.write(body);
|
|
33
|
+
" "$REPORT" "$DATE" "$CHANNEL")
|
|
34
|
+
|
|
35
|
+
# Post to Slack
|
|
36
|
+
curl -s -X POST https://slack.com/api/chat.postMessage \
|
|
37
|
+
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
|
|
38
|
+
-H "Content-Type: application/json" \
|
|
39
|
+
-d "$PAYLOAD" > /dev/null
|
|
40
|
+
|
|
41
|
+
echo "[$DATE] Memory report posted to Slack"
|
package/bin/team-context.js
CHANGED
|
@@ -16,44 +16,7 @@ if (!HOME) {
|
|
|
16
16
|
|
|
17
17
|
const WAYFIND_DIR = process.env.WAYFIND_DIR || path.join(HOME, '.claude', 'team-context');
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
// Honor old MERIDIAN_* env vars with deprecation warning. Remove in v3.0.
|
|
21
|
-
const ENV_VAR_MIGRATION = {
|
|
22
|
-
MERIDIAN_TELEMETRY: 'TEAM_CONTEXT_TELEMETRY',
|
|
23
|
-
MERIDIAN_AUTHOR: 'TEAM_CONTEXT_AUTHOR',
|
|
24
|
-
MERIDIAN_TENANT_ID: 'TEAM_CONTEXT_TENANT_ID',
|
|
25
|
-
MERIDIAN_TEAM_CONTEXT_DIR: 'TEAM_CONTEXT_DIR',
|
|
26
|
-
MERIDIAN_JOURNALS_DIR: 'TEAM_CONTEXT_JOURNALS_DIR',
|
|
27
|
-
MERIDIAN_SIGNALS_DIR: 'TEAM_CONTEXT_SIGNALS_DIR',
|
|
28
|
-
MERIDIAN_SKIP_EXPORT: 'TEAM_CONTEXT_SKIP_EXPORT',
|
|
29
|
-
MERIDIAN_EXCLUDE_REPOS: 'TEAM_CONTEXT_EXCLUDE_REPOS',
|
|
30
|
-
MERIDIAN_SLACK_WEBHOOK: 'TEAM_CONTEXT_SLACK_WEBHOOK',
|
|
31
|
-
MERIDIAN_LLM_MODEL: 'TEAM_CONTEXT_LLM_MODEL',
|
|
32
|
-
MERIDIAN_DIGEST_SCHEDULE: 'TEAM_CONTEXT_DIGEST_SCHEDULE',
|
|
33
|
-
MERIDIAN_SIGNAL_SCHEDULE: 'TEAM_CONTEXT_SIGNAL_SCHEDULE',
|
|
34
|
-
MERIDIAN_STORAGE_BACKEND: 'TEAM_CONTEXT_STORAGE_BACKEND',
|
|
35
|
-
MERIDIAN_MODE: 'TEAM_CONTEXT_MODE',
|
|
36
|
-
MERIDIAN_REINDEX_SCHEDULE: 'TEAM_CONTEXT_REINDEX_SCHEDULE',
|
|
37
|
-
MERIDIAN_ENCRYPTION_KEY: 'TEAM_CONTEXT_ENCRYPTION_KEY',
|
|
38
|
-
MERIDIAN_SIMULATE: 'TEAM_CONTEXT_SIMULATE',
|
|
39
|
-
MERIDIAN_SIM_FIXTURES: 'TEAM_CONTEXT_SIM_FIXTURES',
|
|
40
|
-
MERIDIAN_VERSION: 'TEAM_CONTEXT_VERSION',
|
|
41
|
-
};
|
|
42
|
-
for (const [oldKey, newKey] of Object.entries(ENV_VAR_MIGRATION)) {
|
|
43
|
-
if (process.env[oldKey] && !process.env[newKey]) {
|
|
44
|
-
process.env[newKey] = process.env[oldKey];
|
|
45
|
-
if (!process.env.TEAM_CONTEXT_SKIP_EXPORT) {
|
|
46
|
-
console.warn(`⚠ ${oldKey} is deprecated — rename to ${newKey}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Also migrate config directory: if old ~/.claude/meridian/ exists but new doesn't, use old
|
|
52
|
-
const OLD_DIR = path.join(HOME, '.claude', 'meridian');
|
|
53
|
-
const EFFECTIVE_DIR = (fs.existsSync(WAYFIND_DIR) || !fs.existsSync(OLD_DIR)) ? WAYFIND_DIR : OLD_DIR;
|
|
54
|
-
if (EFFECTIVE_DIR === OLD_DIR) {
|
|
55
|
-
console.warn('⚠ ~/.claude/meridian/ detected — rename to ~/.claude/team-context/');
|
|
56
|
-
}
|
|
19
|
+
const EFFECTIVE_DIR = WAYFIND_DIR;
|
|
57
20
|
|
|
58
21
|
// Auto-load .env from config dir BEFORE requiring modules
|
|
59
22
|
// (modules like content-store read env vars at load time)
|
|
@@ -2884,7 +2847,7 @@ function runMigrateToPlugin(args) {
|
|
|
2884
2847
|
}
|
|
2885
2848
|
|
|
2886
2849
|
// Step 3: Remove old command files (plugin skills replace these)
|
|
2887
|
-
const oldCommandFiles = ['init-memory.md', 'init-team.md', 'doctor.md', 'journal.md', 'standup.md', 'review-prs.md'];
|
|
2850
|
+
const oldCommandFiles = ['init-memory.md', 'init-team.md', 'init-folder.md', 'doctor.md', 'journal.md', 'standup.md', 'review-prs.md'];
|
|
2888
2851
|
for (const file of oldCommandFiles) {
|
|
2889
2852
|
const cmdPath = path.join(commandsDir, file);
|
|
2890
2853
|
if (fs.existsSync(cmdPath)) {
|
|
@@ -4301,6 +4264,23 @@ function ensureContainerConfig() {
|
|
|
4301
4264
|
changed = true;
|
|
4302
4265
|
}
|
|
4303
4266
|
|
|
4267
|
+
// Backfill container-specific paths into existing digest config (fixes configs
|
|
4268
|
+
// created before store_path/journal_dir/signals_dir were added)
|
|
4269
|
+
if (config.digest) {
|
|
4270
|
+
const defaults = {
|
|
4271
|
+
store_path: process.env.TEAM_CONTEXT_STORE_PATH || contentStore.DEFAULT_STORE_PATH,
|
|
4272
|
+
journal_dir: process.env.TEAM_CONTEXT_JOURNALS_DIR || '/data/journals',
|
|
4273
|
+
signals_dir: process.env.TEAM_CONTEXT_SIGNALS_DIR || contentStore.DEFAULT_SIGNALS_DIR,
|
|
4274
|
+
team_context_dir: process.env.TEAM_CONTEXT_TEAM_CONTEXT_DIR || '',
|
|
4275
|
+
};
|
|
4276
|
+
for (const [key, val] of Object.entries(defaults)) {
|
|
4277
|
+
if (!config.digest[key]) {
|
|
4278
|
+
config.digest[key] = val;
|
|
4279
|
+
changed = true;
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4304
4284
|
// Slack bot config
|
|
4305
4285
|
if (!config.slack_bot && process.env.SLACK_BOT_TOKEN) {
|
|
4306
4286
|
config.slack_bot = {
|
|
@@ -4318,6 +4298,20 @@ function ensureContainerConfig() {
|
|
|
4318
4298
|
changed = true;
|
|
4319
4299
|
}
|
|
4320
4300
|
|
|
4301
|
+
// Backfill container-specific paths into existing bot config
|
|
4302
|
+
if (config.slack_bot) {
|
|
4303
|
+
const botDefaults = {
|
|
4304
|
+
store_path: process.env.TEAM_CONTEXT_STORE_PATH || contentStore.DEFAULT_STORE_PATH,
|
|
4305
|
+
journal_dir: process.env.TEAM_CONTEXT_JOURNALS_DIR || '/data/journals',
|
|
4306
|
+
};
|
|
4307
|
+
for (const [key, val] of Object.entries(botDefaults)) {
|
|
4308
|
+
if (!config.slack_bot[key]) {
|
|
4309
|
+
config.slack_bot[key] = val;
|
|
4310
|
+
changed = true;
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4321
4315
|
// GitHub connector
|
|
4322
4316
|
if (!config.github && process.env.GITHUB_TOKEN) {
|
|
4323
4317
|
const repos = process.env.TEAM_CONTEXT_GITHUB_REPOS;
|
|
@@ -4847,6 +4841,10 @@ const COMMANDS = {
|
|
|
4847
4841
|
desc: 'Search indexed entries (journals + conversations, semantic or full-text)',
|
|
4848
4842
|
run: (args) => runSearchJournals(args),
|
|
4849
4843
|
},
|
|
4844
|
+
'memory-compare': {
|
|
4845
|
+
desc: 'Compare Claude Code auto-memory vs Wayfind memory systems',
|
|
4846
|
+
run: () => require('./memory-compare').compare(),
|
|
4847
|
+
},
|
|
4850
4848
|
insights: {
|
|
4851
4849
|
desc: 'Show insights from indexed journal data',
|
|
4852
4850
|
run: (args) => runInsights(args),
|
|
@@ -5065,6 +5063,7 @@ function showHelp() {
|
|
|
5065
5063
|
console.log('');
|
|
5066
5064
|
console.log('In a Claude Code session:');
|
|
5067
5065
|
console.log(' /init-memory Set up memory for current repo');
|
|
5066
|
+
console.log(' /init-folder Set up memory for a non-repo folder');
|
|
5068
5067
|
console.log(' /init-team Set up team context (journals, digests, Notion)');
|
|
5069
5068
|
console.log(' /journal View your session journal digest');
|
|
5070
5069
|
console.log(' /doctor Check installation health');
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ set -euo pipefail
|
|
|
11
11
|
# Skip export for worker agents in multi-agent swarms.
|
|
12
12
|
# Set TEAM_CONTEXT_SKIP_EXPORT=1 when spawning worker agents so only the
|
|
13
13
|
# orchestrator's decisions flow into the journal.
|
|
14
|
-
if [ "${TEAM_CONTEXT_SKIP_EXPORT
|
|
14
|
+
if [ "${TEAM_CONTEXT_SKIP_EXPORT:-}" = "1" ]; then
|
|
15
15
|
exit 0
|
|
16
16
|
fi
|
|
17
17
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: init-folder
|
|
3
|
+
description: Initialize Wayfind for a non-repo folder (e.g., ~/admin, ~/). Creates .claude/personal-state.md, defensively updates .gitignore if one exists, and registers the folder in the global index. Safe to run multiple times (idempotent).
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Initialize Wayfind for a Non-Repo Folder
|
|
8
|
+
|
|
9
|
+
Run these steps in order. Skip any step that's already done (this command is idempotent).
|
|
10
|
+
|
|
11
|
+
This command is for **non-repo working directories** — your home directory, an admin folder,
|
|
12
|
+
a scratch workspace, etc. For git repos, use `/wayfind:init-memory` instead.
|
|
13
|
+
|
|
14
|
+
## Step 1: Detect Context
|
|
15
|
+
|
|
16
|
+
- Determine the current working directory (or ask the user which folder to initialize)
|
|
17
|
+
- Check if it's a git repo (`git rev-parse --show-toplevel 2>/dev/null`)
|
|
18
|
+
- If it IS a git repo, suggest: "This looks like a git repo. Did you mean to run `/wayfind:init-memory` instead? That sets up team-state.md for shared context too."
|
|
19
|
+
- If the user confirms they want init-folder anyway, proceed.
|
|
20
|
+
- Read `~/.claude/global-state.md` to check if this folder is already registered
|
|
21
|
+
|
|
22
|
+
## Step 2: Create personal state file (if missing)
|
|
23
|
+
|
|
24
|
+
Non-repo folders only get **one** state file — there's no team-state.md because there's
|
|
25
|
+
no git repo to commit it to.
|
|
26
|
+
|
|
27
|
+
If `.claude/personal-state.md` does not exist, create it:
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
# [Folder Name] — Personal State
|
|
31
|
+
|
|
32
|
+
Last updated: [today's date]
|
|
33
|
+
|
|
34
|
+
(This file is local-only. It persists context across AI sessions in this folder.)
|
|
35
|
+
|
|
36
|
+
## My Current Focus
|
|
37
|
+
<!-- Your current tasks and next steps for work in this folder -->
|
|
38
|
+
|
|
39
|
+
## Personal Context
|
|
40
|
+
<!-- Working notes, decisions, things to remember -->
|
|
41
|
+
|
|
42
|
+
## What I'm Watching
|
|
43
|
+
<!-- Open questions, things to follow up on -->
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Create the `.claude/` directory first if it doesn't exist.
|
|
47
|
+
|
|
48
|
+
## Step 3: Defensive .gitignore update
|
|
49
|
+
|
|
50
|
+
Check if a `.gitignore` file exists in the folder root.
|
|
51
|
+
|
|
52
|
+
**If `.gitignore` exists:** Ensure `.claude/` is listed (the whole directory — unlike repos,
|
|
53
|
+
non-repo folders have no team-state.md to preserve). If it's missing, append it.
|
|
54
|
+
|
|
55
|
+
**If `.gitignore` does not exist:** Skip this step. Don't create one — this isn't a repo.
|
|
56
|
+
|
|
57
|
+
## Step 4: Check CLAUDE.md
|
|
58
|
+
|
|
59
|
+
Read the folder's `CLAUDE.md` (if it exists).
|
|
60
|
+
|
|
61
|
+
**If it exists and does NOT contain "Session State Protocol":** Note to the user:
|
|
62
|
+
"Your `~/CLAUDE.md` (or global instructions) should include the session protocol.
|
|
63
|
+
If you're using Wayfind's standard `~/CLAUDE.md`, you're already covered."
|
|
64
|
+
|
|
65
|
+
**If it exists and DOES contain "Session State Protocol":** Skip — already configured.
|
|
66
|
+
|
|
67
|
+
**If it does not exist:** Do NOT create one. Note: "No CLAUDE.md in this folder.
|
|
68
|
+
Your `~/CLAUDE.md` global instructions handle the session protocol."
|
|
69
|
+
|
|
70
|
+
## Step 5: Register in Global Index
|
|
71
|
+
|
|
72
|
+
Read `~/.claude/global-state.md`. Add the folder's state file to the State Files table
|
|
73
|
+
if missing:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
| `[full path]/.claude/personal-state.md` | Personal context for [folder name] (non-repo) |
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The Active Projects table is auto-generated by `wayfind status --write` — do NOT add
|
|
80
|
+
rows to it manually.
|
|
81
|
+
|
|
82
|
+
## Step 6: Report
|
|
83
|
+
|
|
84
|
+
Tell the user:
|
|
85
|
+
- `.claude/personal-state.md` — created or already existed (local only)
|
|
86
|
+
- `.gitignore` — updated or not applicable
|
|
87
|
+
- `global-state.md` — folder registered or already listed
|
|
88
|
+
|
|
89
|
+
Then print:
|
|
90
|
+
|
|
91
|
+
**"This folder is now tracked by Wayfind. Context saved here will be available in future
|
|
92
|
+
sessions. For cross-repo context that should be available everywhere, save to
|
|
93
|
+
`~/.claude/memory/` instead."**
|
|
@@ -87,6 +87,14 @@ Create them if missing. If `prompts/` is new, add the README from `templates/pro
|
|
|
87
87
|
|
|
88
88
|
Store the team context repo path for later steps.
|
|
89
89
|
|
|
90
|
+
## Step 1a: Non-Repo Folder Setup (Optional)
|
|
91
|
+
|
|
92
|
+
After the team context repo is set up, ask:
|
|
93
|
+
|
|
94
|
+
**"Do you have a non-repo folder you use for admin or cross-cutting work? (e.g., your home directory) We can set that up too so context from those sessions is preserved."**
|
|
95
|
+
|
|
96
|
+
If yes, run `/wayfind:init-folder` in that directory (or guide them to run it later).
|
|
97
|
+
|
|
90
98
|
## Step 2: Slack Integration
|
|
91
99
|
|
|
92
100
|
Ask: **"Do you want weekly digests posted to Slack? You'll need a Slack Incoming Webhook URL. If you have one, paste it. If not, I can walk you through creating one."**
|
|
@@ -35,4 +35,6 @@ the plugin's hooks. This skill tells you where state files live so you can load
|
|
|
35
35
|
- Keep `global-state.md` under 80 lines. Detail goes in `~/.claude/memory/` files.
|
|
36
36
|
- Per-repo state files stay focused on that repo only.
|
|
37
37
|
- New cross-repo topics get new files in `~/.claude/memory/`, not appended to global-state.md.
|
|
38
|
+
- **NEVER write secrets, API keys, tokens, or credentials into memory or state files.** Store pointers to where secrets live, not the secrets themselves.
|
|
39
|
+
- Do NOT write Wayfind files to `~/.claude/projects/<project>/memory/` root — that's Claude Code's native auto-memory space.
|
|
38
40
|
- Do NOT use external memory databases or CLI tools for state storage. Use plain markdown files only.
|
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
**Memory system:** Hierarchical plain-markdown files. No CLI tools, no databases.
|
|
5
5
|
|
|
6
|
+
**Important:** Claude Code has its own auto-memory system that writes to `~/.claude/projects/<project>/memory/MEMORY.md`. Wayfind's memory files live in a separate `wayfind/` subfolder to avoid conflicts. Do NOT write Wayfind state files to the root memory directory — that's Claude Code's native auto-memory space.
|
|
7
|
+
|
|
6
8
|
### File Hierarchy
|
|
7
9
|
|
|
8
10
|
```
|
|
9
11
|
~/.claude/
|
|
10
12
|
global-state.md # Thin index — ALWAYS load at session start
|
|
11
13
|
state.md # Admin/non-repo work
|
|
12
|
-
memory/ #
|
|
14
|
+
memory/ # Cross-repo topic files — load on demand
|
|
13
15
|
<topic>.md
|
|
14
|
-
journal/YYYY-MM-DD.md # Daily log
|
|
16
|
+
journal/YYYY-MM-DD.md # Daily work log
|
|
15
17
|
|
|
16
18
|
<repo>/.claude/
|
|
17
19
|
team-state.md # Shared team context — committed to git
|
|
@@ -47,7 +49,9 @@ If work drifts from the stated goal, flag it: *"Quick check — we set out to [g
|
|
|
47
49
|
|
|
48
50
|
### Rules
|
|
49
51
|
|
|
52
|
+
- **NEVER write secrets, API keys, tokens, passwords, or credentials into memory or state files.** Store a pointer to where the secret lives (e.g., "API key is in `~/.config/myapp/.env`"), not the secret itself. Memory files are plain text, may be synced to git, and are readable by any tool with filesystem access.
|
|
50
53
|
- Keep `global-state.md` under 80 lines. Detail goes in `~/.claude/memory/` files.
|
|
51
54
|
- Per-repo `state.md` stays focused on that repo only.
|
|
52
55
|
- New cross-repo topics get new files in `~/.claude/memory/`, not appended to global-state.md.
|
|
56
|
+
- Do NOT write Wayfind files to `~/.claude/projects/<project>/memory/` root — that's Claude Code's native auto-memory space.
|
|
53
57
|
- Do NOT use external memory databases or CLI tools for state storage. Use plain markdown files only.
|
|
@@ -13,4 +13,6 @@
|
|
|
13
13
|
3. Do NOT update `~/.claude/global-state.md` — its Active Projects table is rebuilt automatically by `wayfind status`.
|
|
14
14
|
4. If significant new cross-repo context was created (patterns, strategies, decisions), create or update a file in `~/.claude/memory/` and add it to the Memory Files manifest in global-state.md
|
|
15
15
|
|
|
16
|
+
**NEVER write secrets, API keys, tokens, or credentials into memory or state files.** Store pointers to where secrets live, not the secrets themselves.
|
|
17
|
+
**Do NOT write Wayfind files to `~/.claude/projects/<project>/memory/` root** — that's Claude Code's native auto-memory space.
|
|
16
18
|
**Do NOT use external memory databases or CLI tools for state storage.** Use plain markdown files only.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Initialize Wayfind for a non-repo folder (e.g., ~/admin, ~/). Creates .claude/personal-state.md, defensively updates .gitignore if one exists, and registers the folder in the global index. Safe to run multiple times (idempotent).
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Initialize Wayfind for a Non-Repo Folder
|
|
6
|
+
|
|
7
|
+
Run these steps in order. Skip any step that's already done (this command is idempotent).
|
|
8
|
+
|
|
9
|
+
This command is for **non-repo working directories** — your home directory, an admin folder,
|
|
10
|
+
a scratch workspace, etc. For git repos, use `/init-memory` instead.
|
|
11
|
+
|
|
12
|
+
## Step 1: Detect Context
|
|
13
|
+
|
|
14
|
+
- Determine the current working directory (or ask the user which folder to initialize)
|
|
15
|
+
- Check if it's a git repo (`git rev-parse --show-toplevel 2>/dev/null`)
|
|
16
|
+
- If it IS a git repo, suggest: "This looks like a git repo. Did you mean to run `/init-memory` instead? That sets up team-state.md for shared context too."
|
|
17
|
+
- If the user confirms they want init-folder anyway, proceed.
|
|
18
|
+
- Read `~/.claude/global-state.md` to check if this folder is already registered
|
|
19
|
+
|
|
20
|
+
## Step 2: Create personal state file (if missing)
|
|
21
|
+
|
|
22
|
+
Non-repo folders only get **one** state file — there's no team-state.md because there's
|
|
23
|
+
no git repo to commit it to.
|
|
24
|
+
|
|
25
|
+
If `.claude/personal-state.md` does not exist, create it:
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
# [Folder Name] — Personal State
|
|
29
|
+
|
|
30
|
+
Last updated: [today's date]
|
|
31
|
+
|
|
32
|
+
(This file is local-only. It persists context across AI sessions in this folder.)
|
|
33
|
+
|
|
34
|
+
## My Current Focus
|
|
35
|
+
<!-- Your current tasks and next steps for work in this folder -->
|
|
36
|
+
|
|
37
|
+
## Personal Context
|
|
38
|
+
<!-- Working notes, decisions, things to remember -->
|
|
39
|
+
|
|
40
|
+
## What I'm Watching
|
|
41
|
+
<!-- Open questions, things to follow up on -->
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Create the `.claude/` directory first if it doesn't exist.
|
|
45
|
+
|
|
46
|
+
## Step 3: Defensive .gitignore update
|
|
47
|
+
|
|
48
|
+
Check if a `.gitignore` file exists in the folder root.
|
|
49
|
+
|
|
50
|
+
**If `.gitignore` exists:** Ensure `.claude/` is listed (the whole directory — unlike repos,
|
|
51
|
+
non-repo folders have no team-state.md to preserve). If it's missing, append it.
|
|
52
|
+
|
|
53
|
+
**If `.gitignore` does not exist:** Skip this step. Don't create one — this isn't a repo.
|
|
54
|
+
|
|
55
|
+
## Step 4: Check CLAUDE.md
|
|
56
|
+
|
|
57
|
+
Read the folder's `CLAUDE.md` (if it exists).
|
|
58
|
+
|
|
59
|
+
**If it exists and does NOT contain "Session State Protocol":** Note to the user:
|
|
60
|
+
"Your `~/CLAUDE.md` (or global instructions) should include the session protocol.
|
|
61
|
+
If you're using Wayfind's standard `~/CLAUDE.md`, you're already covered."
|
|
62
|
+
|
|
63
|
+
**If it exists and DOES contain "Session State Protocol":** Skip — already configured.
|
|
64
|
+
|
|
65
|
+
**If it does not exist:** Do NOT create one. Note: "No CLAUDE.md in this folder.
|
|
66
|
+
Your `~/CLAUDE.md` global instructions handle the session protocol."
|
|
67
|
+
|
|
68
|
+
## Step 5: Register in Global Index
|
|
69
|
+
|
|
70
|
+
Read `~/.claude/global-state.md`. Add the folder's state file to the State Files table
|
|
71
|
+
if missing:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
| `[full path]/.claude/personal-state.md` | Personal context for [folder name] (non-repo) |
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The Active Projects table is auto-generated by `wayfind status --write` — do NOT add
|
|
78
|
+
rows to it manually.
|
|
79
|
+
|
|
80
|
+
## Step 6: Report
|
|
81
|
+
|
|
82
|
+
Tell the user:
|
|
83
|
+
- `.claude/personal-state.md` — created or already existed (local only)
|
|
84
|
+
- `.gitignore` — updated or not applicable
|
|
85
|
+
- `global-state.md` — folder registered or already listed
|
|
86
|
+
|
|
87
|
+
Then print:
|
|
88
|
+
|
|
89
|
+
**"This folder is now tracked by Wayfind. Context saved here will be available in future
|
|
90
|
+
sessions. For cross-repo context that should be available everywhere, save to
|
|
91
|
+
`~/.claude/memory/` instead."**
|
|
@@ -142,6 +142,14 @@ Create `~/.claude/memory/wayfind-team-context.md` with:
|
|
|
142
142
|
- <list of usernames with journal directories>
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
+
## Step 1a: Non-Repo Folder Setup (Optional)
|
|
146
|
+
|
|
147
|
+
After the team context repo is set up, ask:
|
|
148
|
+
|
|
149
|
+
**"Do you have a non-repo folder you use for admin or cross-cutting work? (e.g., your home directory) We can set that up too so context from those sessions is preserved."**
|
|
150
|
+
|
|
151
|
+
If yes, run `/init-folder` in that directory (or guide them to run it later).
|
|
152
|
+
|
|
145
153
|
## Step 1b: Link and Distribute Context
|
|
146
154
|
|
|
147
155
|
Once the team context repo exists, link it so Wayfind knows where to find shared
|
|
@@ -11,7 +11,7 @@ set -euo pipefail
|
|
|
11
11
|
# Skip export for worker agents in multi-agent swarms.
|
|
12
12
|
# Set TEAM_CONTEXT_SKIP_EXPORT=1 when spawning worker agents so only the
|
|
13
13
|
# orchestrator's decisions flow into the journal.
|
|
14
|
-
if [ "${TEAM_CONTEXT_SKIP_EXPORT
|
|
14
|
+
if [ "${TEAM_CONTEXT_SKIP_EXPORT:-}" = "1" ]; then
|
|
15
15
|
exit 0
|
|
16
16
|
fi
|
|
17
17
|
|