@ycniuqton/devlens 0.1.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.
Files changed (61) hide show
  1. package/README.md +164 -0
  2. package/bin/devlens.js +2 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +205 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/init.d.ts +3 -0
  7. package/dist/init.js +239 -0
  8. package/dist/init.js.map +1 -0
  9. package/dist/routes/diff.d.ts +1 -0
  10. package/dist/routes/diff.js +39 -0
  11. package/dist/routes/diff.js.map +1 -0
  12. package/dist/routes/integrations.d.ts +1 -0
  13. package/dist/routes/integrations.js +132 -0
  14. package/dist/routes/integrations.js.map +1 -0
  15. package/dist/routes/rules.d.ts +1 -0
  16. package/dist/routes/rules.js +115 -0
  17. package/dist/routes/rules.js.map +1 -0
  18. package/dist/routes/tasks.d.ts +4 -0
  19. package/dist/routes/tasks.js +360 -0
  20. package/dist/routes/tasks.js.map +1 -0
  21. package/dist/server.d.ts +7 -0
  22. package/dist/server.js +112 -0
  23. package/dist/server.js.map +1 -0
  24. package/dist/services/claudeTasks.d.ts +23 -0
  25. package/dist/services/claudeTasks.js +160 -0
  26. package/dist/services/claudeTasks.js.map +1 -0
  27. package/dist/services/config.d.ts +3 -0
  28. package/dist/services/config.js +25 -0
  29. package/dist/services/config.js.map +1 -0
  30. package/dist/services/git.d.ts +8 -0
  31. package/dist/services/git.js +90 -0
  32. package/dist/services/git.js.map +1 -0
  33. package/dist/services/jira.d.ts +11 -0
  34. package/dist/services/jira.js +52 -0
  35. package/dist/services/jira.js.map +1 -0
  36. package/dist/services/linear.d.ts +9 -0
  37. package/dist/services/linear.js +69 -0
  38. package/dist/services/linear.js.map +1 -0
  39. package/dist/services/rules.d.ts +14 -0
  40. package/dist/services/rules.js +133 -0
  41. package/dist/services/rules.js.map +1 -0
  42. package/dist/services/taskStore.d.ts +27 -0
  43. package/dist/services/taskStore.js +261 -0
  44. package/dist/services/taskStore.js.map +1 -0
  45. package/dist/services/tunnel.d.ts +8 -0
  46. package/dist/services/tunnel.js +152 -0
  47. package/dist/services/tunnel.js.map +1 -0
  48. package/dist/services/watcher.d.ts +2 -0
  49. package/dist/services/watcher.js +30 -0
  50. package/dist/services/watcher.js.map +1 -0
  51. package/dist/types/index.d.ts +87 -0
  52. package/dist/types/index.js +3 -0
  53. package/dist/types/index.js.map +1 -0
  54. package/package.json +53 -0
  55. package/public/css/style.css +1613 -0
  56. package/public/index.html +395 -0
  57. package/public/js/app.js +104 -0
  58. package/public/js/diff.js +337 -0
  59. package/public/js/integrations.js +194 -0
  60. package/public/js/rules.js +174 -0
  61. package/public/js/tasks.js +301 -0
