claude-capsule-kit 3.0.0

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 (107) hide show
  1. package/README.md +281 -0
  2. package/agents/agent-developer.md +206 -0
  3. package/agents/architecture-explorer.md +90 -0
  4. package/agents/brainstorm-coordinator.md +120 -0
  5. package/agents/code-reviewer.md +135 -0
  6. package/agents/context-librarian.md +227 -0
  7. package/agents/context-manager.md +151 -0
  8. package/agents/database-architect.md +107 -0
  9. package/agents/database-navigator.md +136 -0
  10. package/agents/debugger.md +121 -0
  11. package/agents/devops-sre.md +102 -0
  12. package/agents/error-detective.md +128 -0
  13. package/agents/git-workflow-manager.md +212 -0
  14. package/agents/github-issue-tracker.md +252 -0
  15. package/agents/product-dx-specialist.md +99 -0
  16. package/agents/refactoring-specialist.md +159 -0
  17. package/agents/security-engineer.md +102 -0
  18. package/agents/session-summarizer.md +126 -0
  19. package/agents/system-architect.md +103 -0
  20. package/bin/cck.js +1624 -0
  21. package/commands/crew-setup.md +75 -0
  22. package/commands/load-session.md +68 -0
  23. package/commands/sessions.md +55 -0
  24. package/commands/statusline.md +51 -0
  25. package/commands/sync-disable.md +35 -0
  26. package/commands/sync-enable.md +32 -0
  27. package/commands/sync.md +31 -0
  28. package/crew/lib/activity-monitor.js +128 -0
  29. package/crew/lib/crew-config-reader.js +255 -0
  30. package/crew/lib/health-monitor.js +171 -0
  31. package/crew/lib/merge-pilot.js +340 -0
  32. package/crew/lib/prompt-generator.js +268 -0
  33. package/crew/lib/role-presets.js +63 -0
  34. package/crew/lib/task-decomposer.js +382 -0
  35. package/crew/lib/team-spawner.sh +557 -0
  36. package/crew/lib/team-state-manager.js +155 -0
  37. package/crew/lib/worktree-gc.js +357 -0
  38. package/crew/lib/worktree-manager.sh +700 -0
  39. package/docs/AGENT_ROUTING_GUIDE.md +655 -0
  40. package/docs/AGENT_TEAMS_WORKTREE_MODE.md +681 -0
  41. package/docs/BEST_PRACTICES.md +194 -0
  42. package/docs/CAPSULE_DEGRADATION_RCA.md +577 -0
  43. package/docs/SKILLS_ORCHESTRATION_ARCHITECTURE.md +455 -0
  44. package/docs/SUPER_CLAUDE_SYSTEM_ARCHITECTURE.md +1647 -0
  45. package/docs/TOOL_ENFORCEMENT_REFERENCE.md +418 -0
  46. package/hooks/check-refresh-needed.sh +77 -0
  47. package/hooks/detect-changes.sh +90 -0
  48. package/hooks/keyword-triggers.sh +66 -0
  49. package/hooks/lib/crew-detect.js +241 -0
  50. package/hooks/lib/handoff-generator.js +158 -0
  51. package/hooks/load-from-journal.sh +41 -0
  52. package/hooks/post-tool-use.js +212 -0
  53. package/hooks/pre-compact.js +77 -0
  54. package/hooks/pre-edit-analysis.sh +68 -0
  55. package/hooks/pre-tool-use.sh +212 -0
  56. package/hooks/prompt-submit-memory.sh +87 -0
  57. package/hooks/quality-check.sh +48 -0
  58. package/hooks/session-end.js +133 -0
  59. package/hooks/session-start.js +439 -0
  60. package/hooks/stop.sh +66 -0
  61. package/hooks/suggest-discoveries.sh +84 -0
  62. package/hooks/summarize-session.sh +122 -0
  63. package/hooks/sync-to-journal.sh +77 -0
  64. package/hooks/sync-todowrite.sh +37 -0
  65. package/hooks/tool-auto-suggest.sh +77 -0
  66. package/hooks/user-prompt-submit.sh +71 -0
  67. package/lib/audit-logger.sh +120 -0
  68. package/lib/sandbox-validator.sh +194 -0
  69. package/lib/tool-runner.sh +274 -0
  70. package/package.json +67 -0
  71. package/scripts/postinstall.js +4 -0
  72. package/scripts/show-capsule-visual.sh +103 -0
  73. package/scripts/show-capsule.sh +113 -0
  74. package/scripts/show-deps-tree.sh +66 -0
  75. package/scripts/show-stats-dashboard.sh +52 -0
  76. package/scripts/show-stats.sh +79 -0
  77. package/skills/code-review/SKILL.md +520 -0
  78. package/skills/crew/SKILL.md +395 -0
  79. package/skills/debug/SKILL.md +473 -0
  80. package/skills/deep-context/SKILL.md +446 -0
  81. package/skills/task-router/SKILL.md +390 -0
  82. package/skills/workflow/SKILL.md +370 -0
  83. package/templates/CLAUDE.md +124 -0
  84. package/templates/crew-config.json +21 -0
  85. package/templates/settings-hooks.json +74 -0
  86. package/templates/statusline-command.sh +208 -0
  87. package/tools/context-query/context-query.js +312 -0
  88. package/tools/context-query/context-query.sh +5 -0
  89. package/tools/context-query/tool.json +42 -0
  90. package/tools/dependency-scanner/dependency-scanner.sh +53 -0
  91. package/tools/dependency-scanner/tool.json +8 -0
  92. package/tools/find-circular/find-circular.sh +41 -0
  93. package/tools/find-circular/tool.json +36 -0
  94. package/tools/find-dead-code/find-dead-code.sh +41 -0
  95. package/tools/find-dead-code/tool.json +36 -0
  96. package/tools/impact-analysis/impact-analysis.sh +99 -0
  97. package/tools/impact-analysis/tool.json +38 -0
  98. package/tools/progressive-reader/progressive-reader.sh +14 -0
  99. package/tools/progressive-reader/tool.json +69 -0
  100. package/tools/query-deps/query-deps.sh +69 -0
  101. package/tools/query-deps/tool.json +34 -0
  102. package/tools/stats/stats.js +299 -0
  103. package/tools/stats/stats.sh +5 -0
  104. package/tools/stats/tool.json +34 -0
  105. package/tools/token-counter/README.md +73 -0
  106. package/tools/token-counter/token-counter.py +202 -0
  107. package/tools/token-counter/tool.json +40 -0
