claude-code-kanban 2.4.0 → 3.0.0-rc.1
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 +117 -177
- package/package.json +2 -3
- package/plugin/.claude-plugin/marketplace.json +13 -0
- package/plugin/plugins/claude-code-kanban/.claude-plugin/plugin.json +6 -0
- package/plugin/plugins/claude-code-kanban/hooks/hooks.json +88 -0
- package/{hooks → plugin/plugins/claude-code-kanban/scripts}/agent-spy.sh +8 -6
- package/{hooks → plugin/plugins/claude-code-kanban/scripts}/context-status.sh +2 -2
- package/server.js +3 -2
package/install.js
CHANGED
|
@@ -9,22 +9,9 @@ const { execSync } = require('child_process');
|
|
|
9
9
|
const CLAUDE_DIR = path.join(os.homedir(), '.claude');
|
|
10
10
|
const HOOKS_DIR = path.join(CLAUDE_DIR, 'hooks');
|
|
11
11
|
const SETTINGS_PATH = path.join(CLAUDE_DIR, 'settings.json');
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const PLUGIN_DIR = path.join(__dirname, 'plugin');
|
|
13
|
+
const CTX_SCRIPT_SRC = path.join(PLUGIN_DIR, 'plugins', 'claude-code-kanban', 'scripts', 'context-status.sh');
|
|
14
14
|
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
15
|
|
|
29
16
|
// ANSI helpers
|
|
30
17
|
const green = s => `\x1b[32m${s}\x1b[0m`;
|
|
@@ -43,136 +30,113 @@ function prompt(question) {
|
|
|
43
30
|
});
|
|
44
31
|
}
|
|
45
32
|
|
|
33
|
+
function runCLI(cmd, okPatterns = []) {
|
|
34
|
+
try {
|
|
35
|
+
const out = execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
36
|
+
return { ok: true, output: out };
|
|
37
|
+
} catch (e) {
|
|
38
|
+
const stderr = e.stderr?.trim() || e.message;
|
|
39
|
+
if (okPatterns.some(p => stderr.includes(p))) return { ok: true, idempotent: true };
|
|
40
|
+
return { ok: false, error: stderr };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function copyScript(src, dest) {
|
|
45
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
46
|
+
fs.copyFileSync(src, dest);
|
|
47
|
+
try { fs.chmodSync(dest, 0o755); } catch {}
|
|
48
|
+
}
|
|
49
|
+
|
|
46
50
|
async function runInstall() {
|
|
47
|
-
console.log(`\n ${bold('claude-code-kanban')} —
|
|
51
|
+
console.log(`\n ${bold('claude-code-kanban')} — Plugin & StatusLine installer\n`);
|
|
48
52
|
|
|
49
|
-
// 1. Check
|
|
50
|
-
process.stdout.write(' Checking
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
console.log(green(`✓ found (${
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
} 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
|
-
}
|
|
66
|
-
}
|
|
53
|
+
// 1. Check prerequisites
|
|
54
|
+
process.stdout.write(' Checking claude CLI... ');
|
|
55
|
+
const claude = runCLI('claude --version');
|
|
56
|
+
if (claude.ok) {
|
|
57
|
+
console.log(green(`✓ found (${claude.output})`));
|
|
58
|
+
} else {
|
|
59
|
+
console.log(red('✗ claude CLI not found'));
|
|
60
|
+
console.log(` ${dim('Install Claude Code CLI first: https://docs.anthropic.com/en/docs/claude-code')}`);
|
|
61
|
+
return;
|
|
67
62
|
}
|
|
68
63
|
|
|
69
|
-
// 2. Check jq
|
|
70
64
|
process.stdout.write(' Checking jq... ');
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
console.log(green(`✓ found (${
|
|
74
|
-
}
|
|
75
|
-
console.log(yellow('⚠ not found — hook
|
|
65
|
+
const jq = runCLI('jq --version');
|
|
66
|
+
if (jq.ok) {
|
|
67
|
+
console.log(green(`✓ found (${jq.output})`));
|
|
68
|
+
} else {
|
|
69
|
+
console.log(yellow('⚠ not found — hook scripts require jq for JSON parsing'));
|
|
76
70
|
}
|
|
77
71
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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;
|
|
72
|
+
// 2. Register marketplace & install plugin via Claude CLI
|
|
73
|
+
console.log(`\n Plugin: ${dim(PLUGIN_DIR)}`);
|
|
74
|
+
if (await prompt(` Install claude-code-kanban plugin? [Y/n] `)) {
|
|
75
|
+
process.stdout.write(' Registering marketplace... ');
|
|
76
|
+
const mkt = runCLI(`claude plugin marketplace add "${PLUGIN_DIR}"`, ['already', 'exists']);
|
|
77
|
+
if (mkt.ok) {
|
|
78
|
+
console.log(green(mkt.idempotent ? '✓ already registered' : '✓'));
|
|
79
|
+
} else {
|
|
80
|
+
console.log(yellow(`⚠ ${mkt.error}`));
|
|
103
81
|
}
|
|
104
|
-
console.log(` ${dim('Skipped')}`);
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
|
|
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);
|
|
111
82
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
if (fs.existsSync(SETTINGS_PATH)) {
|
|
117
|
-
settings = JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf8'));
|
|
83
|
+
const inst = runCLI('claude plugin install claude-code-kanban@claude-code-kanban', ['already installed', 'already exists']);
|
|
84
|
+
if (inst.ok) {
|
|
85
|
+
console.log(` ${green('✓')} ${inst.idempotent ? 'Already installed' : 'Plugin installed'}`);
|
|
118
86
|
} else {
|
|
119
|
-
|
|
87
|
+
console.log(` ${red('✗')} Plugin install failed: ${inst.error}`);
|
|
120
88
|
}
|
|
121
|
-
}
|
|
122
|
-
console.log(` ${
|
|
123
|
-
printSummary(hookInstalled, false);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
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 });
|
|
89
|
+
} else {
|
|
90
|
+
console.log(` ${dim('Skipped')}`);
|
|
137
91
|
}
|
|
138
92
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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;
|
|
93
|
+
// 3. StatusLine setup (context-status.sh must be copied globally since statusLine is not plugin-scoped)
|
|
94
|
+
console.log(`\n Context spy: ${dim(CTX_SCRIPT_DEST)}`);
|
|
95
|
+
let ctxInstalled = false;
|
|
96
|
+
if (fs.existsSync(CTX_SCRIPT_DEST)) {
|
|
97
|
+
const existing = fs.readFileSync(CTX_SCRIPT_DEST, 'utf8');
|
|
98
|
+
const bundled = fs.readFileSync(CTX_SCRIPT_SRC, 'utf8');
|
|
99
|
+
if (existing === bundled) {
|
|
100
|
+
console.log(` ${green('✓')} Up to date`);
|
|
101
|
+
ctxInstalled = true;
|
|
102
|
+
} else if (await prompt(` Different version found. Update? [Y/n] `)) {
|
|
103
|
+
copyScript(CTX_SCRIPT_SRC, CTX_SCRIPT_DEST);
|
|
104
|
+
console.log(` ${green('✓')} Updated`);
|
|
105
|
+
ctxInstalled = true;
|
|
156
106
|
} else {
|
|
157
107
|
console.log(` ${dim('Skipped')}`);
|
|
158
108
|
}
|
|
109
|
+
} else if (await prompt(` Not found. Install? [Y/n] `)) {
|
|
110
|
+
copyScript(CTX_SCRIPT_SRC, CTX_SCRIPT_DEST);
|
|
111
|
+
console.log(` ${green('✓')} Installed`);
|
|
112
|
+
ctxInstalled = true;
|
|
113
|
+
} else {
|
|
114
|
+
console.log(` ${dim('Skipped')}`);
|
|
159
115
|
}
|
|
160
116
|
|
|
161
|
-
//
|
|
162
|
-
const CTX_COMMAND = '~/.claude/hooks/context-status.sh';
|
|
163
|
-
let statusLineUpdated = false;
|
|
117
|
+
// 4. StatusLine config in settings.json
|
|
164
118
|
if (ctxInstalled) {
|
|
119
|
+
let settings;
|
|
120
|
+
try {
|
|
121
|
+
settings = fs.existsSync(SETTINGS_PATH)
|
|
122
|
+
? JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf8'))
|
|
123
|
+
: {};
|
|
124
|
+
} catch {
|
|
125
|
+
console.log(` ${red('✗')} Malformed JSON in settings.json — skipping statusline config`);
|
|
126
|
+
printSummary();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const CTX_COMMAND = '~/.claude/hooks/context-status.sh';
|
|
165
131
|
const hasCtx = settings.statusLine?.command?.includes('context-status.sh');
|
|
166
132
|
if (hasCtx) {
|
|
167
133
|
console.log(`\n StatusLine: ${green('✓')} Already configured`);
|
|
168
|
-
statusLineUpdated = true;
|
|
169
134
|
} else if (!settings.statusLine) {
|
|
170
135
|
console.log(`\n StatusLine: ${dim('not configured')}`);
|
|
171
136
|
if (await prompt(` Set up context tracking statusline? [Y/n] `)) {
|
|
172
137
|
settings.statusLine = { command: CTX_COMMAND };
|
|
173
138
|
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
|
|
174
139
|
console.log(` ${green('✓')} StatusLine configured`);
|
|
175
|
-
statusLineUpdated = true;
|
|
176
140
|
} else {
|
|
177
141
|
console.log(` ${dim('Skipped')}`);
|
|
178
142
|
}
|
|
@@ -183,51 +147,55 @@ async function runInstall() {
|
|
|
183
147
|
settings.statusLine.command = `${CTX_COMMAND} | ${existing}`;
|
|
184
148
|
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
|
|
185
149
|
console.log(` ${green('✓')} StatusLine updated`);
|
|
186
|
-
statusLineUpdated = true;
|
|
187
150
|
} else {
|
|
188
151
|
console.log(` ${dim('Skipped')}`);
|
|
189
152
|
}
|
|
190
153
|
}
|
|
191
154
|
}
|
|
192
155
|
|
|
193
|
-
printSummary(
|
|
156
|
+
printSummary();
|
|
194
157
|
}
|
|
195
158
|
|
|
196
|
-
function printSummary(
|
|
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('');
|
|
159
|
+
function printSummary() {
|
|
160
|
+
console.log(`\n ${green('Setup complete. Agent activity will appear in the Kanban dashboard.')}\n`);
|
|
204
161
|
}
|
|
205
162
|
|
|
206
163
|
async function runUninstall() {
|
|
207
|
-
console.log(`\n ${bold('claude-code-kanban')} —
|
|
164
|
+
console.log(`\n ${bold('claude-code-kanban')} — Uninstaller\n`);
|
|
165
|
+
|
|
166
|
+
// 1. Uninstall plugin via Claude CLI
|
|
167
|
+
process.stdout.write(' Removing plugin... ');
|
|
168
|
+
const uninst = runCLI('claude plugin uninstall claude-code-kanban', ['not found', 'not installed']);
|
|
169
|
+
if (uninst.ok) {
|
|
170
|
+
console.log(uninst.idempotent ? dim('Not installed') : green('✓ Removed'));
|
|
171
|
+
} else {
|
|
172
|
+
console.log(yellow(`⚠ ${uninst.error}`));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 2. Remove marketplace
|
|
176
|
+
process.stdout.write(' Removing marketplace... ');
|
|
177
|
+
const rmMkt = runCLI('claude plugin marketplace remove claude-code-kanban', ['not found', 'not configured']);
|
|
178
|
+
if (rmMkt.ok) {
|
|
179
|
+
console.log(rmMkt.idempotent ? dim('Not configured') : green('✓ Removed'));
|
|
180
|
+
} else {
|
|
181
|
+
console.log(yellow(`⚠ ${rmMkt.error}`));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 3. Remove context-status.sh copy
|
|
185
|
+
if (fs.existsSync(CTX_SCRIPT_DEST)) {
|
|
186
|
+
fs.unlinkSync(CTX_SCRIPT_DEST);
|
|
187
|
+
console.log(` Context spy: ${green('✓')} Removed`);
|
|
188
|
+
} else {
|
|
189
|
+
console.log(` Context spy: ${dim('Not found')}`);
|
|
190
|
+
}
|
|
208
191
|
|
|
209
|
-
//
|
|
192
|
+
// 4. Clean up settings.json (statusLine)
|
|
210
193
|
if (fs.existsSync(SETTINGS_PATH)) {
|
|
211
194
|
try {
|
|
212
195
|
const settings = JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf8'));
|
|
213
|
-
let
|
|
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
|
-
}
|
|
196
|
+
let changed = false;
|
|
229
197
|
|
|
230
|
-
// Strip context-status.sh from statusLine
|
|
198
|
+
// Strip context-status.sh from statusLine
|
|
231
199
|
if (settings.statusLine?.command?.includes('context-status.sh')) {
|
|
232
200
|
const cmd = settings.statusLine.command;
|
|
233
201
|
const stripped = cmd.replace(/~\/\.claude\/hooks\/context-status\.sh\s*\|\s*/, '').trim();
|
|
@@ -238,43 +206,15 @@ async function runUninstall() {
|
|
|
238
206
|
delete settings.statusLine;
|
|
239
207
|
console.log(` StatusLine: ${green('✓')} Removed`);
|
|
240
208
|
}
|
|
209
|
+
changed = true;
|
|
241
210
|
}
|
|
242
211
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
console.log(` Settings: ${green('✓')} Removed ${removed} hook entries`);
|
|
246
|
-
} else {
|
|
247
|
-
console.log(` Settings: ${dim('No hook entries found')}`);
|
|
212
|
+
if (changed) {
|
|
213
|
+
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
|
|
248
214
|
}
|
|
249
215
|
} catch {
|
|
250
216
|
console.log(` Settings: ${red('✗')} Could not parse settings.json`);
|
|
251
217
|
}
|
|
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
218
|
}
|
|
279
219
|
|
|
280
220
|
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": "
|
|
3
|
+
"version": "3.0.0-rc.1",
|
|
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
|
-
"
|
|
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,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="$
|
|
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="$
|
|
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="$
|
|
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="$
|
|
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="$
|
|
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
|
|
68
|
-
const
|
|
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;
|