kodelyth-ecc 1.7.3 → 1.7.4

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 (51) hide show
  1. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +41 -0
  3. package/.github/ISSUE_TEMPLATE/good_first_issue.md +36 -0
  4. package/CHANGELOG.md +39 -0
  5. package/GITHUB_SETUP.md +553 -0
  6. package/README.md +51 -6
  7. package/VERSION +1 -1
  8. package/actions/ecc-review/README.md +7 -7
  9. package/bundles/enterprise.md +1 -1
  10. package/bundles/indie-hacker.md +1 -1
  11. package/bundles/red-team.md +1 -1
  12. package/docs/supply-chain.md +1 -1
  13. package/package.json +4 -1
  14. package/rules/common/memory-protocol.md +35 -4
  15. package/scripts/dashboard/data.js +261 -4
  16. package/scripts/dashboard/server.js +28 -3
  17. package/scripts/dashboard/static/index.html +63 -3
  18. package/social/card-install.svg +1 -1
  19. package/social/card-main.svg +2 -2
  20. package/social/facebook-v150.svg +4 -4
  21. package/social/github-social-preview.svg +3 -3
  22. package/social/hype-compound-learning.svg +117 -0
  23. package/social/hype-devil-mode.svg +228 -0
  24. package/social/hype-mcp-server.svg +136 -0
  25. package/social/hype-parallel-agents.svg +150 -0
  26. package/social/hype-stats-hero.svg +126 -0
  27. package/social/readme-hero.svg +4 -4
  28. package/social/section-mcp.svg +1 -1
  29. package/social/twitter-threads.md +621 -0
  30. package/social/x-card-agents-grid.svg +2 -2
  31. package/social/x-card-free.svg +2 -2
  32. package/social/x-card-hook.svg +5 -5
  33. package/tests/dashboard/data.test.js +156 -0
  34. package/tests/dashboard/server.test.js +17 -0
  35. package/wiki/FAQ.md +1 -1
  36. package/wiki/Home.md +5 -3
  37. package/wiki/Installation-Guide.md +2 -2
  38. package/MARKETING_PLAN.md +0 -893
  39. package/cat +0 -0
  40. package/social/facebook-group/POST.md +0 -121
  41. package/social/facebook-group/fb-1-3am-debug.png +0 -0
  42. package/social/facebook-group/fb-1-3am-debug.svg +0 -97
  43. package/social/facebook-group/fb-2-cpu-upgrade.png +0 -0
  44. package/social/facebook-group/fb-2-cpu-upgrade.svg +0 -132
  45. package/social/facebook-group/fb-3-before-after.png +0 -0
  46. package/social/facebook-group/fb-3-before-after.svg +0 -94
  47. package/videos/README.md +0 -88
  48. package/videos/video-01-install-and-routing.md +0 -258
  49. package/videos/video-02-compound-memory.md +0 -301
  50. package/videos/video-03-devil-mode.md +0 -294
  51. package/videos/video-04-mcp-server.md +0 -391
package/README.md CHANGED
@@ -2,13 +2,16 @@
2
2
 
3
3
  <img src="social/readme-hero.svg" alt="Kodelyth ECC — AI Agent Toolkit" width="900"/>
4
4
 