package/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # Devlens
2
+
3
+ A real-time developer dashboard for git diff viewing, task management, and Claude Code integration.
4
+
5
+ Devlens gives you a web-based UI to monitor file changes, manage tasks on a kanban board, and sync activity from Claude Code sessions — all in one place.
6
+
7
+ ## Features
8
+
9
+ - **Real-time Git Diffs** — Watch file changes live with syntax-highlighted diffs (unified or side-by-side)
10
+ - **Task Board** — Kanban-style board with Pending, In Progress, and Completed columns
11
+ - **Claude Code Integration** — Auto-sync tasks and todos from Claude Code sessions via hooks
12
+ - **External Integrations** — Connect Jira and Linear to pull issues into your dashboard
13
+ - **Cloudflare Tunnel** — Optional remote access via `cloudflared`
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install -g devlens
19
+ ```
20
+
21
+ Or install locally in your project:
22
+
23
+ ```bash
24
+ npm install --save-dev devlens
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### 1. Start the dashboard
30
+
31
+ ```bash
32
+ devlens start
33
+ ```
34
+
35
+ This opens your browser at `http://localhost:4700` with the dashboard.
36
+
37
+ ### 2. Set up Claude Code hooks (optional)
38
+
39
+ If you use [Claude Code](https://claude.ai/claude-code), run this in your project directory to auto-sync tasks:
40
+
41
+ ```bash
42
+ devlens init
43
+ ```
44
+
45
+ This installs two hooks:
46
+ - **Auto-start** — Launches the dashboard when a Claude Code session begins
47
+ - **Task sync** — Sends task/todo updates to the dashboard in real-time
48
+
49
+ ### 3. Browse the dashboard
50
+
51
+ - **Diffs tab** — See staged, unstaged, and untracked file changes with full diffs
52
+ - **Tasks tab** — Manage tasks on a kanban board; view Claude's current todos and session history
53
+ - **Integrations tab** — Configure Jira or Linear to pull external issues
54
+
55
+ ## CLI Reference
56
+
57
+ ### `devlens start`
58
+
59
+ Start the dashboard server.
60
+
61
+ ```bash
62
+ devlens start [options]
63
+ ```
64
+
65
+ | Option | Default | Description |
66
+ |--------|---------|-------------|
67
+ | `-p, --port <number>` | `4700` | Port to listen on |
68
+ | `--no-open` | — | Don't auto-open browser |
69
+ | `--tunnel` | — | Enable Cloudflare Tunnel for remote access |
70
+ | `-d, --dir <path>` | Current directory | Project directory to analyze |
71
+
72
+ ### `devlens init`
73
+
74
+ Install Claude Code hooks for automatic task sync.
75
+
76
+ ```bash
77
+ devlens init [options]
78
+ ```
79
+
80
+ | Option | Default | Description |
81
+ |--------|---------|-------------|
82
+ | `-p, --port <number>` | `4700` | Devlens port for hooks to target |
83
+ | `-d, --dir <path>` | Current directory | Project directory |
84
+
85
+ What it does:
86
+ 1. Creates `.claude/hooks/devlens-startup.sh` and `.claude/hooks/devlens-sync.sh`
87
+ 2. Updates `.claude/settings.json` with `SessionStart` and `PostToolUse` hooks
88
+ 3. Adds `.claude/hooks/` to `.gitignore`
89
+
90
+ ### `devlens uninstall`
91
+
92
+ Remove Claude Code hooks from the project.
93
+
94
+ ```bash
95
+ devlens uninstall [options]
96
+ ```
97
+
98
+ | Option | Default | Description |
99
+ |--------|---------|-------------|
100
+ | `-d, --dir <path>` | Current directory | Project directory |
101
+
102
+ ## Configuration
103
+
104
+ ### Environment Variables
105
+
106
+ | Variable | Default | Description |
107
+ |----------|---------|-------------|
108
+ | `DEVLENS_PORT` | `4700` | Port used by hook scripts |
109
+
110
+ ### Jira Integration
111
+
112
+ In the **Integrations** tab, configure:
113
+ - **Host** — Your Jira instance URL (e.g., `https://yourteam.atlassian.net`)
114
+ - **Email** — Your Jira account email
115
+ - **API Token** — [Generate one here](https://id.atlassian.com/manage-profile/security/api-tokens)
116
+ - **Project Key** — The Jira project key (e.g., `PROJ`)
117
+
118
+ ### Linear Integration
119
+
120
+ In the **Integrations** tab, configure:
121
+ - **API Key** — [Generate one here](https://linear.app/settings/api)
122
+ - **Team Key** (optional) — Filter issues by team
123
+
124
+ ## API
125
+
126
+ Devlens exposes a REST API at `http://localhost:4700/api`:
127
+
128
+ | Endpoint | Method | Description |
129
+ |----------|--------|-------------|
130
+ | `/api/diff` | GET | Git diff with file list (`?filter=all\|staged\|unstaged`) |
131
+ | `/api/status` | GET | File change status |
132
+ | `/api/log` | GET | Commit history (`?limit=20`) |
133
+ | `/api/tasks` | GET | List tasks (`?status=pending\|in-progress\|completed`) |
134
+ | `/api/tasks` | POST | Create a task (`{ title, status?, priority? }`) |
135
+ | `/api/tasks/:id` | PUT | Update a task |
136
+ | `/api/tasks/:id` | DELETE | Delete a task |
137
+ | `/api/tasks/sync` | POST | Webhook for Claude Code hook payloads |
138
+ | `/api/tasks/claude-todos` | GET | Current session todos |
139
+ | `/api/tasks/claude-sessions` | GET | Cross-session task data |
140
+ | `/api/integrations/status` | GET | Integration config status |
141
+ | `/api/integrations/all` | GET | Fetch all external issues |
142
+
143
+ A WebSocket is available at `ws://localhost:4700/ws` for real-time updates.
144
+
145
+ ## Development
146
+
147
+ ```bash
148
+ git clone https://github.com/ycniuqton/Devlens.git
149
+ cd Devlens
150
+ npm install
151
+ npm run build
152
+ npm start
153
+ ```
154
+
155
+ For development with auto-recompilation:
156
+
157
+ ```bash
158
+ npm run dev # Watch mode — recompiles on changes
159
+ node bin/devlens.js start --no-open # Run from source
160
+ ```
161
+
162
+ ## License
163
+
164
+ MIT
package/bin/devlens.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('../dist/index.js');
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const commander_1 = require("commander");
40
+ const path_1 = __importDefault(require("path"));
41
+ const fs_1 = __importDefault(require("fs"));
42
+ const os_1 = __importDefault(require("os"));
43
+ const server_1 = require("./server");
44
+ const init_1 = require("./init");
45
+ const pkg = require('../package.json');
46
+ const program = new commander_1.Command();
47
+ program
48
+ .name('devlens')
49
+ .description('Developer dashboard with real-time git diffs and task board')
50
+ .version(pkg.version);
51
+ // devlens start (default command)
52
+ program
53
+ .command('start', { isDefault: true })
54
+ .description('Start the Devlens dashboard')
55
+ .option('-p, --port <number>', 'Port to listen on (default: derived from project path)')
56
+ .option('--no-open', 'Do not open browser automatically')
57
+ .option('--tunnel', 'Enable Cloudflare Tunnel for remote access')
58
+ .option('-d, --dir <path>', 'Project directory to analyze', process.cwd())
59
+ .action(async (opts) => {
60
+ const projectDir = path_1.default.resolve(opts.dir);
61
+ const port = opts.port ? parseInt(opts.port, 10) : (0, init_1.portFromDir)(projectDir);
62
+ const options = {
63
+ port,
64
+ projectDir,
65
+ openBrowser: opts.open !== false,
66
+ tunnel: opts.tunnel || false,
67
+ };
68
+ const { httpServer } = (0, server_1.createServer)(options);
69
+ httpServer.listen(options.port, '0.0.0.0', () => {
70
+ const url = `http://localhost:${options.port}`;
71
+ console.log(`\n Devlens running at ${url}`);
72
+ console.log(` Watching: ${options.projectDir}\n`);
73
+ // Write runtime info so `devlens status` and hooks can find it
74
+ writeRuntimeInfo(options.projectDir, options.port);
75
+ if (options.openBrowser) {
76
+ Promise.resolve().then(() => __importStar(require('open'))).then((mod) => mod.default(url)).catch(() => { });
77
+ }
78
+ if (options.tunnel) {
79
+ Promise.resolve().then(() => __importStar(require('./services/tunnel'))).then((mod) => {
80
+ mod.startTunnel(options.port).then((tunnelUrl) => {
81
+ console.log(` Tunnel: ${tunnelUrl}\n`);
82
+ });
83
+ }).catch((err) => {
84
+ console.log(` Tunnel failed: ${err.message}\n`);
85
+ });
86
+ }
87
+ });
88
+ });
89
+ // devlens init — install Claude Code hooks
90
+ program
91
+ .command('init')
92
+ .description('Install Claude Code hooks for task sync in this project')
93
+ .option('-p, --port <number>', 'Override port (default: derived from project path)')
94
+ .option('-d, --dir <path>', 'Project directory', process.cwd())
95
+ .action((opts) => {
96
+ const projectDir = path_1.default.resolve(opts.dir);
97
+ const port = opts.port ? parseInt(opts.port, 10) : undefined;
98
+ console.log(`\n Installing Devlens hooks in ${projectDir}...`);
99
+ (0, init_1.initDevlens)(projectDir, port);
100
+ });
101
+ // devlens uninstall — remove Claude Code hooks
102
+ program
103
+ .command('uninstall')
104
+ .description('Remove Claude Code hooks from this project')
105
+ .option('-d, --dir <path>', 'Project directory', process.cwd())
106
+ .action((opts) => {
107
+ const projectDir = path_1.default.resolve(opts.dir);
108
+ console.log(`\n Removing Devlens hooks from ${projectDir}...`);
109
+ (0, init_1.uninstallDevlens)(projectDir);
110
+ });
111
+ // devlens status — show dashboard URL if running
112
+ program
113
+ .command('status')
114
+ .description('Show Devlens dashboard URL if running')
115
+ .option('-d, --dir <path>', 'Project directory', process.cwd())
116
+ .action((opts) => {
117
+ const projectDir = path_1.default.resolve(opts.dir);
118
+ const info = readRuntimeInfo(projectDir);
119
+ if (!info) {
120
+ console.log(`\n Devlens is not running for ${projectDir}\n`);
121
+ process.exit(1);
122
+ return;
123
+ }
124
+ // Verify it's actually up
125
+ const http = require('http');
126
+ const req = http.get(`http://localhost:${info.port}`, (res) => {
127
+ if (res.statusCode === 200) {
128
+ console.log(`\n \x1b[32m\x1b[1mDevlens is running\x1b[0m\n`);
129
+ console.log(` \x1b[2mLocal:\x1b[0m \x1b[1m\x1b[36mhttp://localhost:${info.port}\x1b[0m`);
130
+ for (const ip of info.ips || []) {
131
+ console.log(` \x1b[2mNetwork:\x1b[0m \x1b[1m\x1b[36mhttp://${ip}:${info.port}\x1b[0m`);
132
+ }
133
+ console.log(` \x1b[2mPID:\x1b[0m ${info.pid}`);
134
+ console.log(` \x1b[2mStarted:\x1b[0m ${info.startedAt}\n`);
135
+ }
136
+ else {
137
+ cleanRuntimeInfo(projectDir);
138
+ console.log(`\n Devlens is not running (stale info cleaned)\n`);
139
+ process.exit(1);
140
+ }
141
+ });
142
+ req.on('error', () => {
143
+ cleanRuntimeInfo(projectDir);
144
+ console.log(`\n Devlens is not running (stale info cleaned)\n`);
145
+ process.exit(1);
146
+ });
147
+ req.end();
148
+ });
149
+ // Runtime info helpers
150
+ function getRuntimeInfoPath(projectDir) {
151
+ const devlensDir = path_1.default.join(projectDir, '.devlens');
152
+ return path_1.default.join(devlensDir, 'runtime.json');
153
+ }
154
+ function writeRuntimeInfo(projectDir, port) {
155
+ const devlensDir = path_1.default.join(projectDir, '.devlens');
156
+ if (!fs_1.default.existsSync(devlensDir)) {
157
+ fs_1.default.mkdirSync(devlensDir, { recursive: true });
158
+ }
159
+ const interfaces = os_1.default.networkInterfaces();
160
+ const ips = [];
161
+ for (const name of Object.keys(interfaces)) {
162
+ for (const iface of interfaces[name] || []) {
163
+ if (iface.family === 'IPv4' && !iface.internal) {
164
+ ips.push(iface.address);
165
+ }
166
+ }
167
+ }
168
+ const info = {
169
+ port,
170
+ pid: process.pid,
171
+ ips,
172
+ startedAt: new Date().toISOString(),
173
+ projectDir,
174
+ };
175
+ fs_1.default.writeFileSync(getRuntimeInfoPath(projectDir), JSON.stringify(info, null, 2));
176
+ // Clean up on exit
177
+ const cleanup = () => {
178
+ try {
179
+ fs_1.default.unlinkSync(getRuntimeInfoPath(projectDir));
180
+ }
181
+ catch { }
182
+ };
183
+ process.on('SIGINT', () => { cleanup(); process.exit(0); });
184
+ process.on('SIGTERM', () => { cleanup(); process.exit(0); });
185
+ process.on('exit', cleanup);
186
+ }
187
+ function readRuntimeInfo(projectDir) {
188
+ const infoPath = getRuntimeInfoPath(projectDir);
189
+ if (!fs_1.default.existsSync(infoPath))
190
+ return null;
191
+ try {
192
+ return JSON.parse(fs_1.default.readFileSync(infoPath, 'utf-8'));
193
+ }
194
+ catch {
195
+ return null;
196
+ }
197
+ }
198
+ function cleanRuntimeInfo(projectDir) {
199
+ try {
200
+ fs_1.default.unlinkSync(getRuntimeInfoPath(projectDir));
201
+ }
202
+ catch { }
203
+ }
204
+ program.parse();
205
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/init.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function portFromDir(dir: string): number;
2
+ export declare function initDevlens(projectDir: string, port?: number): void;
3
+ export declare function uninstallDevlens(projectDir: string): void;
package/dist/init.js ADDED
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.portFromDir = portFromDir;
7
+ exports.initDevlens = initDevlens;
8
+ exports.uninstallDevlens = uninstallDevlens;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const crypto_1 = __importDefault(require("crypto"));
12
+ // Derive a stable port from the project directory path (range 4700-5700)
13
+ function portFromDir(dir) {
14
+ const hash = crypto_1.default.createHash('md5').update(path_1.default.resolve(dir)).digest();
15
+ return 4700 + (hash.readUInt16BE(0) % 1000);
16
+ }
17
+ const SYNC_HOOK_SCRIPT = `#!/bin/bash
18
+ # Devlens Claude Code hook — syncs task changes to the Devlens dashboard
19
+ # Installed by: devlens init
20
+
21
+ DEVLENS_PORT=__PORT__
22
+ DEVLENS_URL="http://localhost:\$DEVLENS_PORT/api/tasks/sync"
23
+
24
+ INPUT=$(cat)
25
+
26
+ # POST the hook payload to Devlens sync endpoint
27
+ curl -s -X POST "\$DEVLENS_URL" \\
28
+ -H "Content-Type: application/json" \\
29
+ -d "\$INPUT" > /dev/null 2>&1 &
30
+
31
+ exit 0
32
+ `;
33
+ const STARTUP_HOOK_SCRIPT = `#!/bin/bash
34
+ # Devlens — auto-start dashboard on Claude Code session start
35
+ DEVLENS_PORT=__PORT__
36
+ PROJECT_DIR="\${CLAUDE_PROJECT_DIR:-.}"
37
+ RUNTIME_FILE="\$PROJECT_DIR/.devlens/runtime.json"
38
+
39
+ # Already running? Check runtime file + verify process alive
40
+ if [ -f "\$RUNTIME_FILE" ]; then
41
+ PORT=\$(grep -o '"port":[0-9]*' "\$RUNTIME_FILE" | cut -d: -f2)
42
+ if curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$PORT" 2>/dev/null | grep -q "200"; then
43
+ URLS=\$(grep -o '"ips":\\[[^]]*\\]' "\$RUNTIME_FILE" | sed 's/"ips":\\[//;s/\\]//;s/"//g')
44
+ echo "{\\"additionalContext\\":\\"Devlens dashboard is running at http://localhost:\$PORT . Network IPs: \$URLS (port \$PORT). Use devlens status for details.\\"}"
45
+ exit 0
46
+ fi
47
+ fi
48
+
49
+ # Find binary
50
+ BIN=""
51
+ command -v devlens &>/dev/null && BIN="devlens"
52
+ [ -z "\$BIN" ] && [ -f "\$PROJECT_DIR/dist/index.js" ] && BIN="node \$PROJECT_DIR/dist/index.js"
53
+ [ -z "\$BIN" ] && [ -f "\$PROJECT_DIR/node_modules/.bin/devlens" ] && BIN="\$PROJECT_DIR/node_modules/.bin/devlens"
54
+ [ -z "\$BIN" ] && exit 0
55
+
56
+ # Start in background
57
+ nohup \$BIN start --dir "\$PROJECT_DIR" --port \$DEVLENS_PORT --no-open > /tmp/devlens-\$DEVLENS_PORT.log 2>&1 &
58
+
59
+ # Wait up to 5s for runtime.json to appear
60
+ for i in 1 2 3 4 5; do
61
+ sleep 1
62
+ if [ -f "\$RUNTIME_FILE" ]; then
63
+ echo "{\\"additionalContext\\":\\"Devlens dashboard started at http://localhost:\$DEVLENS_PORT . Use devlens status for all URLs.\\"}"
64
+ exit 0
65
+ fi
66
+ done
67
+ exit 0
68
+ `;
69
+ function initDevlens(projectDir, port) {
70
+ const resolvedDir = path_1.default.resolve(projectDir);
71
+ const derivedPort = port || portFromDir(resolvedDir);
72
+ const claudeDir = path_1.default.join(resolvedDir, '.claude');
73
+ const hooksDir = path_1.default.join(claudeDir, 'hooks');
74
+ const settingsFile = path_1.default.join(claudeDir, 'settings.json');
75
+ const syncScriptPath = path_1.default.join(hooksDir, 'devlens-sync.sh');
76
+ const startupScriptPath = path_1.default.join(hooksDir, 'devlens-startup.sh');
77
+ // 1. Create .claude/hooks/ directory
78
+ if (!fs_1.default.existsSync(hooksDir)) {
79
+ fs_1.default.mkdirSync(hooksDir, { recursive: true });
80
+ }
81
+ // 2. Write hook scripts with the project-specific port
82
+ const portStr = String(derivedPort);
83
+ fs_1.default.writeFileSync(syncScriptPath, SYNC_HOOK_SCRIPT.replace('__PORT__', portStr), { mode: 0o755 });
84
+ console.log(` Created hook: .claude/hooks/devlens-sync.sh (task sync)`);
85
+ fs_1.default.writeFileSync(startupScriptPath, STARTUP_HOOK_SCRIPT.replace(/__PORT__/g, portStr), { mode: 0o755 });
86
+ console.log(` Created hook: .claude/hooks/devlens-startup.sh (auto-start)`);
87
+ // 3. Update .claude/settings.json
88
+ let settings = {};
89
+ if (fs_1.default.existsSync(settingsFile)) {
90
+ settings = JSON.parse(fs_1.default.readFileSync(settingsFile, 'utf-8'));
91
+ }
92
+ if (!settings.hooks) {
93
+ settings.hooks = {};
94
+ }
95
+ // --- SessionStart hook ---
96
+ if (!settings.hooks.SessionStart) {
97
+ settings.hooks.SessionStart = [];
98
+ }
99
+ settings.hooks.SessionStart = settings.hooks.SessionStart.filter((h) => !h.command?.includes('devlens-startup') && !h.hooks?.some((hk) => hk.command.includes('devlens-startup')));
100
+ settings.hooks.SessionStart.push({
101
+ matcher: '',
102
+ hooks: [
103
+ {
104
+ type: 'command',
105
+ command: `"$CLAUDE_PROJECT_DIR"/.claude/hooks/devlens-startup.sh`,
106
+ },
107
+ ],
108
+ });
109
+ // --- PostToolUse hook ---
110
+ if (!settings.hooks.PostToolUse) {
111
+ settings.hooks.PostToolUse = [];
112
+ }
113
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter((h) => !h.hooks?.some((hk) => hk.command.includes('devlens-sync')));
114
+ settings.hooks.PostToolUse.push({
115
+ matcher: 'TaskCreate|TaskUpdate',
116
+ hooks: [
117
+ {
118
+ type: 'command',
119
+ command: `"$CLAUDE_PROJECT_DIR"/.claude/hooks/devlens-sync.sh`,
120
+ },
121
+ ],
122
+ });
123
+ fs_1.default.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
124
+ console.log(` Updated hooks config: .claude/settings.json`);
125
+ // 4. Add .claude/hooks/ to .gitignore if not already there
126
+ const gitignorePath = path_1.default.join(resolvedDir, '.gitignore');
127
+ if (fs_1.default.existsSync(gitignorePath)) {
128
+ const gitignore = fs_1.default.readFileSync(gitignorePath, 'utf-8');
129
+ if (!gitignore.includes('.claude/hooks/')) {
130
+ fs_1.default.appendFileSync(gitignorePath, '\n.claude/hooks/\n');
131
+ console.log(` Updated .gitignore`);
132
+ }
133
+ }
134
+ // 5. Create /devlens slash command skill
135
+ const skillDir = path_1.default.join(claudeDir, 'skills', 'devlens');
136
+ if (!fs_1.default.existsSync(skillDir)) {
137
+ fs_1.default.mkdirSync(skillDir, { recursive: true });
138
+ }
139
+ const skillContent = `---
140
+ name: devlens
141
+ description: Show the Devlens dashboard URL and status
142
+ ---
143
+
144
+ Show the Devlens dashboard status by reading the runtime file:
145
+
146
+ \`\`\`!
147
+ cat "$CLAUDE_PROJECT_DIR/.devlens/runtime.json" 2>/dev/null || echo "NOT_RUNNING"
148
+ \`\`\`
149
+
150
+ If the output is NOT_RUNNING, tell the user Devlens is not running and suggest they restart their Claude Code session.
151
+
152
+ Otherwise, parse the JSON and display a clean summary:
153
+ - Local URL: http://localhost:{port}
154
+ - Network URLs: http://{each ip}:{port}
155
+ - PID and start time
156
+ `;
157
+ fs_1.default.writeFileSync(path_1.default.join(skillDir, 'SKILL.md'), skillContent);
158
+ console.log(` Created skill: .claude/skills/devlens (use /devlens in Claude)`);
159
+ // 6. Append the Devlens block to CLAUDE.md (idempotent)
160
+ const claudeMdPath = path_1.default.join(resolvedDir, 'CLAUDE.md');
161
+ const devlensBlock = '## Devlens\nRead and follow all rules in `.devlens/rules.md` before every action.\n';
162
+ let existing = '';
163
+ if (fs_1.default.existsSync(claudeMdPath)) {
164
+ existing = fs_1.default.readFileSync(claudeMdPath, 'utf-8');
165
+ }
166
+ if (!existing.includes('## Devlens')) {
167
+ const sep = existing && !existing.endsWith('\n') ? '\n\n' : (existing ? '\n' : '');
168
+ fs_1.default.writeFileSync(claudeMdPath, existing + sep + devlensBlock);
169
+ console.log(` Updated CLAUDE.md with Devlens rules reference`);
170
+ }
171
+ // 7. Create .devlens/rules.md with default rule
172
+ const devlensProjectDir = path_1.default.join(resolvedDir, '.devlens');
173
+ if (!fs_1.default.existsSync(devlensProjectDir)) {
174
+ fs_1.default.mkdirSync(devlensProjectDir, { recursive: true });
175
+ }
176
+ const rulesPath = path_1.default.join(devlensProjectDir, 'rules.md');
177
+ if (!fs_1.default.existsSync(rulesPath)) {
178
+ const defaultRules = `# Devlens Rules
179
+ - Do not run git commit or git push under any circumstances. Only proceed after receiving an explicit user instruction, and clearly indicate before performing the commit.
180
+ `;
181
+ fs_1.default.writeFileSync(rulesPath, defaultRules);
182
+ console.log(` Created .devlens/rules.md with default rules`);
183
+ }
184
+ // Get network IPs for display
185
+ const os = require('os');
186
+ const interfaces = os.networkInterfaces();
187
+ const ips = [];
188
+ for (const name of Object.keys(interfaces)) {
189
+ for (const iface of interfaces[name] || []) {
190
+ if (iface.family === 'IPv4' && !iface.internal) {
191
+ ips.push(iface.address);
192
+ }
193
+ }
194
+ }
195
+ console.log(`\n \x1b[32m\x1b[1mDevlens installed!\x1b[0m\n`);
196
+ console.log(` \x1b[2mLocal:\x1b[0m \x1b[1m\x1b[36mhttp://localhost:${derivedPort}\x1b[0m`);
197
+ for (const ip of ips) {
198
+ console.log(` \x1b[2mNetwork:\x1b[0m \x1b[1m\x1b[36mhttp://${ip}:${derivedPort}\x1b[0m`);
199
+ }
200
+ console.log(`\n Dashboard auto-starts when Claude Code opens a session.`);
201
+ console.log(` Tasks and todos sync automatically via hooks.\n`);
202
+ }
203
+ function uninstallDevlens(projectDir) {
204
+ const claudeDir = path_1.default.join(projectDir, '.claude');
205
+ const hooksDir = path_1.default.join(claudeDir, 'hooks');
206
+ const settingsFile = path_1.default.join(claudeDir, 'settings.json');
207
+ // Remove skill
208
+ const skillDir = path_1.default.join(claudeDir, 'skills', 'devlens');
209
+ if (fs_1.default.existsSync(skillDir)) {
210
+ fs_1.default.rmSync(skillDir, { recursive: true });
211
+ console.log(` Removed: .claude/skills/devlens`);
212
+ }
213
+ for (const script of ['devlens-sync.sh', 'devlens-startup.sh']) {
214
+ const scriptPath = path_1.default.join(hooksDir, script);
215
+ if (fs_1.default.existsSync(scriptPath)) {
216
+ fs_1.default.unlinkSync(scriptPath);
217
+ console.log(` Removed: .claude/hooks/${script}`);
218
+ }
219
+ }
220
+ if (fs_1.default.existsSync(settingsFile)) {
221
+ const settings = JSON.parse(fs_1.default.readFileSync(settingsFile, 'utf-8'));
222
+ if (settings.hooks?.SessionStart) {
223
+ settings.hooks.SessionStart = settings.hooks.SessionStart.filter((h) => !h.command?.includes('devlens-startup') && !h.hooks?.some((hk) => hk.command.includes('devlens-startup')));
224
+ if (settings.hooks.SessionStart.length === 0)
225
+ delete settings.hooks.SessionStart;
226
+ }
227
+ if (settings.hooks?.PostToolUse) {
228
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter((h) => !h.hooks?.some((hk) => hk.command.includes('devlens-sync')));
229
+ if (settings.hooks.PostToolUse.length === 0)
230
+ delete settings.hooks.PostToolUse;
231
+ }
232
+ if (settings.hooks && Object.keys(settings.hooks).length === 0)
233
+ delete settings.hooks;
234
+ fs_1.default.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
235
+ console.log(` Cleaned hooks from: .claude/settings.json`);
236
+ }
237
+ console.log(`\n Devlens hooks uninstalled.\n`);
238
+ }
239
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare const diffRouter: import("express-serve-static-core").Router;