@@ -0,0 +1,208 @@
1
+ #!/bin/bash
2
+
3
+ # Read JSON input from stdin
4
+ input=$(cat)
5
+
6
+ # Extract current working directory from JSON
7
+ cwd=$(echo "$input" | jq -r '.workspace.current_dir')
8
+
9
+ # Change to the working directory for git commands
10
+ cd "$cwd" 2>/dev/null || cd "$HOME"
11
+
12
+ # Get directory path with home substitution
13
+ dir_path="${PWD/#$HOME/~}"
14
+
15
+ # --- Config: load section visibility ---
16
+ config_file="$HOME/.claude/statusline-config.json"
17
+ cfg_model=true; cfg_duration=true; cfg_context=true
18
+ cfg_worktree=true; cfg_crew=true; cfg_capsule_pill=true
19
+ cfg_compact_threshold=85
20
+ if [ -f "$config_file" ]; then
21
+ _cfg_val() { jq -r "if has(\"$1\") then .$1 else true end" "$config_file" 2>/dev/null; }
22
+ cfg_model=$(_cfg_val model)
23
+ cfg_duration=$(_cfg_val duration)
24
+ cfg_context=$(_cfg_val context)
25
+ cfg_worktree=$(_cfg_val worktree)
26
+ cfg_crew=$(_cfg_val crew)
27
+ cfg_capsule_pill=$(_cfg_val capsule_pill)
28
+ ct=$(jq -r 'if has("compact_threshold") then .compact_threshold else 85 end' "$config_file" 2>/dev/null)
29
+ [ -n "$ct" ] && [ "$ct" != "null" ] && cfg_compact_threshold=$ct
30
+ fi
31
+
32
+ # --- Colors ---
33
+ C_RESET='\033[0m'
34
+ C_TEAL='\033[38;5;116m'
35
+ C_LAVENDER='\033[38;5;183m'
36
+ C_ORANGE='\033[38;5;214m'
37
+ C_GREEN='\033[38;5;114m'
38
+ C_YELLOW='\033[38;5;221m'
39
+ C_RED='\033[38;5;203m'
40
+ C_CYAN='\033[38;5;117m'
41
+ C_DIM='\033[38;5;245m'
42
+ C_SEPARATOR='\033[38;5;240m'
43
+ C_THRESHOLD='\033[38;5;198m'
44
+ C_BOLD='\033[1m'
45
+
46
+ # Initialize output
47
+ output=""
48
+
49
+ # ── Section 1: Directory ──
50
+ output+="$(printf "${C_TEAL}%s${C_RESET}" "$dir_path")"
51
+
52
+ # ── Section 2: Git info ──
53
+ is_worktree=false
54
+ if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
55
+ # Detect if this is a git worktree (not the main working tree)
56
+ git_common=$(git rev-parse --git-common-dir 2>/dev/null)
57
+ git_dir=$(git rev-parse --git-dir 2>/dev/null)
58
+ if [ -n "$git_common" ] && [ -n "$git_dir" ] && [ "$git_common" != "$git_dir" ]; then
59
+ is_worktree=true
60
+ fi
61
+
62
+ # Get git remote symbol
63
+ git_remote=$(git ls-remote --get-url 2>/dev/null)
64
+ git_symbol=""
65
+
66
+ if [[ "$git_remote" =~ "github" ]]; then
67
+ git_symbol=" "
68
+ elif [[ "$git_remote" =~ "gitlab" ]]; then
69
+ git_symbol=" "
70
+ elif [[ "$git_remote" =~ "bitbucket" ]]; then
71
+ git_symbol=" "
72
+ elif [[ "$git_remote" =~ "git" ]]; then
73
+ git_symbol=" "
74
+ else
75
+ git_symbol=" "
76
+ fi
77
+
78
+ output+=" $git_symbol "
79
+
80
+ # Get git branch (bold lavender)
81
+ git_branch=$(git branch --show-current 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)
82
+ if [ -n "$git_branch" ]; then
83
+ output+="$(printf "on ${C_LAVENDER}${C_BOLD}%s${C_RESET} " "$git_branch")"
84
+ fi
85
+
86
+ # Worktree indicator (pill badge, right after branch)
87
+ if [ "$is_worktree" = true ] && [ "$cfg_worktree" = true ]; then
88
+ C_WT_BG='\033[48;5;54m' # purple background
89
+ C_WT_FG='\033[38;5;177m' # light magenta text
90
+ output+="$(printf "${C_WT_BG}${C_WT_FG}${C_BOLD} worktree ${C_RESET} ")"
91
+ fi
92
+
93
+ # Get git status
94
+ git_status_output=""
95
+
96
+ porcelain=$(git status --porcelain 2>/dev/null)
97
+ if [ -n "$porcelain" ]; then
98
+ modified=$(echo "$porcelain" | grep -c '^ M' 2>/dev/null || true)
99
+ added=$(echo "$porcelain" | grep -c '^A' 2>/dev/null || true)
100
+ deleted=$(echo "$porcelain" | grep -c '^ D' 2>/dev/null || true)
101
+ untracked=$(echo "$porcelain" | grep -c '^??' 2>/dev/null || true)
102
+ modified=$((modified + 0))
103
+ added=$((added + 0))
104
+ deleted=$((deleted + 0))
105
+ untracked=$((untracked + 0))
106
+
107
+ [ "$modified" -gt 0 ] && git_status_output+="!${modified} "
108
+ [ "$added" -gt 0 ] && git_status_output+="+${added} "
109
+ [ "$deleted" -gt 0 ] && git_status_output+="✘${deleted} "
110
+ [ "$untracked" -gt 0 ] && git_status_output+="?${untracked} "
111
+ fi
112
+
113
+ ahead_behind=$(git -c core.fileMode=false rev-list --left-right --count HEAD...@{upstream} 2>/dev/null | tr -s '[:space:]' ' ')
114
+ if [ -n "$ahead_behind" ]; then
115
+ ahead=$(echo "$ahead_behind" | awk '{print $1+0}')
116
+ behind=$(echo "$ahead_behind" | awk '{print $2+0}')
117
+ [ "$ahead" -gt 0 ] 2>/dev/null && git_status_output+="⇡${ahead} "
118
+ [ "$behind" -gt 0 ] 2>/dev/null && git_status_output+="⇣${behind} "
119
+ fi
120
+
121
+ if [ -n "$git_status_output" ]; then
122
+ output+=" $(printf '%s' "$git_status_output")"
123
+ fi
124
+ fi
125
+
126
+ # ── Separator ──
127
+ output+="$(printf " ${C_SEPARATOR}║${C_RESET} ")"
128
+
129
+ # ── Section 3: Model ──
130
+ if [ "$cfg_model" = true ]; then
131
+ model_name=$(echo "$input" | jq -r '.model.display_name // empty')
132
+ if [ -n "$model_name" ]; then
133
+ output+="$(printf "${C_ORANGE} %s${C_RESET}" "$model_name")"
134
+ fi
135
+ fi
136
+
137
+ # ── Section 3b: Session duration ──
138
+ if [ "$cfg_duration" = true ]; then
139
+ duration_ms=$(echo "$input" | jq -r '.cost.total_duration_ms // empty')
140
+ if [ -n "$duration_ms" ] && [ "$duration_ms" != "0" ]; then
141
+ total_sec=$((duration_ms / 1000))
142
+ if [ "$total_sec" -ge 3600 ]; then
143
+ hrs=$((total_sec / 3600))
144
+ mins=$(( (total_sec % 3600) / 60 ))
145
+ dur_str="${hrs}h ${mins}m"
146
+ elif [ "$total_sec" -ge 60 ]; then
147
+ mins=$((total_sec / 60))
148
+ dur_str="${mins}m"
149
+ else
150
+ dur_str="${total_sec}s"
151
+ fi
152
+ output+="$(printf " ${C_DIM}%s${C_RESET}" "$dur_str")"
153
+ fi
154
+ fi
155
+
156
+
157
+ # ── Section 4: Context window usage ──
158
+ if [ "$cfg_context" = true ]; then
159
+ ctx_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
160
+ if [ -n "$ctx_pct" ] && [ "$ctx_pct" != "null" ]; then
161
+ pct_int=${ctx_pct%.*}
162
+ pct_int=${pct_int:-0}
163
+ filled=$((pct_int / 10))
164
+
165
+ if [ "$pct_int" -ge 80 ]; then
166
+ bar_color="$C_RED"
167
+ elif [ "$pct_int" -ge 50 ]; then
168
+ bar_color="$C_YELLOW"
169
+ else
170
+ bar_color="$C_GREEN"
171
+ fi
172
+
173
+ # Build bar with auto-compact threshold marker
174
+ threshold_seg=$((cfg_compact_threshold / 10))
175
+ bar=""
176
+ for ((i=0; i<10; i++)); do
177
+ if [ "$i" -eq "$threshold_seg" ]; then
178
+ bar+="$(printf "${C_THRESHOLD}┃${C_RESET}")"
179
+ elif [ "$i" -lt "$filled" ]; then
180
+ bar+="$(printf "${bar_color}█${C_RESET}")"
181
+ else
182
+ bar+="$(printf "${C_DIM}░${C_RESET}")"
183
+ fi
184
+ done
185
+
186
+ output+="$(printf " ${C_DIM}context${C_RESET} %s ${bar_color}%s%%${C_RESET}" "$bar" "$pct_int")"
187
+ fi
188
+ fi
189
+
190
+ # ── Section 5: CCK active (pill badge) ──
191
+ if [ "$cfg_capsule_pill" = true ] && [ -d "$HOME/.claude/cck" ]; then
192
+ C_PILL_BG='\033[48;5;22m' # dark green background
193
+ C_PILL_FG='\033[38;5;157m' # light green text
194
+ output+="$(printf " ${C_SEPARATOR}║${C_RESET} ${C_PILL_BG}${C_PILL_FG}${C_BOLD} capsule kit ${C_RESET}")"
195
+ fi
196
+
197
+ # ── Section 6: Crew mode ──
198
+ if [ "$cfg_crew" = true ]; then
199
+ crew_file="$cwd/crew-identity.json"
200
+ if [ -f "$crew_file" ]; then
201
+ crew_name=$(jq -r '.teammate_name // empty' "$crew_file" 2>/dev/null)
202
+ if [ -n "$crew_name" ]; then
203
+ output+="$(printf " ${C_CYAN}${C_BOLD}@crew${C_RESET}${C_DIM}=${C_RESET}${C_CYAN}%s${C_RESET}" "$crew_name")"
204
+ fi
205
+ fi
206
+ fi
207
+
208
+ echo "$output"
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Context-Query Tool v3.0 - Query Capsule context database
4
+ * Uses blink-query for namespace/type-aware querying
5
+ */
6
+
7
+ import { Blink } from 'blink-query';
8
+ import { existsSync } from 'fs';
9
+ import { dirname, join } from 'path';
10
+ import { homedir } from 'os';
11
+ import { createHash } from 'crypto';
12
+ import { execSync } from 'child_process';
13
+
14
+ function findCapsuleDb() {
15
+ const globalDb = join(homedir(), '.claude', 'capsule.db');
16
+ if (existsSync(globalDb)) return globalDb;
17
+ let dir = process.cwd();
18
+ while (dir !== '/') {
19
+ const dbPath = join(dir, '.claude', 'capsule.db');
20
+ if (existsSync(dbPath)) return dbPath;
21
+ dir = dirname(dir);
22
+ }
23
+ return null;
24
+ }
25
+
26
+ function getProjectHash() {
27
+ let identifier;
28
+ try {
29
+ identifier = execSync('git remote get-url origin', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
30
+ } catch {
31
+ identifier = process.cwd();
32
+ }
33
+ return createHash('sha256').update(identifier).digest('hex').slice(0, 12);
34
+ }
35
+
36
+ function getAllRecords(blink) {
37
+ const hash = getProjectHash();
38
+ let projRecords = [];
39
+ try { projRecords = blink.list(`proj/${hash}/session`, 'recent'); } catch { /* no proj records */ }
40
+ let projCrew = [];
41
+ try { projCrew = blink.list(`proj/${hash}/crew`, 'recent'); } catch { /* no crew records */ }
42
+ let solo = [];
43
+ try { solo = blink.list('session', 'recent'); } catch { /* no legacy records */ }
44
+ let crew = [];
45
+ try { crew = blink.list('crew', 'recent'); } catch { /* no crew records */ }
46
+ return [...projRecords, ...projCrew, ...solo, ...crew];
47
+ }
48
+
49
+ const dbPath = findCapsuleDb();
50
+ if (!dbPath) {
51
+ console.log('No capsule.db found. Context will be available after your first session.');
52
+ process.exit(0);
53
+ }
54
+
55
+ const blink = new Blink({ dbPath });
56
+ const [,, cmd = 'help', arg, limitArg] = process.argv;
57
+
58
+ try {
59
+ switch (cmd) {
60
+ case 'search': {
61
+ if (!arg) {
62
+ console.log('Usage: context-query search <term>');
63
+ process.exit(1);
64
+ }
65
+ const results = blink.search(arg, undefined, parseInt(limitArg) || 10);
66
+ console.log(`## Capsule Search: ${arg}\n`);
67
+ if (results.length === 0) {
68
+ console.log(`No results found for '${arg}'`);
69
+ } else {
70
+ results.forEach(r => {
71
+ console.log(`- **[${r.type}]** ${r.title}`);
72
+ if (r.summary) console.log(` ${r.summary.replace(/\n/g, ' ').slice(0, 120)}`);
73
+ });
74
+ }
75
+ break;
76
+ }
77
+
78
+ case 'files': {
79
+ const limit = parseInt(arg) || 20;
80
+ const all = getAllRecords(blink);
81
+ const fileRecords = all.filter(r => r.namespace?.includes('/files')).slice(0, limit);
82
+ console.log(`## Recent File Operations (${fileRecords.length})\n`);
83
+ if (fileRecords.length === 0) {
84
+ console.log('No file operations recorded yet');
85
+ } else {
86
+ fileRecords.forEach(r => {
87
+ console.log(`- **${r.title}**: ${r.summary?.replace(/\n/g, ' ').slice(0, 100) || ''}`);
88
+ });
89
+ }
90
+ break;
91
+ }
92
+
93
+ case 'agents': {
94
+ const limit = parseInt(arg) || 10;
95
+ const all = getAllRecords(blink);
96
+ const agentRecords = all.filter(r => r.namespace?.includes('/subagents')).slice(0, limit);
97
+ console.log(`## Sub-Agent History (${agentRecords.length})\n`);
98
+ if (agentRecords.length === 0) {
99
+ console.log('No sub-agent records found');
100
+ } else {
101
+ agentRecords.forEach(r => {
102
+ console.log(`- **${r.title}**`);
103
+ if (r.summary) console.log(` ${r.summary.replace(/\n/g, ' ').slice(0, 120)}`);
104
+ });
105
+ }
106
+ break;
107
+ }
108
+
109
+ case 'sessions': {
110
+ const limit = parseInt(arg) || 5;
111
+ const all = getAllRecords(blink);
112
+ const sessionRecords = all.filter(r =>
113
+ r.namespace?.endsWith('/session') || r.namespace === 'session' || (r.namespace?.includes('crew') && r.type === 'META')
114
+ ).slice(0, limit);
115
+ console.log(`## Session History (${sessionRecords.length})\n`);
116
+ if (sessionRecords.length === 0) {
117
+ console.log('No session records found');
118
+ } else {
119
+ sessionRecords.forEach(r => {
120
+ console.log(`- **${r.title}**`);
121
+ if (r.summary) console.log(` ${r.summary.replace(/\n/g, ' ').slice(0, 120)}`);
122
+ });
123
+ }
124
+ break;
125
+ }
126
+
127
+ case 'recent': {
128
+ const limit = parseInt(arg) || 15;
129
+ const all = getAllRecords(blink).slice(0, limit);
130
+ console.log(`## Recent Activity (${all.length})\n`);
131
+ all.forEach(r => {
132
+ console.log(`- [${r.type}] **${r.title}** (${r.namespace})`);
133
+ if (r.summary) console.log(` ${r.summary.replace(/\n/g, ' ').slice(0, 80)}`);
134
+ });
135
+ break;
136
+ }
137
+
138
+ case 'ns': {
139
+ if (!arg) {
140
+ console.log('Usage: context-query ns <namespace> [limit]');
141
+ console.log('Example: context-query ns "session" 10');
142
+ process.exit(1);
143
+ }
144
+ const limit = parseInt(limitArg) || 10;
145
+ const results = blink.query(`${arg} limit ${limit}`);
146
+ console.log(`## Namespace: ${arg} (${results.length})\n`);
147
+ if (results.length === 0) {
148
+ console.log('No records in this namespace');
149
+ } else {
150
+ results.forEach(r => {
151
+ console.log(`- **[${r.type}]** ${r.title}`);
152
+ if (r.summary) console.log(` ${r.summary.replace(/\n/g, ' ').slice(0, 120)}`);
153
+ });
154
+ }
155
+ break;
156
+ }
157
+
158
+ case 'save': {
159
+ if (!arg) {
160
+ console.log('Usage: context-query save <namespace> <title> <summary> [type]');
161
+ console.log('Types: SUMMARY (default), META, COLLECTION, SOURCE, ALIAS');
162
+ console.log('');
163
+ console.log('Examples:');
164
+ console.log(' save discoveries "OAuth flow" "Uses PKCE with refresh tokens"');
165
+ console.log(' save session/notes "DB schema" "Users table has soft deletes" META');
166
+ process.exit(1);
167
+ }
168
+ const namespace = arg;
169
+ const title = process.argv[4];
170
+ const summary = process.argv[5];
171
+ const type = process.argv[6] || 'SUMMARY';
172
+ if (!title || !summary) {
173
+ console.log('Error: title and summary are required');
174
+ console.log('Usage: context-query save <namespace> <title> <summary> [type]');
175
+ process.exit(1);
176
+ }
177
+ blink.save({ namespace, title, summary, type, tags: [] });
178
+ console.log(`Saved to ${namespace}: "${title}" [${type}]`);
179
+ break;
180
+ }
181
+
182
+ case 'update': {
183
+ if (!arg) {
184
+ console.log('Usage: context-query update <search-term> <new-summary>');
185
+ console.log('');
186
+ console.log('Finds the most recent record matching the search term and updates it.');
187
+ console.log('');
188
+ console.log('Examples:');
189
+ console.log(' update "OAuth flow" "Now uses PKCE + DPoP with 15min token expiry"');
190
+ process.exit(1);
191
+ }
192
+ const searchTerm = arg;
193
+ const newSummary = process.argv[4];
194
+ if (!newSummary) {
195
+ console.log('Error: new summary is required');
196
+ process.exit(1);
197
+ }
198
+ const matches = blink.search(searchTerm, undefined, 1);
199
+ if (matches.length === 0) {
200
+ console.log(`No record found matching '${searchTerm}'`);
201
+ process.exit(1);
202
+ }
203
+ const record = matches[0];
204
+ blink.save({
205
+ namespace: record.namespace,
206
+ title: record.title,
207
+ summary: newSummary,
208
+ type: record.type,
209
+ tags: record.tags || []
210
+ });
211
+ console.log(`Updated "${record.title}" in ${record.namespace}`);
212
+ console.log(` Old: ${record.summary?.replace(/\n/g, ' ').slice(0, 80)}`);
213
+ console.log(` New: ${newSummary.slice(0, 80)}`);
214
+ break;
215
+ }
216
+
217
+ case 'discoveries': {
218
+ const limit = parseInt(arg) || 50;
219
+ const hash = getProjectHash();
220
+ let allDiscoveries = [];
221
+
222
+ // Query all possible discovery namespaces
223
+ const namespaces = [
224
+ 'discoveries', // legacy global
225
+ `proj/${hash}/discoveries`, // project-scoped
226
+ `proj/${hash}/crew/_shared/discoveries`, // crew shared
227
+ ];
228
+
229
+ namespaces.forEach(ns => {
230
+ try {
231
+ const result = blink.resolve(ns);
232
+ // blink.resolve returns {status, record} — content has child refs
233
+ // Resolve each child fully to get created_at, summary, etc.
234
+ if (result?.record?.content && Array.isArray(result.record.content)) {
235
+ for (const child of result.record.content) {
236
+ try {
237
+ const full = blink.resolve(child.path);
238
+ if (full?.record) allDiscoveries.push(full.record);
239
+ } catch { /* skip unresolvable */ }
240
+ }
241
+ } else if (result?.record?.summary) {
242
+ allDiscoveries.push(result.record);
243
+ }
244
+ } catch {
245
+ // Namespace doesn't exist, skip
246
+ }
247
+ });
248
+
249
+ // Sort by timestamp descending
250
+ allDiscoveries.sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
251
+ const discoveries = allDiscoveries.slice(0, limit);
252
+
253
+ console.log(`## Discoveries (${discoveries.length})\n`);
254
+ if (discoveries.length === 0) {
255
+ console.log('No discoveries saved yet.');
256
+ console.log('');
257
+ console.log('Save a discovery:');
258
+ console.log(' context-query save discoveries "Finding title" "Summary of the discovery"');
259
+ } else {
260
+ discoveries.forEach(r => {
261
+ const date = r.created_at ? new Date(r.created_at).toISOString().split('T')[0] : 'unknown';
262
+ console.log(`- **${r.title}** (${date})`);
263
+ if (r.summary) {
264
+ console.log(` ${r.summary.replace(/\n/g, ' ')}`);
265
+ }
266
+ console.log('');
267
+ });
268
+ }
269
+ break;
270
+ }
271
+
272
+ case 'stats': {
273
+ const all = getAllRecords(blink);
274
+ const byType = {};
275
+ let files = 0, agents = 0;
276
+ const sessions = new Set();
277
+ all.forEach(r => {
278
+ byType[r.type] = (byType[r.type] || 0) + 1;
279
+ if (r.namespace?.includes('/files')) files++;
280
+ if (r.namespace?.includes('/subagents')) agents++;
281
+ const match = r.namespace?.match(/session\/([^/]+)/);
282
+ if (match) sessions.add(match[1]);
283
+ });
284
+ console.log(`## Capsule Stats\n`);
285
+ console.log(`- Total records: ${all.length}`);
286
+ console.log(`- Files tracked: ${files}`);
287
+ console.log(`- Sub-agents: ${agents}`);
288
+ console.log(`- Sessions: ${sessions.size}`);
289
+ console.log(`- Types: ${Object.entries(byType).map(([k,v]) => `${k}(${v})`).join(', ')}`);
290
+ break;
291
+ }
292
+
293
+ default:
294
+ console.log(`## Capsule Context Query\n`);
295
+ console.log(`Usage: bash $HOME/.claude/cck/tools/context-query/context-query.sh <command> [args]\n`);
296
+ console.log(`Read:`);
297
+ console.log(` search <term> Search records by keyword`);
298
+ console.log(` files [limit] Recent file operations (default: 20)`);
299
+ console.log(` agents [limit] Sub-agent invocation history (default: 10)`);
300
+ console.log(` sessions [limit] Session summaries (default: 5)`);
301
+ console.log(` recent [limit] All recent activity (default: 15)`);
302
+ console.log(` discoveries [limit] All saved discoveries (default: 50)`);
303
+ console.log(` ns <namespace> Query specific Capsule namespace`);
304
+ console.log(` stats Database statistics`);
305
+ console.log(``);
306
+ console.log(`Write:`);
307
+ console.log(` save <ns> <title> <summary> [type] Save a record`);
308
+ console.log(` update <search> <new-summary> Update most recent match`);
309
+ }
310
+ } finally {
311
+ blink.close();
312
+ }
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ # Context-Query Tool - Bash wrapper for global CCK
3
+ # Queries Capsule context database
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ exec node "$SCRIPT_DIR/context-query.js" "$@"
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "context-query",
3
+ "version": "3.0.0",
4
+ "description": "Query Capsule context database for session history, file operations, and sub-agent results",
5
+ "type": "node",
6
+ "entry": "context-query.js",
7
+ "author": "Arpit Nath",
8
+ "keywords": ["context", "capsule", "retrieval", "session", "history"],
9
+ "usage": {
10
+ "command": "bash .claude/tools/context-query/context-query.sh <command> [args]",
11
+ "args": {
12
+ "command": "One of: search, files, agents, sessions, recent"
13
+ },
14
+ "examples": [
15
+ {
16
+ "description": "Search for context about authentication",
17
+ "command": "bash .claude/tools/context-query/context-query.sh search authentication"
18
+ },
19
+ {
20
+ "description": "List recent file operations",
21
+ "command": "bash .claude/tools/context-query/context-query.sh files"
22
+ },
23
+ {
24
+ "description": "Show sub-agent history",
25
+ "command": "bash .claude/tools/context-query/context-query.sh agents"
26
+ },
27
+ {
28
+ "description": "Show last 3 session summaries",
29
+ "command": "bash .claude/tools/context-query/context-query.sh sessions 3"
30
+ }
31
+ ]
32
+ },
33
+ "requires": {
34
+ "bash": ">=4.0",
35
+ "sqlite3": "any"
36
+ },
37
+ "permissions": {
38
+ "read": [".claude/capsule.db"],
39
+ "write": [],
40
+ "network": false
41
+ }
42
+ }
@@ -0,0 +1,53 @@
1
+ #!/bin/bash
2
+ # Dependency Scanner Wrapper
3
+ # Rebuilds the dependency graph for current project
4
+
5
+ set -euo pipefail
6
+
7
+ # Find .claude directory by searching parent directories
8
+ find_claude_dir() {
9
+ local current_dir="$PWD"
10
+ while [ "$current_dir" != "/" ]; do
11
+ if [ -d "$current_dir/.claude" ]; then
12
+ echo "$current_dir/.claude"
13
+ return 0
14
+ fi
15
+ current_dir=$(dirname "$current_dir")
16
+ done
17
+ echo ".claude"
18
+ return 1
19
+ }
20
+
21
+ CLAUDE_DIR=$(find_claude_dir)
22
+
23
+ # Check if dependency-scanner binary exists
24
+ if [ ! -x "${HOME}/.claude/bin/dependency-scanner" ]; then
25
+ echo "Error: dependency-scanner binary not found at ~/.claude/bin/dependency-scanner"
26
+ echo "Run installation to build it."
27
+ exit 1
28
+ fi
29
+
30
+ echo "Building dependency graph..."
31
+ echo ""
32
+
33
+ # Run the scanner
34
+ "${HOME}/.claude/bin/dependency-scanner" \
35
+ --output "${CLAUDE_DIR}/dep-graph.toon" \
36
+ --languages typescript,javascript,python,go \
37
+ . 2>&1
38
+
39
+ if [ -f "${CLAUDE_DIR}/dep-graph.toon" ]; then
40
+ FILE_COUNT=$(grep -c "^FILE{" "${CLAUDE_DIR}/dep-graph.toon" || echo "0")
41
+ echo ""
42
+ echo "✓ Dependency graph built successfully"
43
+ echo " Files analyzed: ${FILE_COUNT}"
44
+ echo " Graph saved to: ${CLAUDE_DIR}/dep-graph.toon"
45
+ echo ""
46
+ echo "Query with:"
47
+ echo " bash $HOME/.claude/cck/tools/query-deps/query-deps.sh <file>"
48
+ echo " bash $HOME/.claude/cck/tools/find-circular/find-circular.sh"
49
+ echo " bash $HOME/.claude/cck/tools/find-dead-code/find-dead-code.sh"
50
+ else
51
+ echo "Error: Failed to create dependency graph"
52
+ exit 1
53
+ fi
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "dependency-scanner",
3
+ "description": "Rebuild dependency graph for current project (analyzes imports/exports)",
4
+ "type": "bash",
5
+ "command": "bash .claude/tools/dependency-scanner/dependency-scanner.sh",
6
+ "entry": "dependency-scanner.sh",
7
+ "when_to_use": "When dependency graph is stale or needs manual refresh"
8
+ }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ # Find circular dependencies
3
+
4
+ set -eo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ LIB_DIR="${CCK_LIB_DIR:-$(cd "$SCRIPT_DIR/../../lib" && pwd 2>/dev/null || echo "$HOME/.claude/cck/lib")}"
8
+
9
+ source "$LIB_DIR/toon-parser.sh"
10
+
11
+ GRAPH_FILE="${DEP_GRAPH_FILE:-.claude/dep-graph.toon}"
12
+
13
+ if [ $# -ge 1 ]; then
14
+ GRAPH_FILE="$1"
15
+ fi
16
+
17
+ if [ ! -f "$GRAPH_FILE" ]; then
18
+ echo "Error: Graph file not found: $GRAPH_FILE"
19
+ echo "Run the dependency scanner first to generate the graph"
20
+ exit 1
21
+ fi
22
+
23
+ CIRCULAR=$(toon_get_circular "$GRAPH_FILE")
24
+
25
+ if [ -z "$CIRCULAR" ]; then
26
+ echo "No circular dependencies found"
27
+ exit 0
28
+ fi
29
+
30
+ CYCLE_COUNT=$(echo "$CIRCULAR" | wc -l | tr -d ' ')
31
+
32
+ echo "Found $CYCLE_COUNT circular dependency cycle(s):"
33
+ echo ""
34
+
35
+ INDEX=1
36
+ echo "$CIRCULAR" | while IFS= read -r cycle; do
37
+ echo "Cycle $INDEX:"
38
+ echo " $cycle" | tr '>' $'\n → '
39
+ echo ""
40
+ INDEX=$((INDEX + 1))
41
+ done