claude-code-kanban 7.4.0 → 7.6.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.
- package/README.md +5 -3
- package/cli.js +95 -29
- package/hub-app.json +5 -0
- package/lib/panes.js +41 -6
- package/lib/parsers.js +42 -3
- package/lib/retention.js +54 -6
- package/lib/scratchpad-dir.js +19 -2
- package/lib/session-cache.js +1 -1
- package/lib/session-events.js +174 -47
- package/lib/show.js +385 -0
- package/lib/task-dir.js +26 -0
- package/lib/terminal-client.js +14 -1
- package/lib/terminal-host.js +2 -1
- package/lib/terminal.js +28 -8
- package/package.json +1 -1
- package/plugin/plugins/claude-code-kanban/.claude-plugin/plugin.json +2 -5
- package/plugin/plugins/claude-code-kanban/hooks/doorbell.ts +136 -0
- package/plugin/plugins/claude-code-kanban/hooks/index.ts +4 -0
- package/plugin/plugins/claude-code-kanban/hooks/show.ts +120 -0
- package/plugin/plugins/claude-code-kanban/skills/dispatch/SKILL.md +2 -0
- package/plugin/plugins/claude-code-kanban/skills/kanban/SKILL.md +3 -3
- package/plugin/plugins/claude-code-kanban/skills/show/SKILL.md +45 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/NOTICE.md +27 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/chart.md +58 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram/architecture.md +18 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram/er.md +10 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram/flow.md +13 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram/layers.md +10 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram/sequence.md +19 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram/state.md +12 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram/swimlane.md +10 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram/timeline.md +9 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram/tree.md +10 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/diagram.md +87 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/explain.md +98 -0
- package/plugin/plugins/claude-code-kanban/skills/show/references/mockup.md +30 -0
- package/plugin/plugins/claude-code-kanban/tests/doorbell.test.ts +133 -0
- package/plugin/plugins/claude-code-kanban/tests/show.test.ts +137 -0
- package/public/app.js +2151 -1108
- package/public/index.html +19 -36
- package/public/style.css +1047 -125
- package/public/terminal-frame.js +0 -2
- package/public/tree-highlight.js +137 -15
- package/server.js +237 -178
- package/lib/dispatch.js +0 -32
- package/plugin/plugins/claude-code-kanban/monitors.json +0 -8
- package/plugin/plugins/claude-code-kanban/scripts/postman.js +0 -82
- package/plugin/plugins/claude-code-kanban/skills/follow/SKILL.md +0 -47
package/public/terminal-frame.js
CHANGED
package/public/tree-highlight.js
CHANGED
|
@@ -11,7 +11,8 @@ const treeHighlight = (() => {
|
|
|
11
11
|
String.raw`^([+-](?=[ \t│├└▼]|$))?([ \t]*(?:[│├└─▼↓→ \t]*[│├└─▼↓→][ \t]*)?)(${NAME})?(.*)$`,
|
|
12
12
|
'su',
|
|
13
13
|
);
|
|
14
|
-
const
|
|
14
|
+
const FRAME = new RegExp(String.raw`^( *)(~.*?|${NAME}(?:\([^()]*\))?)( \*)?(?:( {2,})(\S(?:.*\S)?))?( *)$`, 'u');
|
|
15
|
+
const LOC = /^[^\s:]+:\d+(?::\d+)?$/;
|
|
15
16
|
|
|
16
17
|
function escapeText(s) {
|
|
17
18
|
return s
|
|
@@ -36,25 +37,61 @@ const treeHighlight = (() => {
|
|
|
36
37
|
return false;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
// The notation of skills/show/references/explain.md: frames indented by spaces, one name or
|
|
41
|
+
// call each, an optional ` *` focus mark, a note after 2+ spaces, `~` folds, and `+`/`-` in
|
|
42
|
+
// column 1 for a change. Prose and pseudocode have single-spaced words, so they never parse.
|
|
43
|
+
function parseStack(text) {
|
|
44
|
+
const lines = text.split('\n');
|
|
45
|
+
const diff = lines.some((l) => /^[+-]/.test(l));
|
|
46
|
+
const rows = [];
|
|
47
|
+
let roots = [];
|
|
48
|
+
let stack = [];
|
|
49
|
+
for (const line of lines) {
|
|
50
|
+
if (!line.trim()) {
|
|
51
|
+
rows.push({ blank: true, text: line });
|
|
52
|
+
roots = [];
|
|
53
|
+
stack = [];
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const marker = diff ? line[0] : '';
|
|
57
|
+
if (diff && !/[ +-]/.test(marker)) return null;
|
|
58
|
+
const m = line.slice(marker.length).match(FRAME);
|
|
59
|
+
if (!m) return null;
|
|
60
|
+
const [, ind, label, star = '', gap = '', note = '', tail] = m;
|
|
61
|
+
while (stack.length && stack[stack.length - 1].indent >= ind.length) stack.pop();
|
|
62
|
+
const parent = stack[stack.length - 1] || null;
|
|
63
|
+
const sibs = parent ? parent.kids : roots;
|
|
64
|
+
const row = {
|
|
65
|
+
id: rows.length,
|
|
66
|
+
marker,
|
|
67
|
+
indent: ind.length,
|
|
68
|
+
parent,
|
|
69
|
+
sibs,
|
|
70
|
+
label,
|
|
71
|
+
fold: label.startsWith('~'),
|
|
72
|
+
star,
|
|
73
|
+
gap,
|
|
74
|
+
note,
|
|
75
|
+
tail,
|
|
76
|
+
kids: [],
|
|
77
|
+
};
|
|
78
|
+
sibs.push(row);
|
|
79
|
+
stack.push(row);
|
|
80
|
+
rows.push(row);
|
|
50
81
|
}
|
|
51
|
-
return
|
|
82
|
+
return rows;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// An untagged call tree: 3 frames at least, on 2 indent levels at least.
|
|
86
|
+
function looksLikeIndentTree(text) {
|
|
87
|
+
const frames = parseStack(text)?.filter((r) => !r.blank) || [];
|
|
88
|
+
return frames.length >= 3 && new Set(frames.map((r) => r.indent)).size >= 2;
|
|
52
89
|
}
|
|
53
90
|
|
|
54
91
|
function wantsTree(lang, text) {
|
|
55
92
|
const tag = (lang || '').trim().toLowerCase();
|
|
56
93
|
if (TREE_TAGS.has(tag)) return true;
|
|
57
|
-
return PLAIN_TAGS.has(tag) && (looksLikeTree(text) || looksLikeIndentTree(text
|
|
94
|
+
return PLAIN_TAGS.has(tag) && (looksLikeTree(text) || looksLikeIndentTree(text));
|
|
58
95
|
}
|
|
59
96
|
|
|
60
97
|
function highlightLine(line) {
|
|
@@ -68,7 +105,92 @@ const treeHighlight = (() => {
|
|
|
68
105
|
return text.split('\n').map(highlightLine).join('\n');
|
|
69
106
|
}
|
|
70
107
|
|
|
71
|
-
|
|
108
|
+
const isLast = (row) => row.sibs[row.sibs.length - 1] === row;
|
|
109
|
+
|
|
110
|
+
function guideCells(row) {
|
|
111
|
+
const g = Array(row.indent).fill('');
|
|
112
|
+
if (row.parent) {
|
|
113
|
+
for (let a = row.parent; a.parent; a = a.parent) if (!isLast(a)) g[a.parent.indent] = 'v';
|
|
114
|
+
g[row.parent.indent] = isLast(row) ? 'l' : 't';
|
|
115
|
+
if (row.parent.indent + 1 < row.indent) g[row.parent.indent + 1] = 'h';
|
|
116
|
+
}
|
|
117
|
+
return g.map((c) => (c ? `<span class="ct-g ct-${c}"> </span>` : ' ')).join('');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function renderRow(row) {
|
|
121
|
+
if (row.blank) return `<span class="ct-row">${row.text}</span>`;
|
|
122
|
+
const cls = ['ct-row'];
|
|
123
|
+
if (row.star) cls.push('ct-focus');
|
|
124
|
+
if (row.marker === '+') cls.push('ct-add');
|
|
125
|
+
if (row.marker === '-') cls.push('ct-del');
|
|
126
|
+
const above = [];
|
|
127
|
+
for (let a = row.parent; a; a = a.parent) above.push(a);
|
|
128
|
+
if (above.some((a) => !a.open)) cls.push('ct-hidden');
|
|
129
|
+
if (above.some((a) => a.fold)) cls.push('ct-lib');
|
|
130
|
+
let attrs = above.length ? ` data-ct-in="${above.map((a) => a.id).join(' ')}"` : '';
|
|
131
|
+
let label;
|
|
132
|
+
if (row.fold) {
|
|
133
|
+
cls.push('ct-fold');
|
|
134
|
+
label = `<span class="ct-tilde">~</span>${escapeText(row.label.slice(1))}`;
|
|
135
|
+
} else {
|
|
136
|
+
label = span('ct-name', row.label);
|
|
137
|
+
}
|
|
138
|
+
if (row.kids.length) {
|
|
139
|
+
cls.push('ct-has');
|
|
140
|
+
attrs += ` data-ct="${row.id}" role="button" tabindex="0" aria-expanded="${row.open}"`;
|
|
141
|
+
}
|
|
142
|
+
return (
|
|
143
|
+
`<span class="${cls.join(' ')}"${attrs}>` +
|
|
144
|
+
span('ct-m', row.marker) +
|
|
145
|
+
guideCells(row) +
|
|
146
|
+
label +
|
|
147
|
+
span('ct-star', row.star) +
|
|
148
|
+
row.gap +
|
|
149
|
+
span(LOC.test(row.note) ? 'ct-loc' : 'ct-note', row.note) +
|
|
150
|
+
row.tail +
|
|
151
|
+
'</span>'
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Rows are blocks, so the newlines between them sit in hidden spans: the text stays
|
|
156
|
+
// byte-identical and the rows draw no blank lines.
|
|
157
|
+
function renderTree(text) {
|
|
158
|
+
const rows = parseStack(text);
|
|
159
|
+
if (!rows) return `<pre><code class="hljs language-tree">${highlightTree(text)}</code></pre>`;
|
|
160
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
161
|
+
const r = rows[i];
|
|
162
|
+
if (r.parent && (r.star || r.marker === '+' || r.marker === '-' || r.holdsPoint)) r.parent.holdsPoint = true;
|
|
163
|
+
}
|
|
164
|
+
for (const r of rows) r.open = !r.fold || !!r.holdsPoint;
|
|
165
|
+
return `<pre><code class="hljs ct">${rows.map(renderRow).join('<span class="ct-nl">\n</span>')}</code></pre>`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function toggleFold(fold) {
|
|
169
|
+
fold.setAttribute('aria-expanded', String(fold.getAttribute('aria-expanded') !== 'true'));
|
|
170
|
+
const code = fold.closest('code');
|
|
171
|
+
const closed = new Set();
|
|
172
|
+
for (const f of code.querySelectorAll('.ct-has[aria-expanded="false"]')) closed.add(f.dataset.ct);
|
|
173
|
+
for (const r of code.querySelectorAll('[data-ct-in]')) {
|
|
174
|
+
r.classList.toggle(
|
|
175
|
+
'ct-hidden',
|
|
176
|
+
r.dataset.ctIn.split(' ').some((id) => closed.has(id)),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// A note in the LOC form → { path, line }, else null.
|
|
182
|
+
function parseLoc(text) {
|
|
183
|
+
if (!LOC.test(text)) return null;
|
|
184
|
+
const [path, line] = text.split(':');
|
|
185
|
+
return { path, line: Math.max(1, Number(line)) };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// A trailing newline does not start a line.
|
|
189
|
+
function lineCount(text) {
|
|
190
|
+
return Math.max(1, text.split('\n').length - (text.endsWith('\n') ? 1 : 0));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return { wantsTree, highlightTree, renderTree, toggleFold, parseLoc, lineCount };
|
|
72
194
|
})();
|
|
73
195
|
|
|
74
196
|
if (typeof module === 'object' && module.exports) module.exports = treeHighlight;
|