@mcpware/claude-code-organizer 0.2.4 → 0.2.5

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.
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "claude-code-organizer",
3
- "version": "0.1.0",
3
+ "version": "0.2.5",
4
4
  "description": "Organize all your Claude Code memories, skills, MCP servers, and hooks — view by scope hierarchy, move between scopes via drag-and-drop",
5
5
  "author": {
6
- "name": "mcpware",
7
- "url": "https://github.com/mcpware"
6
+ "name": "ithiria894",
7
+ "url": "https://github.com/ithiria894"
8
8
  },
9
9
  "homepage": "https://github.com/mcpware/claude-code-organizer",
10
10
  "repository": "https://github.com/mcpware/claude-code-organizer",
package/.mcp.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "claude-code-organizer": {
4
+ "type": "stdio",
5
+ "command": "node",
6
+ "args": ["./bin/cli.mjs"]
7
+ }
8
+ }
9
+ }
package/README.ja.md CHANGED
@@ -43,6 +43,21 @@ Claude Code に「これ覚えて」って言ったのに、**違うスコープ
43
43
  - **依存ゼロ** — 純粋な Node.js ビルトインモジュールのみ、SortableJS は CDN 経由
44
44
  - **ガチのファイル移動** — `~/.claude/` 内のファイルを実際に移動する。閲覧専用ツールじゃない
45
45
 
46
+ ## なぜビジュアルダッシュボード?
47
+
48
+ Claude Code は CLI でファイルの一覧表示や移動ができる。じゃあなぜこのツールが必要?
49
+
50
+ | やりたいこと | CLI / Skill | ビジュアルダッシュボード |
51
+ |-------------|:-----------:|:-------------------:|
52
+ | **全体像** — 全スコープのメモリ・スキル・MCPサーバーを一度に見る | 長いテキスト出力をスクロール | スコープツリーで一目瞭然 |
53
+ | **スコープ横断の把握** — Global vs Workspace vs Project の継承関係を理解 | 複数コマンドを実行して頭の中で組み立てる | インデント付きツリー階層 |
54
+ | **スコープ間でアイテム移動** | 正確なパスを覚えてコマンド入力 | ドラッグ&ドロップ |
55
+ | **中身をプレビュー** | ファイルを1つずつ `cat` | クリック → サイドパネル |
56
+ | **横断検索** | `grep` + 手動フィルタリング | リアルタイム検索 + カテゴリフィルター |
57
+ | **何を持っているか把握** | ディレクトリごとに自分でファイル数を数える | スコープ×カテゴリごとの自動カウント |
58
+
59
+ テキスト出力では得られない**全体像**が手に入る — スコープツリー全体が見えて、置き場所の間違いを即座に発見、ドラッグで修正。コマンドを覚える必要もパスを打つ必要もない。
60
+
46
61
  ## クイックスタート
47
62
 
48
63
  ```bash
package/README.ko.md CHANGED
@@ -43,6 +43,21 @@ Claude Code한테 "이거 기억해"라고 했는데, **엉뚱한 스코프에
43
43
  - **의존성 제로** — 순수 Node.js 내장 모듈, SortableJS는 CDN으로
44
44
  - **진짜 파일 이동** — `~/.claude/` 안의 파일을 실제로 옮깁니다. 보기만 하는 뷰어가 아닙니다
45
45
 
46
+ ## 왜 비주얼 대시보드인가?
47
+
48
+ Claude Code는 CLI로도 파일 목록 확인과 이동이 가능합니다. 그런데 왜 이 도구가 필요할까요?
49
+
50
+ | 하고 싶은 것 | CLI / Skill | 비주얼 대시보드 |
51
+ |-------------|:-----------:|:-------------:|
52
+ | **전체 그림** — 모든 스코프의 메모리, 스킬, MCP 서버를 한 번에 보기 | 긴 텍스트 출력 스크롤 | 스코프 트리로 한눈에 |
53
+ | **스코프 간 인식** — Global vs Workspace vs Project 상속 관계 파악 | 여러 명령어 실행 후 머릿속으로 조합 | 들여쓰기된 트리 계층 |
54
+ | **스코프 간 항목 이동** | 정확한 경로 기억 + 명령어 입력 | 드래그 앤 드롭 |
55
+ | **내용 미리보기** | 파일 하나씩 `cat` | 클릭 → 사이드 패널 |
56
+ | **전체 검색** | `grep` + 수동 필터링 | 실시간 검색 + 카테고리 필터 |
57
+ | **보유 현황 파악** | 디렉토리별 파일 수를 직접 세기 | 스코프×카테고리별 자동 카운트 |
58
+
59
+ 텍스트 출력으로는 얻을 수 없는 **전체 그림**을 제공합니다 — 스코프 트리 전체가 보이고, 잘못 배치된 항목을 즉시 발견하고, 드래그로 수정. 명령어를 외울 필요도 경로를 입력할 필요도 없습니다.
60
+
46
61
  ## 빠른 시작
47
62
 
48
63
  ```bash
