claude-code-kanban 2.4.0 → 3.0.0-rc.2

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/install.js CHANGED
@@ -7,24 +7,13 @@ const readline = require('readline');
7
7
  const { execSync } = require('child_process');
8
8
 
9
9
  const CLAUDE_DIR = path.join(os.homedir(), '.claude');
10
+ const CCK_DIR = path.join(CLAUDE_DIR, '.cck');
10
11
  const HOOKS_DIR = path.join(CLAUDE_DIR, 'hooks');
11
12
  const SETTINGS_PATH = path.join(CLAUDE_DIR, 'settings.json');
12
- const HOOK_SCRIPT_DEST = path.join(HOOKS_DIR, 'agent-spy.sh');
13
- const HOOK_SCRIPT_SRC = path.join(__dirname, 'hooks', 'agent-spy.sh');
13
+ const PLUGIN_SRC = path.join(__dirname, 'plugin');
14
+ const PLUGIN_DEST = path.join(CCK_DIR, 'plugin');
15
+ const CTX_SCRIPT_SRC = path.join(PLUGIN_SRC, 'plugins', 'claude-code-kanban', 'scripts', 'context-status.sh');
14
16
  const CTX_SCRIPT_DEST = path.join(HOOKS_DIR, 'context-status.sh');
15
- const CTX_SCRIPT_SRC = path.join(__dirname, 'hooks', 'context-status.sh');
16
- const AGENT_ACTIVITY_DIR = path.join(CLAUDE_DIR, 'agent-activity');
17
-
18
- const HOOK_COMMAND = '~/.claude/hooks/agent-spy.sh';
19
- const HOOK_EVENTS = [
20
- { event: 'SessionStart' },
21
- { event: 'SubagentStart' },
22
- { event: 'SubagentStop' },
23
- { event: 'TeammateIdle' },
24
- { event: 'PermissionRequest' },
25
- { event: 'PreToolUse', matcher: 'AskUserQuestion' },
26
- { event: 'PostToolUse' },
27
- ];
28
17
 
29
18
  // ANSI helpers
30
19
  const green = s => `\x1b[32m${s}\x1b[0m`;
@@ -43,136 +32,136 @@ function prompt(question) {
43
32
  });
44
33
  }
45
34
 