5
+ [![npm version](https://img.shields.io/npm/v/kodelyth-ecc.svg)](https://www.npmjs.com/package/kodelyth-ecc)
6
+ [![npm downloads](https://img.shields.io/npm/dm/kodelyth-ecc.svg)](https://www.npmjs.com/package/kodelyth-ecc)
7
+ [![GitHub Stars](https://img.shields.io/github/stars/sifxprime/kodelyth-ecc?style=social)](https://github.com/sifxprime/kodelyth-ecc/stargazers)
5
8
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+ ![Tests](https://img.shields.io/badge/tests-336%20passing-brightgreen)
6
10
  ![Claude Code](https://img.shields.io/badge/Claude%20Code-Ready-8A2BE2)
7
11
  ![Windsurf](https://img.shields.io/badge/Windsurf-Ready-06b6d4)
8
12
  ![Antigravity](https://img.shields.io/badge/Antigravity-Ready-00ADD8)
9
13
  ![Cursor](https://img.shields.io/badge/Cursor-Ready-3b82f6)
10
14
  ![Codex](https://img.shields.io/badge/Codex-Ready-10b981)
11
- ![Version](https://img.shields.io/badge/version-1.7.0-orange)
12
15
  ![Agents](https://img.shields.io/badge/Agents-70-brightgreen)
13
16
  ![Devil Mode](https://img.shields.io/badge/Devil%20Mode-new-red)
14
17
  ![Skills](https://img.shields.io/badge/Skills-194-brightgreen)
@@ -74,7 +77,7 @@ You never typed `use debug-detective`. You didn't have to. The toolkit read the
74
77
  | Quality hooks | ✅ 22+ | Some | ❌ | ❌ |
75
78
  | IDE platforms | **11** (Claude Code, Windsurf, Cursor, Codex, Antigravity, OpenCode, Cline, Roo Code, Aider, Kimi, Gemini CLI) | 1-2 | 1 | Varies |
76
79
  | Telemetry | ❌ none | Varies | ❌ | Varies |
77
- | Test coverage | ✅ 336 tests | ❌ | ❌ | ❌ |
80
+ | Test coverage | ✅ 348 tests | ❌ | ❌ | ❌ |
78
81
  | Distributed via | `npx`, curl, clone | Manual | Manual | Manual |
79
82
 
80
83
  ---
@@ -125,6 +128,30 @@ Feature depth varies by platform — hooks are a Claude Code native format, and
125
128
 
126
129
  Hooks use Claude Code's JSON settings format — no equivalent exists on other platforms. Cursor reads rules and skills from `.cursor/`; its agent system uses a different format not yet compatible with ECC agents.
127
130
 
131
+ ### Memory & dashboard reality per IDE
132
+
133
+ Memory storage is a single shared file at `~/.kodelyth/memory/memories.jsonl` — every IDE on the same machine reads and writes the **same** memories. The only thing that varies is *how* memories surface:
134
+
135
+ | Platform | Auto-recall on every prompt | Auto-capture on success | Manual recall via MCP tool | Dashboard "Live IDE activity" |
136
+ |---|---|---|---|---|
137
+ | **Claude Code** | ✓ (hook) | ✓ (hook) | ✓ | ✓ Claude session files |
138
+ | **Windsurf** | — | — | ✓ `recall_memory` | ✓ Windsurf + Windsurf-Next state |
139
+ | **Cursor** | — | — | ✓ `recall_memory` | ✓ workspace storage dirs |
140
+ | **Codex CLI** | — | — | ✓ `recall_memory` | — |
141
+ | **Antigravity** | — | — | ✓ `recall_memory` | ✓ `.agent/` in cwd |
142
+ | **Roo Code / Aider / Kimi / Cline / Gemini CLI / OpenCode** | — | — | ✓ if MCP-capable | — |
143
+
144
+ What this means in practice:
145
+ - A memory captured in **Claude Code** is recall-able from every other IDE the same day — the file is shared.
146
+ - In **Windsurf / Cursor / Codex / Antigravity**, the AI does NOT auto-fire memory recall; the `rules/common/memory-protocol.md` rule (installed automatically) tells the AI to call the `recall_memory` MCP tool proactively at the start of substantive prompts.
147
+ - The dashboard's **Sessions → Live IDE activity** tab surfaces session files for Claude Code, Windsurf, Windsurf-Next, Cursor, and Antigravity. Add custom paths via the `KODELYTH_EXTRA_IDE_WATCH` env var (comma-separated).
148
+
149
+ ```bash
150
+ # Watch additional paths in the dashboard
151
+ export KODELYTH_EXTRA_IDE_WATCH="$HOME/my-agent-logs,$HOME/other-tool/state"
152
+ npx kodelyth-ecc dashboard
153
+ ```
154
+
128
155
  ### Option 2 — npx from GitHub (always latest commit)
129
156
 
130
157
  ```bash
@@ -352,7 +379,13 @@ What's inside:
352
379
  | **Memory** | Browse, search, and manage your local BM25 memory store |
353
380
  | **Evolve** | Self-improving memory — review AI-proposed refinements |
354
381
  | **Catalog** | Full searchable index of all 70 agents, 194 skills, 97 commands |
355
- | **Sessions** | Browse past session excerpts and exported bundles |
382
+ | **Sessions** | **Live IDE activity** (Claude Code, Windsurf, Windsurf-Next, Cursor, Antigravity) + orchestration/swarm sessions |
383
+
384
+ Real-time:
385
+ - SSE push every 3 seconds, only when at least one browser tab is connected (zero CPU otherwise)
386
+ - Watches memory writes, evolve signals, token-budget changes, and IDE session files
387
+ - "Last activity" auto-updates without a page refresh
388
+ - Set `KODELYTH_EXTRA_IDE_WATCH=path1,path2` to watch additional paths
356
389
 
357
390
  Security design:
358
391
  - **GET-only** — no write endpoints accessible from the UI
@@ -752,12 +785,24 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for templates, checklists, and the full K
752
785
 
753
786
  ## Changelog
754
787
 
755
- See [CHANGELOG.md](CHANGELOG.md). v1.7.0 highlights:
788
+ See [CHANGELOG.md](CHANGELOG.md). v1.7.4 highlights:
789
+
790
+ - **Real-time IDE-aware dashboard** — Sessions tab now surfaces live Claude Code / Windsurf / Windsurf-Next / Cursor / Antigravity activity; SSE poll 10 s → 3 s; new `/api/ide-sessions` endpoint
791
+ - **Cross-IDE memory protocol** — `rules/common/memory-protocol.md` now explicitly instructs non-Claude IDEs (Windsurf, Cursor, Codex, Antigravity) to call the `recall_memory` and `capture_memory` MCP tools proactively
792
+ - **`KODELYTH_EXTRA_IDE_WATCH` env var** — add custom watch paths to the dashboard (comma/semicolon/colon-separated)
793
+ - **Watch-path fixes** — broken `proposals.jsonl`-only watch now also watches `routing-misses.jsonl` and the `token-budget/` directory
794
+ - **+12 dashboard tests** for IDE platform coverage and cross-platform path resolution
795
+
796
+ v1.7.3 highlights:
797
+
798
+ - **Social hype pack** — 5 X/Twitter SVG cards + 5 thread scripts, full SVG version sync, GitHub issue templates, repo polish
799
+
800
+ v1.7.0 highlights:
756
801
 
757
- - **MCP Server** — 16 tools, 6 prompts, 365 resources; stdio JSON-RPC; zero telemetry; works with Claude Desktop, LangGraph, AutoGen, CrewAI, OpenAI Agents SDK
802
+ - **MCP Server** — 16 tools, 6 prompts, 377 resources; stdio JSON-RPC; zero telemetry; works with Claude Desktop, LangGraph, AutoGen, CrewAI, OpenAI Agents SDK
758
803
  - **Dashboard hardening** — DNS rebinding protection, shell injection fix (`execFileSync`), CSP on all responses, SSE client cap (max 10), TOCTOU fixes
759
804
  - **Devil Mode** — 8 adversarial agents, `--pre-public` / `--pre-launch` / `--all` flags, real bash-grep patterns and remediation playbooks
760
- - **336 tests** passing across 24 test files
805
+ - **336 tests** passing across 24 test files (now 348 at v1.7.4)
761
806
 
762
807
  v1.5.8 highlights:
763
808
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.7.3
1
+ 1.7.4
@@ -29,7 +29,7 @@ jobs:
29
29
  with:
30
30
  fetch-depth: 0
31
31
 
32
- - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.6.0
32
+ - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.7.3
33
33
  with:
34
34
  bundle: red-team
35
35
  fail-on: critical
@@ -75,7 +75,7 @@ That's it. Every PR now gets reviewed by ECC's adversarial crew and the findings
75
75
  To run a custom set, use `agents:` instead:
76
76
 
77
77
  ```yaml
78
- - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.6.0
78
+ - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.7.3
79
79
  with:
80
80
  agents: secret-hunter,license-violation-finder,jailbreak-tester
81
81
  ```
@@ -87,7 +87,7 @@ To run a custom set, use `agents:` instead:
87
87
  ### 1. Indie Hacker — fast feedback, never block
88
88
 
89
89
  ```yaml
90
- - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.6.0
90
+ - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.7.3
91
91
  with:
92
92
  bundle: indie-hacker
93
93
  fail-on: none
@@ -99,7 +99,7 @@ PR gets a comment, but CI never fails. You decide what to address.
99
99
  ### 2. Red Team — full adversarial sweep, block on critical
100
100
 
101
101
  ```yaml
102
- - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.6.0
102
+ - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.7.3
103
103
  with:
104
104
  bundle: red-team
105
105
  fail-on: critical
@@ -111,7 +111,7 @@ Default. Recommended for any user-facing product.
111
111
  ### 3. Enterprise — strict, block on high
112
112
 
113
113
  ```yaml
114
- - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.6.0
114
+ - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.7.3
115
115
  with:
116
116
  bundle: enterprise
117
117
  fail-on: high
@@ -121,7 +121,7 @@ Default. Recommended for any user-facing product.
121
121
  ### 4. Pre-public-release — secret + license + AI safety only
122
122
 
123
123
  ```yaml
124
- - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.6.0
124
+ - uses: sifxprime/kodelyth-ecc/actions/ecc-review@v1.7.3
125
125
  with:
126
126
  agents: secret-hunter,license-violation-finder,prompt-injection-hunter
127
127
  fail-on: critical
@@ -236,7 +236,7 @@ Reduce findings by tightening `agents:` or `max-files:`. The action caps to firs
236
236
 
237
237
  - [Kodelyth ECC repo](https://github.com/sifxprime/kodelyth-ecc) — the toolkit this action runs
238
238
  - [`/devil-mode` command](../../commands/devil-mode.md) — same agents, run locally
239
- - [Devil Roadmap](../../) — the broader v1.6.0 → v2.0.0 plan
239
+ - [Devil Roadmap](../../) — the broader v1.7.3 → v2.0.0 plan
240
240
 
241
241
  ---
242
242
 
@@ -169,4 +169,4 @@ These provide additional audit playbooks on top of the enterprise baseline.
169
169
 
170
170
  ---
171
171
 
172
- **Powered by Kodelyth ECC v1.6.0+ · Enterprise bundle**
172
+ **Powered by Kodelyth ECC v1.7.4 · Enterprise bundle**
@@ -103,4 +103,4 @@ Run `/devil-mode --all` or `use <agent-name>` any time to access the full crew.
103
103
 
104
104
  ---
105
105
 
106
- **Powered by Kodelyth ECC v1.6.0+ · Indie Hacker bundle**
106
+ **Powered by Kodelyth ECC v1.7.4 · Indie Hacker bundle**
@@ -135,4 +135,4 @@ Each ~300 lines of real audit logic, not theatrical persona.
135
135
 
136
136
  ---
137
137
 
138
- **Powered by Kodelyth ECC v1.6.0+ · Red Team bundle**
138
+ **Powered by Kodelyth ECC v1.7.4 · Red Team bundle**
@@ -102,7 +102,7 @@ Exits `0` on `ok=true`, `1` otherwise. With `--json`, prints the full report:
102
102
 
103
103
  ```bash
104
104
  # 1) Download the manifest from the GitHub release that matches your installed version.
105
- gh release download v1.7.0 -p kodelyth-ecc-manifest.json -O /tmp/manifest.json
105
+ gh release download v1.7.3 -p kodelyth-ecc-manifest.json -O /tmp/manifest.json
106
106
 
107
107
  # 2) Run verify against your installed copy.
108
108
  npx kodelyth-ecc verify --root "$(npm root -g)/kodelyth-ecc" --manifest /tmp/manifest.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodelyth-ecc",
3
- "version": "1.7.3",
3
+ "version": "1.7.4",
4
4
  "description": "Production-grade AI coding toolkit — 70 agents (incl. devil-mode adversarial crew), 194 skills, 97 commands, parallel multi-agent commands, semantic intent routing, self-learning memory, and a built-in MCP server (16 tools / 6 prompts / 377 resources) that bridges to Claude Desktop, LangGraph, AutoGen, CrewAI, and OpenAI Agents SDK. Works with Claude Code, Windsurf, Cursor, Codex, Antigravity, OpenCode, Cline, RooCode, Aider, Kimi, and Gemini CLI.",
5
5
  "author": "Kodelyth <github.com/sifxprime>",
6
6
  "license": "MIT",
@@ -9,6 +9,9 @@
9
9
  "url": "https://github.com/sifxprime/kodelyth-ecc.git"
10
10
  },
11
11
  "homepage": "https://github.com/sifxprime/kodelyth-ecc",
12
+ "bugs": {
13
+ "url": "https://github.com/sifxprime/kodelyth-ecc/issues"
14
+ },
12
15
  "keywords": [
13
16
  "claude-code",
14
17
  "windsurf",
@@ -6,19 +6,37 @@
6
6
 
7
7
  A local file at `~/.kodelyth/memory/memories.jsonl` storing solutions, patterns, and gotchas extracted from past sessions. Retrieval is BM25 (keyword + tag matching). It is **not** a learned model — it is a retrieval store that gives you better context.
8
8
 
9
- If the SessionStart hook ran, you have already received a memory block in your initial context with:
9
+ **Cross-IDE: the same file is read/written by every IDE on this machine.** A memory captured in Claude Code is recall-able from Windsurf, Cursor, Antigravity, Codex, and any other MCP-capable client. There is one shared store.
10
+
11
+ If the SessionStart hook ran (Claude Code only — other IDEs do not have a hook system), you have already received a memory block in your initial context with:
10
12
  - The user's recurring patterns (tags seen across multiple sessions)
11
13
  - Recent solutions in this project
12
14
  - Their detected stack
13
15
 
16
+ If you are NOT in Claude Code (Windsurf / Cursor / Antigravity / Codex / etc.) you must proactively call the MCP tools below — no hook will surface memories for you automatically.
17
+
18
+ ## How to recall memory (works in EVERY IDE)
19
+
20
+ Call the Kodelyth MCP tool **`recall_memory`** with:
21
+ - `query`: the user's task or topic in their own words
22
+ - `project_root` (optional): the absolute path of the current project — scopes recall to that project's memories first
23
+ - `limit` (optional, default 5)
24
+
25
+ Equivalent CLIs (any terminal):
26
+ - `npx kodelyth-ecc mcp-call kodelyth recall_memory --query "<text>"`
27
+ - `node scripts/memory/cli.js search "<query>"` (if running from the repo)
28
+
14
29
  ## When to recall memory mid-session
15
30
 
16
- Trigger a memory recall (run the `kodelyth-memory` agent or `node scripts/memory/cli.js search "<query>"`) when **any** of these is true:
31
+ Trigger `recall_memory` when **any** of these is true:
17
32
 
18
- 1. The user describes a task in a domain that matches a tag in their memory (`payments`, `auth`, `database`, `deployment`, etc.)
33
+ 1. The user describes a task in a domain that likely matches a tag in their memory (`payments`, `auth`, `database`, `deployment`, `tailwind`, `prisma`, etc.)
19
34
  2. The user asks "have I done this before?" or "how did I solve X last time?"
20
- 3. The user mentions a library, framework, or service by name that appears in past memories
35
+ 3. The user mentions a library, framework, or service by name
21
36
  4. You're about to commit to an architectural decision and want to check past precedent
37
+ 5. **First substantive prompt of a non-Claude-Code session** — always recall once at the start so you don't miss prior context
38
+
39
+ Skip recall on trivial prompts (`ok`, `yes`, `thanks`), agent invocations (`use <agent>`, `@<agent>`), and meta-questions about ECC itself.
22
40
 
23
41
  ## How to surface a recalled memory
24
42
 
@@ -37,6 +55,19 @@ Capture when **all** are true:
37
55
 
38
56
  Show the user the draft memory before storing. Never silently capture.
39
57
 
58
+ ### How to capture (works in EVERY IDE)
59
+
60
+ Call the Kodelyth MCP tool **`capture_memory`** with:
61
+ - `problem`: short statement of what was hard (required)
62
+ - `approach`: the solution that worked, with the key insight (required)
63
+ - `tags`: array of short keywords (`['tailwind', 'css', 'v4']`) — these power recall
64
+ - `language` (optional): primary language of the fix
65
+ - `project_root` (optional): absolute path of the project
66
+ - `files` (optional): files touched
67
+ - `gotchas` (optional): array of pitfalls future-you should avoid
68
+
69
+ Equivalent CLI: `npx kodelyth-ecc mcp-call kodelyth capture_memory --problem "..." --approach "..."`
70
+
40
71
  ## What you must not do
41
72
 
42
73
  - **Never** auto-recall the same memory twice in one session (the user has seen it)
@@ -1,6 +1,6 @@
1
1
  // scripts/dashboard/data.js
2
2
  //
3
- // Local observability dashboard: pure aggregators (v1.7.0).
3
+ // Local observability dashboard: pure aggregators (v1.7.4).
4
4
  //
5
5
  // All data the dashboard renders comes from:
6
6
  // - The MCP catalog (filesystem reads of agents/, skills/, commands/, rules/, bundles/)
@@ -8,6 +8,7 @@
8
8
  // - The evolve signal streams (~/.kodelyth/evolve/)
9
9
  // - The token-budget hook state (${KODELYTH_TOKEN_BUDGET_DIR})
10
10
  // - The orchestration session dirs (.orchestration/<session>/)
11
+ // - Live IDE session activity (Claude Code, Windsurf, Antigravity)
11
12
  //
12
13
  // Every aggregator is pure: takes its inputs explicitly OR uses well-defined
13
14
  // env-overridable defaults. No global mutable state. Errors are swallowed
@@ -45,6 +46,35 @@ function defaultBudgetDir() {
45
46
  function defaultCoordRoot() {
46
47
  return process.env.KODELYTH_COORDINATION_ROOT || path.join(process.cwd(), '.orchestration');
47
48
  }
49
+ function defaultClaudeProjectsDir() {
50
+ return process.env.KODELYTH_CLAUDE_PROJECTS_DIR || path.join(os.homedir(), '.claude', 'projects');
51
+ }
52
+ function defaultWindsurfStateDir() {
53
+ return process.env.KODELYTH_WINDSURF_DIR || path.join(os.homedir(), '.codeium', 'windsurf');
54
+ }
55
+ function defaultWindsurfNextStateDir() {
56
+ // Windsurf Next (newer Codeium release) uses a separate state dir.
57
+ return process.env.KODELYTH_WINDSURF_NEXT_DIR || path.join(os.homedir(), '.codeium', 'windsurf-next');
58
+ }
59
+ function defaultCursorWorkspaceDir() {
60
+ if (process.env.KODELYTH_CURSOR_DIR) return process.env.KODELYTH_CURSOR_DIR;
61
+ const home = os.homedir();
62
+ switch (process.platform) {
63
+ case 'darwin': return path.join(home, 'Library', 'Application Support', 'Cursor', 'User', 'workspaceStorage');
64
+ case 'win32': return path.join(home, 'AppData', 'Roaming', 'Cursor', 'User', 'workspaceStorage');
65
+ default: return path.join(home, '.config', 'Cursor', 'User', 'workspaceStorage');
66
+ }
67
+ }
68
+ function defaultAntigravityStateDir() {
69
+ // Best-effort — Google Antigravity is in early access and the canonical session
70
+ // path is not yet documented. Users can override via KODELYTH_ANTIGRAVITY_DIR.
71
+ return process.env.KODELYTH_ANTIGRAVITY_DIR || path.join(os.homedir(), '.antigravity');
72
+ }
73
+ // Comma-separated extra dirs from env. Each path's mtime contributes to SSE.
74
+ function extraIdeWatchDirs() {
75
+ const raw = process.env.KODELYTH_EXTRA_IDE_WATCH || '';
76
+ return raw.split(/[,;:]/).map(s => s.trim()).filter(Boolean);
77
+ }
48
78
 
49
79
  function safeReadDir(p) {
50
80
  try { return fs.readdirSync(p, { withFileTypes: true }); } catch { return []; }
@@ -308,6 +338,225 @@ function safeReadExcerpt(p, max = 800) {
308
338
  } catch { return null; }
309
339
  }
310
340
 
341
+ // ── IDE sessions (Claude Code / Windsurf / Antigravity) ──────────────────────
342
+ //
343
+ // Surfaces the AI coding sessions the user is actually running RIGHT NOW.
344
+ // Read-only filesystem stats — never reads JSONL contents (could be 27MB+).
345
+ // Caps the walk to keep watcher latency predictable.
346
+
347
+ const IDE_WALK_FILE_CAP = 500; // hard ceiling so a runaway dir can't hang the watcher
348
+
349
+ function decodeClaudeProjectName(name) {
350
+ // Claude Code encodes the project's absolute path by replacing '/' with '-'.
351
+ // Best-effort decode for display only — original separators are unrecoverable.
352
+ if (!name) return null;
353
+ return '/' + name.replace(/^-+/, '').replace(/-/g, '/');
354
+ }
355
+
356
+ function pushTopLevelFiles(sessions, dir, platform, cap, scannedRef) {
357
+ if (!fs.existsSync(dir)) return;
358
+ for (const entry of safeReadDir(dir)) {
359
+ if (scannedRef.value++ > cap) break;
360
+ if (!entry.isFile()) continue;
361
+ const fp = path.join(dir, entry.name);
362
+ const st = safeStat(fp);
363
+ if (!st) continue;
364
+ sessions.push({
365
+ platform,
366
+ session_id: entry.name,
367
+ project: null,
368
+ path: fp,
369
+ size_bytes: st.size,
370
+ modified: st.mtime.toISOString(),
371
+ });
372
+ }
373
+ }
374
+
375
+ function ideSessionsList({
376
+ claudeProjectsDir = defaultClaudeProjectsDir(),
377
+ windsurfDir = defaultWindsurfStateDir(),
378
+ windsurfNextDir = defaultWindsurfNextStateDir(),
379
+ cursorDir = defaultCursorWorkspaceDir(),
380
+ antigravityDir = defaultAntigravityStateDir(),
381
+ extraDirs = extraIdeWatchDirs(),
382
+ cwd = process.cwd(),
383
+ limit = 15,
384
+ } = {}) {
385
+ const sessions = [];
386
+ const scannedRef = { value: 0 };
387
+
388
+ // Claude Code: ~/.claude/projects/<encoded-path>/<uuid>.jsonl
389
+ if (fs.existsSync(claudeProjectsDir)) {
390
+ for (const proj of safeReadDir(claudeProjectsDir)) {
391
+ if (!proj.isDirectory()) continue;
392
+ const projDir = path.join(claudeProjectsDir, proj.name);
393
+ for (const f of safeReadDir(projDir)) {
394
+ if (scannedRef.value++ > IDE_WALK_FILE_CAP) break;
395
+ if (!f.isFile() || !f.name.endsWith('.jsonl')) continue;
396
+ const fp = path.join(projDir, f.name);
397
+ const st = safeStat(fp);
398
+ if (!st) continue;
399
+ sessions.push({
400
+ platform: 'claude-code',
401
+ session_id: f.name.replace(/\.jsonl$/, ''),
402
+ project: decodeClaudeProjectName(proj.name),
403
+ path: fp,
404
+ size_bytes: st.size,
405
+ modified: st.mtime.toISOString(),
406
+ });
407
+ }
408
+ if (scannedRef.value > IDE_WALK_FILE_CAP) break;
409
+ }
410
+ }
411
+
412
+ // Windsurf (legacy) + Windsurf-Next: top-level state files only.
413
+ pushTopLevelFiles(sessions, windsurfDir, 'windsurf', IDE_WALK_FILE_CAP, scannedRef);
414
+ pushTopLevelFiles(sessions, windsurfNextDir, 'windsurf-next', IDE_WALK_FILE_CAP, scannedRef);
415
+
416
+ // Cursor: ~/Library/Application Support/Cursor/User/workspaceStorage/<hash>/
417
+ // Each workspace is a directory containing .vscdb sqlite files; we surface
418
+ // the directory mtime (proxies for last open) without reading SQLite.
419
+ if (fs.existsSync(cursorDir)) {
420
+ for (const ws of safeReadDir(cursorDir)) {
421
+ if (scannedRef.value++ > IDE_WALK_FILE_CAP) break;
422
+ if (!ws.isDirectory()) continue;
423
+ const wsDir = path.join(cursorDir, ws.name);
424
+ const st = safeStat(wsDir);
425
+ if (!st) continue;
426
+ sessions.push({
427
+ platform: 'cursor',
428
+ session_id: ws.name,
429
+ project: null,
430
+ path: wsDir,
431
+ size_bytes: 0,
432
+ modified: st.mtime.toISOString(),
433
+ });
434
+ }
435
+ }
436
+
437
+ // Antigravity (home dir, best-effort) + per-workspace .agent/ in cwd.
438
+ if (fs.existsSync(antigravityDir)) {
439
+ const st = safeStat(antigravityDir);
440
+ if (st) {
441
+ sessions.push({
442
+ platform: 'antigravity',
443
+ session_id: 'home',
444
+ project: null,
445
+ path: antigravityDir,
446
+ size_bytes: 0,
447
+ modified: st.mtime.toISOString(),
448
+ });
449
+ }
450
+ }
451
+ const agentDir = path.join(cwd, '.agent');
452
+ if (fs.existsSync(agentDir)) {
453
+ const st = safeStat(agentDir);
454
+ if (st) {
455
+ sessions.push({
456
+ platform: 'antigravity',
457
+ session_id: path.basename(cwd) || 'workspace',
458
+ project: cwd,
459
+ path: agentDir,
460
+ size_bytes: 0,
461
+ modified: st.mtime.toISOString(),
462
+ });
463
+ }
464
+ }
465
+
466
+ // User-supplied extra paths (KODELYTH_EXTRA_IDE_WATCH).
467
+ // We only record an entry when the path exists; otherwise we silently skip.
468
+ for (const extra of extraDirs) {
469
+ if (!fs.existsSync(extra)) continue;
470
+ const st = safeStat(extra);
471
+ if (!st) continue;
472
+ sessions.push({
473
+ platform: 'custom',
474
+ session_id: path.basename(extra) || 'extra',
475
+ project: extra,
476
+ path: extra,
477
+ size_bytes: st.isFile() ? st.size : 0,
478
+ modified: st.mtime.toISOString(),
479
+ });
480
+ }
481
+
482
+ sessions.sort((a, b) => b.modified.localeCompare(a.modified));
483
+ return sessions.slice(0, Math.max(1, Math.min(100, limit)));
484
+ }
485
+
486
+ // Cheap mtime aggregator used by the SSE file-watcher tick.
487
+ // Returns the largest mtimeMs across IDE session files. 0 if nothing found.
488
+ function getMaxIdeMtime({
489
+ claudeProjectsDir = defaultClaudeProjectsDir(),
490
+ windsurfDir = defaultWindsurfStateDir(),
491
+ windsurfNextDir = defaultWindsurfNextStateDir(),
492
+ cursorDir = defaultCursorWorkspaceDir(),
493
+ antigravityDir = defaultAntigravityStateDir(),
494
+ extraDirs = extraIdeWatchDirs(),
495
+ cwd = process.cwd(),
496
+ } = {}) {
497
+ let maxMs = 0;
498
+ let scanned = 0;
499
+
500
+ // Claude Code
501
+ if (fs.existsSync(claudeProjectsDir)) {
502
+ for (const proj of safeReadDir(claudeProjectsDir)) {
503
+ if (!proj.isDirectory()) continue;
504
+ const projDir = path.join(claudeProjectsDir, proj.name);
505
+ for (const f of safeReadDir(projDir)) {
506
+ if (scanned++ > IDE_WALK_FILE_CAP) break;
507
+ if (!f.isFile() || !f.name.endsWith('.jsonl')) continue;
508
+ const st = safeStat(path.join(projDir, f.name));
509
+ if (st && st.mtimeMs > maxMs) maxMs = st.mtimeMs;
510
+ }
511
+ if (scanned > IDE_WALK_FILE_CAP) break;
512
+ }
513
+ }
514
+
515
+ // Windsurf (legacy + next) — dir mtime + one level of files.
516
+ for (const dir of [windsurfDir, windsurfNextDir]) {
517
+ if (!fs.existsSync(dir)) continue;
518
+ const st = safeStat(dir);
519
+ if (st && st.mtimeMs > maxMs) maxMs = st.mtimeMs;
520
+ for (const entry of safeReadDir(dir)) {
521
+ if (scanned++ > IDE_WALK_FILE_CAP) break;
522
+ if (!entry.isFile()) continue;
523
+ const fst = safeStat(path.join(dir, entry.name));
524
+ if (fst && fst.mtimeMs > maxMs) maxMs = fst.mtimeMs;
525
+ }
526
+ }
527
+
528
+ // Cursor workspace dirs
529
+ if (fs.existsSync(cursorDir)) {
530
+ for (const ws of safeReadDir(cursorDir)) {
531
+ if (scanned++ > IDE_WALK_FILE_CAP) break;
532
+ if (!ws.isDirectory()) continue;
533
+ const st = safeStat(path.join(cursorDir, ws.name));
534
+ if (st && st.mtimeMs > maxMs) maxMs = st.mtimeMs;
535
+ }
536
+ }
537
+
538
+ // Antigravity
539
+ if (fs.existsSync(antigravityDir)) {
540
+ const st = safeStat(antigravityDir);
541
+ if (st && st.mtimeMs > maxMs) maxMs = st.mtimeMs;
542
+ }
543
+ const agentDir = path.join(cwd, '.agent');
544
+ if (fs.existsSync(agentDir)) {
545
+ const st = safeStat(agentDir);
546
+ if (st && st.mtimeMs > maxMs) maxMs = st.mtimeMs;
547
+ }
548
+
549
+ // Extra user-supplied watch paths (KODELYTH_EXTRA_IDE_WATCH).
550
+ for (const extra of extraDirs) {
551
+ if (scanned++ > IDE_WALK_FILE_CAP) break;
552
+ if (!fs.existsSync(extra)) continue;
553
+ const st = safeStat(extra);
554
+ if (st && st.mtimeMs > maxMs) maxMs = st.mtimeMs;
555
+ }
556
+
557
+ return maxMs;
558
+ }
559
+
311
560
  // ── token budget snapshot ────────────────────────────────────────────────────
312
561
 
313
562
  function tokenBudgetSnapshot({ budgetDir = defaultBudgetDir() } = {}) {
@@ -343,11 +592,19 @@ module.exports = {
343
592
  // catalog
344
593
  catalogList,
345
594
  catalogCounts,
346
- // sessions
595
+ // sessions (orchestration)
347
596
  sessionsList,
348
597
  sessionDetail,
598
+ // ide sessions (claude code / windsurf / antigravity)
599
+ ideSessionsList,
349
600
  // token budget
350
601
  tokenBudgetSnapshot,
351
- // exposed for tests
352
- _internals: { defaultMemoryDir, defaultEvolveDir, defaultBudgetDir, defaultCoordRoot, safeReadDir, safeReadJson, safeReadExcerpt },
602
+ // exposed for tests + the file-watcher tick
603
+ _internals: {
604
+ defaultMemoryDir, defaultEvolveDir, defaultBudgetDir, defaultCoordRoot,
605
+ defaultClaudeProjectsDir, defaultWindsurfStateDir, defaultWindsurfNextStateDir,
606
+ defaultCursorWorkspaceDir, defaultAntigravityStateDir, extraIdeWatchDirs,
607
+ safeReadDir, safeReadJson, safeReadExcerpt,
608
+ getMaxIdeMtime,
609
+ },
353
610
  };
@@ -1,6 +1,6 @@
1
1
  // scripts/dashboard/server.js
2
2
  //
3
- // Local observability dashboard server (v1.7.0).
3
+ // Local observability dashboard server (v1.7.4).
4
4
  //
5
5
  // HTTP server using ONLY Node.js built-ins. Serves:
6
6
  // GET / → static index.html
@@ -11,7 +11,9 @@
11
11
  // GET /api/catalog?kind=…&q=… → agents | skills | commands | rules | bundles
12
12
  // GET /api/sessions → orchestration session list
13
13
  // GET /api/sessions/:name → single session detail
14
+ // GET /api/ide-sessions → live Claude Code / Windsurf / Antigravity activity
14
15
  // GET /api/token-budget → per-session token usage
16
+ // GET /api/events → SSE stream of data-changed events
15
17
  // GET /api/health → tiny liveness probe
16
18
  //
17
19
  // Default bind: 127.0.0.1 (localhost ONLY). Refuses to bind 0.0.0.0 unless
@@ -32,13 +34,18 @@ const sseClients = new Set();
32
34
  const MAX_SSE_CLIENTS = 10;
33
35
 
34
36
  // Key files that signal data changes when their mtime moves.
37
+ // IMPORTANT: include both `routing-misses.jsonl` (always present once evolve sees
38
+ // any miss) AND `proposals.jsonl` (created later, after `evolve analyze` runs).
35
39
  const WATCH_PATHS = [
36
- path.join(os.homedir(), '.kodelyth', 'memory', 'memories.jsonl'),
40
+ path.join(os.homedir(), '.kodelyth', 'memory', 'memories.jsonl'),
37
41
  path.join(os.homedir(), '.kodelyth', 'evolve', 'reuse.json'),
42
+ path.join(os.homedir(), '.kodelyth', 'evolve', 'routing-misses.jsonl'),
38
43
  path.join(os.homedir(), '.kodelyth', 'evolve', 'proposals.jsonl'),
44
+ path.join(os.homedir(), '.kodelyth', 'token-budget'), // dir mtime — changes when budget files appear
39
45
  ];
40
46
  const lastMtimes = new Map();
41
- const FILE_CHECK_MS = 10000; // check every 10 s
47
+ let lastIdeMtime = 0; // max mtime across Claude Code / Windsurf / Antigravity sessions
48
+ const FILE_CHECK_MS = 3000; // poll every 3 s — "feels live" without burning CPU
42
49
 
43
50
  function broadcastSSE(obj) {
44
51
  const msg = `data: ${JSON.stringify(obj)}\n\n`;
@@ -55,15 +62,27 @@ function startFileWatcher() {
55
62
  for (const p of WATCH_PATHS) {
56
63
  try { lastMtimes.set(p, fs.statSync(p).mtimeMs); } catch { /* file not yet present */ }
57
64
  }
65
+ try { lastIdeMtime = data._internals.getMaxIdeMtime(); } catch { lastIdeMtime = 0; }
66
+
58
67
  _fileWatchTimer = setInterval(() => {
59
68
  if (sseClients.size === 0) return; // no connected browsers — skip stat I/O
60
69
  let changed = false;
70
+
71
+ // Static watched files (memory, evolve, token-budget).
61
72
  for (const p of WATCH_PATHS) {
62
73
  try {
63
74
  const m = fs.statSync(p).mtimeMs;
64
75
  if (lastMtimes.get(p) !== m) { changed = true; lastMtimes.set(p, m); }
65
76
  } catch { /* file doesn't exist yet — that's fine */ }
66
77
  }
78
+
79
+ // Live IDE activity — Claude Code / Windsurf / Antigravity.
80
+ // Cheap: only stats jsonl files in known dirs, capped at 500 files per tick.
81
+ try {
82
+ const m = data._internals.getMaxIdeMtime();
83
+ if (m > lastIdeMtime) { changed = true; lastIdeMtime = m; }
84
+ } catch { /* swallow — IDE watch must never break the heartbeat */ }
85
+
67
86
  broadcastSSE({ type: changed ? 'data-changed' : 'heartbeat', time: new Date().toISOString() });
68
87
  }, FILE_CHECK_MS);
69
88
  }
@@ -241,6 +260,12 @@ function handleRequest(req, res) {
241
260
  });
242
261
  }
243
262
 
263
+ if (p === '/api/ide-sessions') {
264
+ return jsonResponse(res, 200, {
265
+ sessions: data.ideSessionsList({ limit: Number(q.get('limit')) || 15 }),
266
+ });
267
+ }
268
+
244
269
  const sessMatch = p.match(/^\/api\/sessions\/([A-Za-z0-9._-]+)$/);
245
270
  if (sessMatch) {
246
271
  const sessionName = sessMatch[1];