package/README.md CHANGED
@@ -43,6 +43,21 @@ You have a deploy skill sitting in global, but it only makes sense for one repo.
43
43
  - **Zero dependencies** — Pure Node.js built-in modules, SortableJS via CDN
44
44
  - **Real file moves** — Actually moves files in `~/.claude/`, not just a viewer
45
45
 
46
+ ## Why a Visual Dashboard?
47
+
48
+ Claude Code can already list and move files via CLI. So why does this tool exist?
49
+
50
+ | What you need | CLI / Skill | Visual Dashboard |
51
+ |---------------|:-----------:|:----------------:|
52
+ | **Big picture** — see every memory, skill, MCP server across all scopes at once | Scroll through long text output | Scope tree, one glance |
53
+ | **Cross-scope awareness** — understand Global vs Workspace vs Project inheritance | Run multiple commands, piece it together | Tree hierarchy with indentation |
54
+ | **Move items between scopes** | Remember exact paths, type commands | Drag-and-drop |
55
+ | **Preview content** | `cat` each file one by one | Click → side panel |
56
+ | **Search across everything** | `grep` with manual filtering | Real-time search + category filters |
57
+ | **Understand what you have** | Count files per directory yourself | Automatic counts per category per scope |
58
+
59
+ The dashboard gives you the **big picture that text output can't** — you see the full scope tree, spot misplaced items instantly, and fix them with a drag. No commands to memorize, no paths to type.
60
+
46
61
  ## Quick Start
47
62
 
48
63
  ```bash
package/README.zh-CN.md CHANGED
@@ -43,6 +43,21 @@
43
43
  - **零依赖** — 纯 Node.js 内置模块,SortableJS 走 CDN
44
44
  - **真·文件移动** — 直接操作 `~/.claude/` 目录里的文件,不是什么只读查看器
45
45
 
46
+ ## 为什么需要可视化仪表盘?
47
+
48
+ Claude Code 用 CLI 就能列出和移动文件。那为什么还要这个工具?
49
+
50
+ | 你想做的事 | CLI / Skill | 可视化仪表盘 |
51
+ |-----------|:-----------:|:----------:|
52
+ | **全局视野** — 一次看到所有作用域的记忆、技能、MCP 服务器 | 滚动一大段文本输出 | 作用域树,一目了然 |
53
+ | **跨作用域感知** — 理解 Global vs Workspace vs Project 的继承关系 | 跑多条命令,脑内拼凑 | 带缩进的树状层级 |
54
+ | **跨作用域移动** | 记住准确路径,敲命令 | 拖拽搞定 |
55
+ | **预览内容** | 一个个 `cat` 文件 | 点击 → 侧边面板 |
56
+ | **全局搜索** | `grep` + 手动筛选 | 实时搜索 + 分类过滤 |
57
+ | **了解你有什么** | 自己数每个目录里的文件 | 按作用域×类别自动统计 |
58
+
59
+ 仪表盘给你**文字输出无法提供的全局视野** — 完整的作用域树一览无余,一眼发现放错位置的东西,拖一下就修好。不用背命令,不用打路径。
60
+
46
61
  ## 快速上手
47
62
 
48
63
  ```bash