46
- async function runInstall() {
47
- console.log(`\n ${bold('claude-code-kanban')} — Agent Log hook installer\n`);
48
-
49
- // 1. Check bash
50
- process.stdout.write(' Checking bash... ');
35
+ function runCLI(cmd, okPatterns = []) {
51
36
  try {
52
- const bashPath = execSync('which bash', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
53
- console.log(green(`✓ found (${bashPath})`));
54
- } catch {
55
- const shell = process.env.SHELL || process.env.BASH || '';
56
- if (shell.includes('bash')) {
57
- console.log(green(`✓ found via $SHELL (${shell})`));
37
+ const out = execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
38
+ return { ok: true, output: out };
39
+ } catch (e) {
40
+ const stderr = e.stderr?.trim() || e.message;
41
+ if (okPatterns.some(p => stderr.includes(p))) return { ok: true, idempotent: true };
42
+ return { ok: false, error: stderr };
43
+ }
44
+ }
45
+
46
+ function copyScript(src, dest) {
47
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
48
+ fs.copyFileSync(src, dest);
49
+ try { fs.chmodSync(dest, 0o755); } catch {}
50
+ }
51
+
52
+ function copyDirSync(src, dest) {
53
+ fs.mkdirSync(dest, { recursive: true });
54
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
55
+ const srcPath = path.join(src, entry.name);
56
+ const destPath = path.join(dest, entry.name);
57
+ if (entry.isDirectory()) {
58
+ copyDirSync(srcPath, destPath);
58
59
  } else {
59
- const currentShell = shell || process.env.ComSpec || 'unknown';
60
- console.log(yellow(`⚠ bash not found (current shell: ${currentShell})`));
61
- console.log(` ${dim('Hook scripts use #!/bin/bash and require a bash environment')}`);
62
- if (!(await prompt(` Continue anyway? [Y/n] `))) {
63
- console.log(`\n ${dim('Install cancelled.')}\n`);
64
- return;
65
- }
60
+ fs.copyFileSync(srcPath, destPath);
61
+ try { fs.chmodSync(destPath, 0o755); } catch {}
66
62
  }
67
63
  }
64
+ }
68
65
 
69
- // 2. Check jq
70
- process.stdout.write(' Checking jq... ');
71
- try {
72
- const ver = execSync('jq --version', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
73
- console.log(green(`✓ found (${ver})`));
74
- } catch {
75
- console.log(yellow('⚠ not found — hook script requires jq for JSON parsing'));
66
+ async function runInstall() {
67
+ console.log(`\n ${bold('claude-code-kanban')} Plugin & StatusLine installer\n`);
68
+
69
+ // 1. Check prerequisites
70
+ process.stdout.write(' Checking claude CLI... ');
71
+ const claude = runCLI('claude --version');
72
+ if (claude.ok) {
73
+ console.log(green(`✓ found (${claude.output})`));
74
+ } else {
75
+ console.log(red('✗ claude CLI not found'));
76
+ console.log(` ${dim('Install Claude Code CLI first: https://docs.anthropic.com/en/docs/claude-code')}`);
77
+ return;
76
78
  }
77
79
 
78
- async function installScript(label, src, dest) {
79
- console.log(`\n ${label}: ${dim(dest)}`);
80
- if (fs.existsSync(dest)) {
81
- const existing = fs.readFileSync(dest, 'utf8');
82
- const bundled = fs.readFileSync(src, 'utf8');
83
- if (existing === bundled) {
84
- console.log(` ${green('✓')} Up to date`);
85
- return true;
86
- }
87
- if (await prompt(` Different version found. Update? [Y/n] `)) {
88
- fs.mkdirSync(HOOKS_DIR, { recursive: true });
89
- fs.copyFileSync(src, dest);
90
- try { fs.chmodSync(dest, 0o755); } catch {}
91
- console.log(` ${green('✓')} Updated`);
92
- return true;
93
- }
94
- console.log(` ${dim('Skipped')}`);
95
- return false;
96
- }
97
- if (await prompt(` Not found. Install? [Y/n] `)) {
98
- fs.mkdirSync(HOOKS_DIR, { recursive: true });
99
- fs.copyFileSync(src, dest);
100
- try { fs.chmodSync(dest, 0o755); } catch {}
101
- console.log(` ${green('✓')} Installed and set executable`);
102
- return true;
103
- }
104
- console.log(` ${dim('Skipped')}`);
105
- return false;
80
+ process.stdout.write(' Checking jq... ');
81
+ const jq = runCLI('jq --version');
82
+ if (jq.ok) {
83
+ console.log(green(`✓ found (${jq.output})`));
84
+ } else {
85
+ console.log(yellow('⚠ not found — hook scripts require jq for JSON parsing'));
106
86
  }
107
87
 
108
- // 3. Hook scripts
109
- const hookInstalled = await installScript('Hook script', HOOK_SCRIPT_SRC, HOOK_SCRIPT_DEST);
110
- const ctxInstalled = await installScript('Context spy', CTX_SCRIPT_SRC, CTX_SCRIPT_DEST);
88
+ // 2. Copy plugin to stable location & register marketplace
89
+ console.log(`\n Plugin: ${dim(PLUGIN_DEST)}`);
90
+ if (await prompt(` Install claude-code-kanban plugin? [Y/n] `)) {
91
+ process.stdout.write(' Copying plugin to ~/.claude/.cck/plugin... ');
92
+ try {
93
+ if (fs.existsSync(PLUGIN_DEST)) fs.rmSync(PLUGIN_DEST, { recursive: true, force: true });
94
+ copyDirSync(PLUGIN_SRC, PLUGIN_DEST);
95
+ console.log(green('✓'));
96
+ } catch (e) {
97
+ console.log(red(`✗ ${e.message}`));
98
+ }
111
99
 
112
- // 4. Settings.json
113
- console.log(`\n Settings: ${dim(SETTINGS_PATH)}`);
114
- let settings;
115
- try {
116
- if (fs.existsSync(SETTINGS_PATH)) {
117
- settings = JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf8'));
100
+ process.stdout.write(' Registering marketplace... ');
101
+ const mkt = runCLI(`claude plugin marketplace add "${PLUGIN_DEST}"`, ['already', 'exists']);
102
+ if (mkt.ok) {
103
+ console.log(green(mkt.idempotent ? '✓ already registered' : '✓'));
118
104
  } else {
119
- settings = {};
105
+ console.log(yellow(`⚠ ${mkt.error}`));
120
106
  }
121
- } catch (e) {
122
- console.log(` ${red('✗')} Malformed JSON in settings.json — aborting settings update`);
123
- printSummary(hookInstalled, false);
124
- return;
125
- }
126
107
 
127
- if (!settings.hooks) settings.hooks = {};
128
-
129
- const needed = [];
130
- for (const { event, matcher } of HOOK_EVENTS) {
131
- if (!settings.hooks[event]) settings.hooks[event] = [];
132
- const matcherStr = matcher || '';
133
- const exists = settings.hooks[event].some(g =>
134
- g.matcher === matcherStr && g.hooks?.some(h => h.command === HOOK_COMMAND)
135
- );
136
- if (!exists) needed.push({ event, matcher: matcherStr });
108
+ const inst = runCLI('claude plugin install claude-code-kanban@claude-code-kanban', ['already installed', 'already exists']);
109
+ if (inst.ok) {
110
+ console.log(` ${green('✓')} ${inst.idempotent ? 'Already installed' : 'Plugin installed'}`);
111
+ } else {
112
+ console.log(` ${red('✗')} Plugin install failed: ${inst.error}`);
113
+ }
114
+ } else {
115
+ console.log(` ${dim('Skipped')}`);
137
116
  }
138
117
 
139
- let settingsUpdated = false;
140
- if (needed.length === 0) {
141
- console.log(` ${green('✓')} Already configured`);
142
- settingsUpdated = true;
143
- } else {
144
- console.log(` Adding hooks for: ${needed.map(n => n.matcher ? `${n.event}:${n.matcher}` : n.event).join(', ')}`);
145
- if (await prompt(` Update settings? [Y/n] `)) {
146
- for (const { event, matcher } of needed) {
147
- settings.hooks[event].push({
148
- matcher,
149
- hooks: [{ type: 'command', command: HOOK_COMMAND, timeout: 5 }]
150
- });
151
- }
152
- fs.mkdirSync(CLAUDE_DIR, { recursive: true });
153
- fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
154
- console.log(` ${green('✓')} ${needed.length} hook entries added`);
155
- settingsUpdated = true;
118
+ // 3. StatusLine setup (context-status.sh must be copied globally since statusLine is not plugin-scoped)
119
+ console.log(`\n Context spy: ${dim(CTX_SCRIPT_DEST)}`);
120
+ let ctxInstalled = false;
121
+ if (fs.existsSync(CTX_SCRIPT_DEST)) {
122
+ const existing = fs.readFileSync(CTX_SCRIPT_DEST, 'utf8');
123
+ const bundled = fs.readFileSync(CTX_SCRIPT_SRC, 'utf8');
124
+ if (existing === bundled) {
125
+ console.log(` ${green('✓')} Up to date`);
126
+ ctxInstalled = true;
127
+ } else if (await prompt(` Different version found. Update? [Y/n] `)) {
128
+ copyScript(CTX_SCRIPT_SRC, CTX_SCRIPT_DEST);
129
+ console.log(` ${green('✓')} Updated`);
130
+ ctxInstalled = true;
156
131
  } else {
157
132
  console.log(` ${dim('Skipped')}`);
158
133
  }
134
+ } else if (await prompt(` Not found. Install? [Y/n] `)) {
135
+ copyScript(CTX_SCRIPT_SRC, CTX_SCRIPT_DEST);
136
+ console.log(` ${green('✓')} Installed`);
137
+ ctxInstalled = true;
138
+ } else {
139
+ console.log(` ${dim('Skipped')}`);
159
140
  }
160
141
 
161
- // 5. StatusLine setup (separate approval)
162
- const CTX_COMMAND = '~/.claude/hooks/context-status.sh';
163
- let statusLineUpdated = false;
142
+ // 4. StatusLine config in settings.json
164
143
  if (ctxInstalled) {
144
+ let settings;
145
+ try {
146
+ settings = fs.existsSync(SETTINGS_PATH)
147
+ ? JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf8'))
148
+ : {};
149
+ } catch {
150
+ console.log(` ${red('✗')} Malformed JSON in settings.json — skipping statusline config`);
151
+ printSummary();
152
+ return;
153
+ }
154
+
155
+ const CTX_COMMAND = '~/.claude/hooks/context-status.sh';
165
156
  const hasCtx = settings.statusLine?.command?.includes('context-status.sh');
166
157
  if (hasCtx) {
167
158
  console.log(`\n StatusLine: ${green('✓')} Already configured`);
168
- statusLineUpdated = true;
169
159
  } else if (!settings.statusLine) {
170
160
  console.log(`\n StatusLine: ${dim('not configured')}`);
171
161
  if (await prompt(` Set up context tracking statusline? [Y/n] `)) {
172
162
  settings.statusLine = { command: CTX_COMMAND };
173
163
  fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
174
164
  console.log(` ${green('✓')} StatusLine configured`);
175
- statusLineUpdated = true;
176
165
  } else {
177
166
  console.log(` ${dim('Skipped')}`);
178
167
  }
@@ -183,51 +172,63 @@ async function runInstall() {
183
172
  settings.statusLine.command = `${CTX_COMMAND} | ${existing}`;
184
173
  fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
185
174
  console.log(` ${green('✓')} StatusLine updated`);
186
- statusLineUpdated = true;
187
175
  } else {
188
176
  console.log(` ${dim('Skipped')}`);
189
177
  }
190
178
  }
191
179
  }
192
180
 
193
- printSummary(hookInstalled, settingsUpdated);
181
+ printSummary();
194
182
  }
195
183
 
196
- function printSummary(hookOk, settingsOk) {
197
- console.log('');
198
- if (hookOk && settingsOk) {
199
- console.log(` ${green('Agent Log will appear in the Kanban footer when subagents are active.')}`);
200
- } else {
201
- console.log(` ${yellow('Partial install — re-run --install to complete setup.')}`);
202
- }
203
- console.log('');
184
+ function printSummary() {
185
+ console.log(`\n ${green('Setup complete. Agent activity will appear in the Kanban dashboard.')}\n`);
204
186
  }
205
187
 
206
188
  async function runUninstall() {
207
- console.log(`\n ${bold('claude-code-kanban')} — Agent Log hook uninstaller\n`);
189
+ console.log(`\n ${bold('claude-code-kanban')} — Uninstaller\n`);
190
+
191
+ // 1. Uninstall plugin via Claude CLI
192
+ process.stdout.write(' Removing plugin... ');
193
+ const uninst = runCLI('claude plugin uninstall claude-code-kanban', ['not found', 'not installed']);
194
+ if (uninst.ok) {
195
+ console.log(uninst.idempotent ? dim('Not installed') : green('✓ Removed'));
196
+ } else {
197
+ console.log(yellow(`⚠ ${uninst.error}`));
198
+ }
199
+
200
+ // 2. Remove marketplace
201
+ process.stdout.write(' Removing marketplace... ');
202
+ const rmMkt = runCLI('claude plugin marketplace remove claude-code-kanban', ['not found', 'not configured']);
203
+ if (rmMkt.ok) {
204
+ console.log(rmMkt.idempotent ? dim('Not configured') : green('✓ Removed'));
205
+ } else {
206
+ console.log(yellow(`⚠ ${rmMkt.error}`));
207
+ }
208
+
209
+ // 3. Remove plugin copy
210
+ if (fs.existsSync(PLUGIN_DEST)) {
211
+ fs.rmSync(PLUGIN_DEST, { recursive: true, force: true });
212
+ console.log(` Plugin copy: ${green('✓')} Removed`);
213
+ } else {
214
+ console.log(` Plugin copy: ${dim('Not found')}`);
215
+ }
216
+
217
+ // 4. Remove context-status.sh copy
218
+ if (fs.existsSync(CTX_SCRIPT_DEST)) {
219
+ fs.unlinkSync(CTX_SCRIPT_DEST);
220
+ console.log(` Context spy: ${green('✓')} Removed`);
221
+ } else {
222
+ console.log(` Context spy: ${dim('Not found')}`);
223
+ }
208
224
 
209
- // 1. Remove hook entries from settings.json
225
+ // 5. Clean up settings.json (statusLine)
210
226
  if (fs.existsSync(SETTINGS_PATH)) {
211
227
  try {
212
228
  const settings = JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf8'));
213
- let removed = 0;
214
- if (settings.hooks) {
215
- const eventNames = [...new Set(HOOK_EVENTS.map(e => e.event))];
216
- for (const event of eventNames) {
217
- if (!Array.isArray(settings.hooks[event])) continue;
218
- const before = settings.hooks[event].length;
219
- settings.hooks[event] = settings.hooks[event].map(g => {
220
- if (!g.hooks?.some(h => h.command === HOOK_COMMAND)) return g;
221
- const filtered = g.hooks.filter(h => h.command !== HOOK_COMMAND);
222
- return filtered.length > 0 ? { ...g, hooks: filtered } : null;
223
- }).filter(Boolean);
224
- removed += before - settings.hooks[event].length;
225
- if (settings.hooks[event].length === 0) delete settings.hooks[event];
226
- }
227
- if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
228
- }
229
+ let changed = false;
229
230
 
230
- // Strip context-status.sh from statusLine, restore downstream command if any
231
+ // Strip context-status.sh from statusLine
231
232
  if (settings.statusLine?.command?.includes('context-status.sh')) {
232
233
  const cmd = settings.statusLine.command;
233
234
  const stripped = cmd.replace(/~\/\.claude\/hooks\/context-status\.sh\s*\|\s*/, '').trim();
@@ -238,43 +239,15 @@ async function runUninstall() {
238
239
  delete settings.statusLine;
239
240
  console.log(` StatusLine: ${green('✓')} Removed`);
240
241
  }
242
+ changed = true;
241
243
  }
242
244
 
243
- fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
244
- if (removed > 0) {
245
- console.log(` Settings: ${green('✓')} Removed ${removed} hook entries`);
246
- } else {
247
- console.log(` Settings: ${dim('No hook entries found')}`);
245
+ if (changed) {
246
+ fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
248
247
  }
249
248
  } catch {
250
249
  console.log(` Settings: ${red('✗')} Could not parse settings.json`);
251
250
  }
252
- } else {
253
- console.log(` Settings: ${dim('No settings.json found')}`);
254
- }
255
-
256
- // 2. Remove hook scripts
257
- if (fs.existsSync(HOOK_SCRIPT_DEST)) {
258
- fs.unlinkSync(HOOK_SCRIPT_DEST);
259
- console.log(` Hook script: ${green('✓')} Removed`);
260
- } else {
261
- console.log(` Hook script: ${dim('Not found')}`);
262
- }
263
- if (fs.existsSync(CTX_SCRIPT_DEST)) {
264
- fs.unlinkSync(CTX_SCRIPT_DEST);
265
- console.log(` Context spy: ${green('✓')} Removed`);
266
- } else {
267
- console.log(` Context spy: ${dim('Not found')}`);
268
- }
269
-
270
- // 3. Optionally remove agent-activity data
271
- if (fs.existsSync(AGENT_ACTIVITY_DIR)) {
272
- if (await prompt(`\n Remove agent activity data (${AGENT_ACTIVITY_DIR})? [y/N] `)) {
273
- fs.rmSync(AGENT_ACTIVITY_DIR, { recursive: true, force: true });
274
- console.log(` ${green('✓')} Agent activity data removed`);
275
- } else {
276
- console.log(` ${dim('Kept agent activity data')}`);
277
- }
278
251
  }
279
252
 
280
253
  console.log(`\n ${green('Uninstall complete.')}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-kanban",
3
- "version": "2.4.0",
3
+ "version": "3.0.0-rc.2",
4
4
  "description": "A web-based Kanban board for viewing Claude Code tasks with agent teams support",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -45,8 +45,7 @@
45
45
  "files": [
46
46
  "server.js",
47
47
  "install.js",
48
- "hooks/agent-spy.sh",
49
- "hooks/context-status.sh",
48
+ "plugin/**/*",
50
49
  "lib/**/*",
51
50
  "public/**/*"
52
51
  ],
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "claude-code-kanban",
3
+ "owner": {
4
+ "name": "NikiforovAll"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "claude-code-kanban",
9
+ "source": "./plugins/claude-code-kanban",
10
+ "description": "Agent activity tracking for claude-code-kanban dashboard"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "claude-code-kanban",
3
+ "version": "1.0.0",
4
+ "description": "Agent activity tracking for claude-code-kanban dashboard",
5
+ "hooks": "./hooks/hooks.json"
6
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/agent-spy.sh",
10
+ "timeout": 5
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "SubagentStart": [
16
+ {
17
+ "matcher": "",
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/agent-spy.sh",
22
+ "timeout": 5
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "SubagentStop": [
28
+ {
29
+ "matcher": "",
30
+ "hooks": [
31
+ {
32
+ "type": "command",
33
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/agent-spy.sh",
34
+ "timeout": 5
35
+ }
36
+ ]
37
+ }
38
+ ],
39
+ "TeammateIdle": [
40
+ {
41
+ "matcher": "",
42
+ "hooks": [
43
+ {
44
+ "type": "command",
45
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/agent-spy.sh",
46
+ "timeout": 5
47
+ }
48
+ ]
49
+ }
50
+ ],
51
+ "PermissionRequest": [
52
+ {
53
+ "matcher": "",
54
+ "hooks": [
55
+ {
56
+ "type": "command",
57
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/agent-spy.sh",
58
+ "timeout": 5
59
+ }
60
+ ]
61
+ }
62
+ ],
63
+ "PreToolUse": [
64
+ {
65
+ "matcher": "AskUserQuestion",
66
+ "hooks": [
67
+ {
68
+ "type": "command",
69
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/agent-spy.sh",
70
+ "timeout": 5
71
+ }
72
+ ]
73
+ }
74
+ ],
75
+ "PostToolUse": [
76
+ {
77
+ "matcher": "",
78
+ "hooks": [
79
+ {
80
+ "type": "command",
81
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/agent-spy.sh",
82
+ "timeout": 5
83
+ }
84
+ ]
85
+ }
86
+ ]
87
+ }
88
+ }
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  # Tracks subagent lifecycle: one JSON file per agent, grouped by session
3
- # Layout: ~/.claude/agent-activity/{sessionId}/{agentId}.json
3
+ # Layout: ~/.claude/.cck/agent-activity/{sessionId}/{agentId}.json
4
4
 
5
5
  INPUT=$(cat)
6
6
 
@@ -16,12 +16,14 @@ eval "$(echo "$INPUT" | jq -r '
16
16
 
17
17
  [ -z "$SESSION_ID" ] && exit 0
18
18
 
19
+ CCK_ACTIVITY="$HOME/.claude/.cck/agent-activity"
20
+
19
21
  # Map session to custom task list on session start
20
22
  if [ "$EVENT" = "SessionStart" ]; then
21
23
  TASK_LIST_ID="${CLAUDE_CODE_TASK_LIST_ID:-}"
22
24
  if [ -n "$TASK_LIST_ID" ]; then
23
25
  CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
24
- MAPS_DIR="$HOME/.claude/agent-activity/_task-maps"
26
+ MAPS_DIR="$CCK_ACTIVITY/_task-maps"
25
27
  mkdir -p "$MAPS_DIR"
26
28
  MAP_FILE="$MAPS_DIR/$TASK_LIST_ID.json"
27
29
  TMP_FILE="$MAP_FILE.$$"
@@ -36,7 +38,7 @@ fi
36
38
 
37
39
  # PostToolUse / non-waiting PreToolUse: clear waiting state
38
40
  if [ "$EVENT" = "PostToolUse" ] || { [ "$EVENT" = "PreToolUse" ] && [ "$TOOL_NAME" != "AskUserQuestion" ]; }; then
39
- WFILE="$HOME/.claude/agent-activity/$SESSION_ID/_waiting.json"
41
+ WFILE="$CCK_ACTIVITY/$SESSION_ID/_waiting.json"
40
42
  rm -f "$WFILE"
41
43
  [ "$EVENT" = "PostToolUse" ] && exit 0
42
44
  fi
@@ -46,7 +48,7 @@ fi
46
48
 
47
49
  # Waiting-for-user events → write _waiting.json marker
48
50
  if [ "$EVENT" = "PermissionRequest" ] || { [ "$EVENT" = "PreToolUse" ] && [ "$TOOL_NAME" = "AskUserQuestion" ]; }; then
49
- DIR="$HOME/.claude/agent-activity/$SESSION_ID"
51
+ DIR="$CCK_ACTIVITY/$SESSION_ID"
50
52
  mkdir -p "$DIR"
51
53
  KIND="permission"
52
54
  [ "$EVENT" = "PreToolUse" ] && KIND="question"
@@ -63,7 +65,7 @@ fi
63
65
 
64
66
  # TeammateIdle has no agent_id — resolve via name→id mapping file
65
67
  if [ "$EVENT" = "TeammateIdle" ] && [ -z "$AGENT_ID" ] && [ -n "$TEAMMATE_NAME" ]; then
66
- DIR="$HOME/.claude/agent-activity/$SESSION_ID"
68
+ DIR="$CCK_ACTIVITY/$SESSION_ID"
67
69
  MAP_FILE="$DIR/_name-${TEAMMATE_NAME}.id"
68
70
  [ ! -f "$MAP_FILE" ] && exit 0
69
71
  AGENT_ID=$(cat "$MAP_FILE")
@@ -83,7 +85,7 @@ fi
83
85
 
84
86
  [ -z "$AGENT_ID" ] && exit 0
85
87
 
86
- DIR="$HOME/.claude/agent-activity/$SESSION_ID"
88
+ DIR="$CCK_ACTIVITY/$SESSION_ID"
87
89
  FILE="$DIR/$AGENT_ID.json"
88
90
 
89
91
  # On Start: skip if no type (internal agents like AskUserQuestion)
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  # Statusline spy: writes raw context data for kanban dashboard, passes input through
3
- # Layout: ~/.claude/context-status/{sessionId}.json
3
+ # Layout: ~/.claude/.cck/context-status/{sessionId}.json
4
4
  #
5
5
  # Usage: pipe before your statusline command:
6
6
  # "command": "~/.claude/hooks/context-status.sh | npx -y ccstatusline@latest"
@@ -9,7 +9,7 @@ INPUT=$(cat)
9
9
 
10
10
  SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // ""')
11
11
  if [ -n "$SESSION_ID" ]; then
12
- DIR="$HOME/.claude/context-status"
12
+ DIR="$HOME/.claude/.cck/context-status"
13
13
  mkdir -p "$DIR"
14
14
  echo "$INPUT" > "$DIR/$SESSION_ID.json"
15
15
  fi
package/server.js CHANGED
@@ -64,8 +64,9 @@ const TASKS_DIR = path.join(CLAUDE_DIR, 'tasks');
64
64
  const PROJECTS_DIR = path.join(CLAUDE_DIR, 'projects');
65
65
  const TEAMS_DIR = path.join(CLAUDE_DIR, 'teams');
66
66
  const PLANS_DIR = path.join(CLAUDE_DIR, 'plans');
67
- const AGENT_ACTIVITY_DIR = path.join(CLAUDE_DIR, 'agent-activity');
68
- const CONTEXT_STATUS_DIR = path.join(CLAUDE_DIR, 'context-status');
67
+ const CCK_DIR = path.join(CLAUDE_DIR, '.cck');
68
+ const AGENT_ACTIVITY_DIR = path.join(CCK_DIR, 'agent-activity');
69
+ const CONTEXT_STATUS_DIR = path.join(CCK_DIR, 'context-status');
69
70
 
70
71
  const PERMISSION_TTL_MS = 1800000;
71
72
  const AGENT_TTL_MS = 3600000;