package/README.zh-TW.md CHANGED
@@ -43,6 +43,21 @@
43
43
  - **零相依** — 純 Node.js 內建模組,SortableJS 走 CDN
44
44
  - **真・檔案搬移** — 直接動 `~/.claude/` 裡的檔案,不是只能看的 viewer
45
45
 
46
+ ## 點解要用視覺化儀表板?
47
+
48
+ Claude Code 用 CLI 都可以 list 同搬 file。咁點解仲要呢個工具?
49
+
50
+ | 你想做嘅嘢 | CLI / Skill | 視覺化儀表板 |
51
+ |-----------|:-----------:|:----------:|
52
+ | **全局視野** — 一次睇晒所有 scope 嘅記憶、技能、MCP 伺服器 | Scroll 一大段 text output | Scope tree,一眼睇晒 |
53
+ | **跨 scope 認知** — 理解 Global vs Workspace vs Project 嘅繼承關係 | 行幾條 command,自己喺腦入面砌 | 有縮排嘅 tree 層級 |
54
+ | **跨 scope 搬移** | 記住準確路徑,打 command | 拖拉搞掂 |
55
+ | **預覽內容** | 逐個 `cat` 檔案 | 撳一下 → 側邊面板 |
56
+ | **全局搜尋** | `grep` + 自己 filter | 即時搜尋 + 分類篩選 |
57
+ | **了解你有咩** | 自己數每個目錄幾多個 file | 按 scope × 類別自動統計 |
58
+
59
+ 儀表板俾你**text output 做唔到嘅全局視野** — 成個 scope tree 一覽無遺,一眼就見到擺錯位嘅嘢,拖一下就搞掂。唔使背 command,唔使打 path。
60
+
46
61
  ## 快速開始
47
62
 
48
63
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcpware/claude-code-organizer",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Organize all your Claude Code memories, skills, MCP servers, and hooks — view by scope hierarchy, move between scopes via drag-and-drop",
5
5
  "type": "module",
6
6
  "bin": {
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/mcpware/claude-code-organizer",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.2.4",
9
+ "version": "0.2.5",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@mcpware/claude-code-organizer",
14
- "version": "0.2.4",
14
+ "version": "0.2.5",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: organize
3
+ description: Open the Claude Code Organizer dashboard — view and manage all memories, skills, MCP servers, hooks, and configs across scopes
4
+ argument-hint: [--port <number>]
5
+ allowed-tools: [Bash, Read]
6
+ ---
7
+
8
+ # Organize — Claude Code Dashboard
9
+
10
+ Launch the Claude Code Organizer dashboard to visually manage all your customizations.
11
+
12
+ ## What to do
13
+
14
+ 1. Run the organizer server:
15
+
16
+ ```bash
17
+ npx @mcpware/claude-code-organizer $ARGUMENTS
18
+ ```
19
+
20
+ 2. Tell the user the dashboard is opening in their browser at `http://localhost:3847` (or the next available port).
21
+
22
+ 3. If the user provides `--port <number>`, pass it through as the argument.
23
+
24
+ ## What this does
25
+
26
+ Opens a drag-and-drop web dashboard showing:
27
+ - All memories, skills, MCP servers, hooks, configs, and plugins
28
+ - Organized by scope hierarchy (Global → Workspace → Project)
29
+ - Move items between scopes via drag-and-drop
30
+ - Search, filter, and preview any item
31
+ - Delete items with undo support
package/src/scanner.mjs CHANGED
@@ -175,6 +175,44 @@ async function discoverScopes() {
175
175
  return scopes;
176
176
  }
177
177
 
178
+ // ── Skill bundle detection (via skills-lock.json) ───────────────────
179
+
180
+ /**
181
+ * Load skill bundle info from skills-lock.json files.
182
+ * Returns a Map of skillName → { source, sourceType }.
183
+ *
184
+ * Checks:
185
+ * 1. Project-level: <repoDir>/skills-lock.json (version 1)
186
+ * 2. Global: ~/.agents/.skill-lock.json (version 3)
187
+ */
188
+ async function loadSkillBundles(repoDir) {
189
+ const bundles = new Map();
190
+
191
+ // Paths to check (project-level first, then global)
192
+ const lockPaths = [];
193
+ if (repoDir) lockPaths.push(join(repoDir, "skills-lock.json"));
194
+ lockPaths.push(join(HOME, ".agents", ".skill-lock.json"));
195
+
196
+ for (const lockPath of lockPaths) {
197
+ const content = await safeReadFile(lockPath);
198
+ if (!content) continue;
199
+ try {
200
+ const lock = JSON.parse(content);
201
+ const skills = lock.skills || {};
202
+ for (const [name, entry] of Object.entries(skills)) {
203
+ if (!bundles.has(name) && entry.source) {
204
+ bundles.set(name, {
205
+ source: entry.source,
206
+ sourceType: entry.sourceType || "unknown",
207
+ });
208
+ }
209
+ }
210
+ } catch {}
211
+ }
212
+
213
+ return bundles;
214
+ }
215
+
178
216
  // ── Item scanners ────────────────────────────────────────────────────
179
217
 
180
218
  async function scanMemories(scope) {
@@ -224,10 +262,14 @@ async function scanSkills(scope) {
224
262
  if (await exists(dir)) skillDirs.push(dir);
225
263
  }
226
264
 
265
+ // Load bundle info from skills-lock.json
266
+ const bundleMap = await loadSkillBundles(scope.repoDir);
267
+
227
268
  for (const skillsRoot of skillDirs) {
228
269
  const entries = await readdir(skillsRoot, { withFileTypes: true });
229
270
  for (const entry of entries) {
230
- if (!entry.isDirectory()) continue;
271
+ // Support both real directories and symlinks pointing to directories
272
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
231
273
  // Skip "private" directory (usually copies of global skills)
232
274
  if (entry.name === "private") continue;
233
275
 
@@ -268,6 +310,9 @@ async function scanSkills(scope) {
268
310
  if (fs) totalSize += fs.size;
269
311
  }
270
312
 
313
+ // Bundle detection from skills-lock.json
314
+ const bundleInfo = bundleMap.get(entry.name);
315
+
271
316
  items.push({
272
317
  category: "skill",
273
318
  scopeId: scope.id,
@@ -280,6 +325,7 @@ async function scanSkills(scope) {
280
325
  fileCount,
281
326
  mtime: s ? s.mtime.toISOString().slice(0, 10) : "",
282
327
  path: skillDir,
328
+ bundle: bundleInfo?.source || null,
283
329
  });
284
330
  }
285
331
  }
package/src/ui/app.js CHANGED
@@ -192,7 +192,61 @@ function renderScope(scope, depth) {
192
192
  // Render each category
193
193
  for (const [cat, catItems] of Object.entries(categories)) {
194
194
  const catConfig = CATEGORIES[cat] || { icon: "📄", label: cat.toUpperCase(), group: null };
195
- html += `
195
+
196
+ // Skills: group by bundle if any items have bundle info
197
+ if (cat === "skill") {
198
+ const bundled = {}; // source → items[]
199
+ const unbundled = []; // items without bundle
200
+ for (const item of catItems) {
201
+ if (item.bundle) {
202
+ (bundled[item.bundle] ??= []).push(item);
203
+ } else {
204
+ unbundled.push(item);
205
+ }
206
+ }
207
+
208
+ const hasBundles = Object.keys(bundled).length > 0;
209
+
210
+ html += `
211
+ <div class="cat-hdr" data-cat="${esc(cat)}">
212
+ <span class="cat-tog">▼</span>
213
+ <span class="cat-ico">${catConfig.icon}</span>
214
+ <span class="cat-nm">${catConfig.label}</span>
215
+ <span class="cat-cnt">${catItems.length}</span>
216
+ </div>
217
+ <div class="cat-body" data-cat="${esc(cat)}">`;
218
+
219
+ // Render each bundle as a collapsible sub-group
220
+ for (const [source, bundleItems] of Object.entries(bundled)) {
221
+ const bundleName = source.split("/").pop(); // "pbakaus/impeccable" → "impeccable"
222
+ const bundleLabel = source; // full "owner/repo"
223
+ html += `
224
+ <div class="bundle-hdr" data-bundle="${esc(source)}">
225
+ <span class="bundle-tog">▼</span>
226
+ <span class="bundle-ico">📦</span>
227
+ <span class="bundle-nm">${esc(bundleName)}</span>
228
+ <span class="bundle-src">${esc(bundleLabel)}</span>
229
+ <span class="bundle-cnt">${bundleItems.length}</span>
230
+ </div>
231
+ <div class="bundle-body" data-bundle="${esc(source)}">
232
+ <div class="sortable-zone" data-scope="${esc(scope.id)}" data-group="${catConfig.group || 'none'}">
233
+ ${bundleItems.map(item => renderItem(item)).join("")}
234
+ </div>
235
+ </div>`;
236
+ }
237
+
238
+ // Render unbundled skills flat (no group header)
239
+ if (unbundled.length > 0) {
240
+ html += `
241
+ <div class="sortable-zone" data-scope="${esc(scope.id)}" data-group="${catConfig.group || 'none'}">
242
+ ${unbundled.map(item => renderItem(item)).join("")}
243
+ </div>`;
244
+ }
245
+
246
+ html += `</div>`;
247
+ } else {
248
+ // Non-skill categories: render flat as before
249
+ html += `
196
250
  <div class="cat-hdr" data-cat="${esc(cat)}">
197
251
  <span class="cat-tog">▼</span>
198
252
  <span class="cat-ico">${catConfig.icon}</span>
@@ -204,6 +258,7 @@ function renderScope(scope, depth) {
204
258
  ${catItems.map(item => renderItem(item)).join("")}
205
259
  </div>
206
260
  </div>`;
261
+ }
207
262
  }
208
263
 
209
264
  // Render child scopes
@@ -281,6 +336,15 @@ function saveExpandState() {
281
336
  expandState.cats.add(catKey);
282
337
  }
283
338
  });
339
+ // Save bundle expand state
340
+ document.querySelectorAll(".bundle-hdr").forEach(hdr => {
341
+ const body = hdr.nextElementSibling;
342
+ const scopeId = hdr.closest(".scope-block")?.querySelector(".scope-hdr")?.dataset.scopeId || "";
343
+ const bundleKey = `${scopeId}::bundle::${hdr.dataset.bundle}`;
344
+ if (body && !body.classList.contains("c")) {
345
+ expandState.cats.add(bundleKey);
346
+ }
347
+ });
284
348
  }
285
349
 
286
350
  function restoreExpandState() {
@@ -384,6 +448,24 @@ function initSortable() {
384
448
  });
385
449
  });
386
450
 
451
+ // Bundle toggle — default collapsed
452
+ document.querySelectorAll(".bundle-hdr").forEach(hdr => {
453
+ const body = hdr.nextElementSibling;
454
+ const tog = hdr.querySelector(".bundle-tog");
455
+ // Default collapsed
456
+ const scopeId = hdr.closest(".scope-block")?.querySelector(".scope-hdr")?.dataset.scopeId || "";
457
+ const bundleKey = `${scopeId}::bundle::${hdr.dataset.bundle}`;
458
+ if (!expandState.cats.has(bundleKey)) {
459
+ body?.classList.add("c");
460
+ tog?.classList.add("c");
461
+ }
462
+ hdr.addEventListener("click", (e) => {
463
+ e.stopPropagation();
464
+ body.classList.toggle("c");
465
+ tog.classList.toggle("c");
466
+ });
467
+ });
468
+
387
469
  // Item click → detail panel
388
470
  document.querySelectorAll(".item-row").forEach(row => {
389
471
  row.addEventListener("click", (e) => {
@@ -633,6 +715,19 @@ function setupExpandToggle() {
633
715
  tog?.classList.add("c");
634
716
  }
635
717
  });
718
+
719
+ // Also expand/collapse bundles
720
+ document.querySelectorAll(".bundle-hdr").forEach(hdr => {
721
+ const body = hdr.nextElementSibling;
722
+ const tog = hdr.querySelector(".bundle-tog");
723
+ if (allExpanded) {
724
+ body?.classList.remove("c");
725
+ tog?.classList.remove("c");
726
+ } else {
727
+ body?.classList.add("c");
728
+ tog?.classList.add("c");
729
+ }
730
+ });
636
731
  });
637
732
  }
638
733
 
@@ -670,7 +765,22 @@ function setupSearch() {
670
765
  row.style.display = (!q || text.includes(q)) ? "" : "none";
671
766
  });
672
767
 
673
- // 2. Hide category sections where all items are hidden
768
+ // 2a. Hide bundle groups where all items are hidden
769
+ document.querySelectorAll(".bundle-hdr").forEach(bundleHdr => {
770
+ const bundleBody = bundleHdr.nextElementSibling;
771
+ if (!bundleBody) return;
772
+ const rows = bundleBody.querySelectorAll(".item-row");
773
+ const anyVisible = rows.length === 0 || [...rows].some(r => r.style.display !== "none");
774
+ bundleHdr.style.display = anyVisible ? "" : "none";
775
+ bundleBody.style.display = anyVisible ? "" : "none";
776
+ // When searching, auto-expand visible bundles
777
+ if (q && anyVisible) {
778
+ bundleBody.classList.remove("c");
779
+ bundleHdr.querySelector(".bundle-tog")?.classList.remove("c");
780
+ }
781
+ });
782
+
783
+ // 2b. Hide category sections where all items are hidden
674
784
  document.querySelectorAll(".cat-hdr").forEach(catHdr => {
675
785
  const catBody = catHdr.nextElementSibling;
676
786
  if (!catBody) return;
package/src/ui/style.css CHANGED
@@ -128,6 +128,25 @@ body {
128
128
  .cat-body { padding:0; }
129
129
  .cat-body.c { display:none; }
130
130
 
131
+ /* ══════════════════════════════════════
132
+ BUNDLE SUB-GROUP — Inside skill category
133
+ ══════════════════════════════════════ */
134
+ .bundle-hdr {
135
+ display:flex; align-items:center; gap:8px;
136
+ padding:6px 12px; margin:3px 0 2px 64px; cursor:pointer;
137
+ background:var(--surface); border:1px solid var(--border-light);
138
+ border-radius:7px; user-select:none; transition:all 0.15s;
139
+ }
140
+ .bundle-hdr:hover { background:var(--hover); border-color:var(--border); }
141
+ .bundle-tog { width:12px; text-align:center; color:var(--text-faint); font-size:0.55rem; transition:transform 0.15s; flex-shrink:0; }
142
+ .bundle-tog.c { transform:rotate(-90deg); }
143
+ .bundle-ico { font-size:0.75rem; }
144
+ .bundle-nm { font-size:0.68rem; color:var(--text-secondary); font-weight:600; }
145
+ .bundle-src { font-size:0.58rem; color:var(--text-faint); font-family:'SF Mono','Cascadia Code','Fira Code','Courier New',monospace; }
146
+ .bundle-cnt { font-size:0.58rem; color:var(--text-muted); margin-left:auto; font-weight:500; }
147
+ .bundle-body { padding:0; }
148
+ .bundle-body.c { display:none; }
149
+
131
150
  /* ══════════════════════════════════════
132
151
  ITEM ROW — Least prominent
133
152
  ══════════════════════════════════════ */