@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.
- package/README.md +164 -0
- package/bin/devlens.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +205 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +3 -0
- package/dist/init.js +239 -0
- package/dist/init.js.map +1 -0
- package/dist/routes/diff.d.ts +1 -0
- package/dist/routes/diff.js +39 -0
- package/dist/routes/diff.js.map +1 -0
- package/dist/routes/integrations.d.ts +1 -0
- package/dist/routes/integrations.js +132 -0
- package/dist/routes/integrations.js.map +1 -0
- package/dist/routes/rules.d.ts +1 -0
- package/dist/routes/rules.js +115 -0
- package/dist/routes/rules.js.map +1 -0
- package/dist/routes/tasks.d.ts +4 -0
- package/dist/routes/tasks.js +360 -0
- package/dist/routes/tasks.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +112 -0
- package/dist/server.js.map +1 -0
- package/dist/services/claudeTasks.d.ts +23 -0
- package/dist/services/claudeTasks.js +160 -0
- package/dist/services/claudeTasks.js.map +1 -0
- package/dist/services/config.d.ts +3 -0
- package/dist/services/config.js +25 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/git.d.ts +8 -0
- package/dist/services/git.js +90 -0
- package/dist/services/git.js.map +1 -0
- package/dist/services/jira.d.ts +11 -0
- package/dist/services/jira.js +52 -0
- package/dist/services/jira.js.map +1 -0
- package/dist/services/linear.d.ts +9 -0
- package/dist/services/linear.js +69 -0
- package/dist/services/linear.js.map +1 -0
- package/dist/services/rules.d.ts +14 -0
- package/dist/services/rules.js +133 -0
- package/dist/services/rules.js.map +1 -0
- package/dist/services/taskStore.d.ts +27 -0
- package/dist/services/taskStore.js +261 -0
- package/dist/services/taskStore.js.map +1 -0
- package/dist/services/tunnel.d.ts +8 -0
- package/dist/services/tunnel.js +152 -0
- package/dist/services/tunnel.js.map +1 -0
- package/dist/services/watcher.d.ts +2 -0
- package/dist/services/watcher.js +30 -0
- package/dist/services/watcher.js.map +1 -0
- package/dist/types/index.d.ts +87 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +53 -0
- package/public/css/style.css +1613 -0
- package/public/index.html +395 -0
- package/public/js/app.js +104 -0
- package/public/js/diff.js +337 -0
- package/public/js/integrations.js +194 -0
- package/public/js/rules.js +174 -0
- 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
package/dist/index.d.ts
ADDED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAAoC;AACpC,gDAAwB;AACxB,4CAAoB;AACpB,4CAAoB;AACpB,qCAAwC;AAExC,iCAAoE;AAEpE,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEvC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,6DAA6D,CAAC;KAC1E,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,kCAAkC;AAClC,OAAO;KACJ,OAAO,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;KACrC,WAAW,CAAC,6BAA6B,CAAC;KAC1C,MAAM,CAAC,qBAAqB,EAAE,wDAAwD,CAAC;KACvF,MAAM,CAAC,WAAW,EAAE,mCAAmC,CAAC;KACxD,MAAM,CAAC,UAAU,EAAE,4CAA4C,CAAC;KAChE,MAAM,CAAC,kBAAkB,EAAE,8BAA8B,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;KACzE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,UAAU,GAAG,cAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAA,kBAAW,EAAC,UAAU,CAAC,CAAC;IAE3E,MAAM,OAAO,GAAkB;QAC7B,IAAI;QACJ,UAAU;QACV,WAAW,EAAE,IAAI,CAAC,IAAI,KAAK,KAAK;QAChC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;KAC7B,CAAC;IAEF,MAAM,EAAE,UAAU,EAAE,GAAG,IAAA,qBAAY,EAAC,OAAO,CAAC,CAAC;IAE7C,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE;QAC9C,MAAM,GAAG,GAAG,oBAAoB,OAAO,CAAC,IAAI,EAAE,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,eAAe,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;QAEnD,+DAA+D;QAC/D,gBAAgB,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAEnD,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACxB,kDAAO,MAAM,IAAE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACjE,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,kDAAO,mBAAmB,IAAE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;gBACvC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;oBAC/C,OAAO,CAAC,GAAG,CAAC,aAAa,SAAS,IAAI,CAAC,CAAC;gBAC1C,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACf,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;YACnD,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,2CAA2C;AAC3C,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,yDAAyD,CAAC;KACtE,MAAM,CAAC,qBAAqB,EAAE,oDAAoD,CAAC;KACnF,MAAM,CAAC,kBAAkB,EAAE,mBAAmB,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;KAC9D,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;IACf,MAAM,UAAU,GAAG,cAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,mCAAmC,UAAU,KAAK,CAAC,CAAC;IAChE,IAAA,kBAAW,EAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC,CAAC,CAAC;AAEL,+CAA+C;AAC/C,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,4CAA4C,CAAC;KACzD,MAAM,CAAC,kBAAkB,EAAE,mBAAmB,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;KAC9D,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;IACf,MAAM,UAAU,GAAG,cAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,mCAAmC,UAAU,KAAK,CAAC,CAAC;IAChE,IAAA,uBAAgB,EAAC,UAAU,CAAC,CAAC;AAC/B,CAAC,CAAC,CAAC;AAEL,iDAAiD;AACjD,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,uCAAuC,CAAC;KACpD,MAAM,CAAC,kBAAkB,EAAE,mBAAmB,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;KAC9D,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;IACf,MAAM,UAAU,GAAG,cAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAEzC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,kCAAkC,UAAU,IAAI,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChB,OAAO;IACT,CAAC;IAED,0BAA0B;IAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,GAAQ,EAAE,EAAE;QACjE,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;YAC9D,OAAO,CAAC,GAAG,CAAC,4DAA4D,IAAI,CAAC,IAAI,SAAS,CAAC,CAAC;YAC5F,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC;gBAChC,OAAO,CAAC,GAAG,CAAC,kDAAkD,EAAE,IAAI,IAAI,CAAC,IAAI,SAAS,CAAC,CAAC;YAC1F,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YACpD,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACN,gBAAgB,CAAC,UAAU,CAAC,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACnB,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,EAAE,CAAC;AACZ,CAAC,CAAC,CAAC;AAEL,uBAAuB;AACvB,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACrD,OAAO,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,gBAAgB,CAAC,UAAkB,EAAE,IAAY;IACxD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,YAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,UAAU,GAAG,YAAE,CAAC,iBAAiB,EAAE,CAAC;IAC1C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3C,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBAC/C,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG;QACX,IAAI;QACJ,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG;QACH,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,UAAU;KACX,CAAC;IAEF,YAAE,CAAC,aAAa,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAEhF,mBAAmB;IACnB,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,CAAC;YAAC,YAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACjE,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,eAAe,CAAC,UAAkB;IACzC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAChD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,UAAkB;IAC1C,IAAI,CAAC;QAAC,YAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AACjE,CAAC;AAED,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
package/dist/init.d.ts
ADDED
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
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":";;;;;AAKA,kCAGC;AAuED,kCAyJC;AAED,4CA4CC;AAtRD,4CAAoB;AACpB,gDAAwB;AACxB,oDAA4B;AAE5B,yEAAyE;AACzE,SAAgB,WAAW,CAAC,GAAW;IACrC,MAAM,IAAI,GAAG,gBAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,cAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACzE,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;CAexB,CAAC;AAEF,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmC3B,CAAC;AAiBF,SAAgB,WAAW,CAAC,UAAkB,EAAE,IAAa;IAC3D,MAAM,WAAW,GAAG,cAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,IAAI,WAAW,CAAC,WAAW,CAAC,CAAC;IAErD,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAC3D,MAAM,cAAc,GAAG,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;IAC9D,MAAM,iBAAiB,GAAG,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;IAEpE,qCAAqC;IACrC,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,YAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,uDAAuD;IACvD,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAEpC,YAAE,CAAC,aAAa,CAAC,cAAc,EAAE,gBAAgB,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjG,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;IAEzE,YAAE,CAAC,aAAa,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxG,OAAO,CAAC,GAAG,CAAC,+DAA+D,CAAC,CAAC;IAE7E,kCAAkC;IAClC,IAAI,QAAQ,GAAiB,EAAE,CAAC;IAChC,IAAI,YAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACpB,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;IACtB,CAAC;IAED,4BAA4B;IAC5B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QACjC,QAAQ,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC;IACnC,CAAC;IACD,QAAQ,CAAC,KAAK,CAAC,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAC9D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CACjH,CAAC;IACF,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC;QAC/B,OAAO,EAAE,EAAE;QACX,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,wDAAwD;aAClE;SACF;KACF,CAAC,CAAC;IAEH,2BAA2B;IAC3B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAChC,QAAQ,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC;IAClC,CAAC;IACD,QAAQ,CAAC,KAAK,CAAC,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAC5D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CACnE,CAAC;IACF,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC;QAC9B,OAAO,EAAE,uBAAuB;QAChC,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,qDAAqD;aAC/D;SACF;KACF,CAAC,CAAC;IAEH,YAAE,CAAC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;IAE7D,2DAA2D;IAC3D,MAAM,aAAa,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAC3D,IAAI,YAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,YAAE,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAC1D,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC1C,YAAE,CAAC,cAAc,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC3D,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,YAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;CAiBtB,CAAC;IACA,YAAE,CAAC,aAAa,CAAC,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;IAEhF,wDAAwD;IACxD,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACzD,MAAM,YAAY,GAAG,qFAAqF,CAAC;IAC3G,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,IAAI,YAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,QAAQ,GAAG,YAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACnF,YAAE,CAAC,aAAa,CAAC,YAAY,EAAE,QAAQ,GAAG,GAAG,GAAG,YAAY,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAClE,CAAC;IAED,gDAAgD;IAChD,MAAM,iBAAiB,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAC7D,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACtC,YAAE,CAAC,SAAS,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;IAC3D,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,MAAM,YAAY,GAAG;;CAExB,CAAC;QACE,YAAE,CAAC,aAAa,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;IAChE,CAAC;IAED,8BAA8B;IAC9B,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,UAAU,GAAG,EAAE,CAAC,iBAAiB,EAAE,CAAC;IAC1C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3C,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBAC/C,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,4DAA4D,WAAW,SAAS,CAAC,CAAC;IAC9F,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,kDAAkD,EAAE,IAAI,WAAW,SAAS,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;IAC3E,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;AACnE,CAAC;AAED,SAAgB,gBAAgB,CAAC,UAAkB;IACjD,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAE3D,eAAe;IACf,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC3D,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,YAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,EAAE,CAAC;QAC/D,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/C,IAAI,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,4BAA4B,MAAM,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,IAAI,YAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAiB,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;QAElF,IAAI,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC;YACjC,QAAQ,CAAC,KAAK,CAAC,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAC9D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CACjH,CAAC;YACF,IAAI,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC;QACnF,CAAC;QAED,IAAI,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;YAChC,QAAQ,CAAC,KAAK,CAAC,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAC5D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CACnE,CAAC;YACF,IAAI,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC;QACjF,CAAC;QAED,IAAI,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,QAAQ,CAAC,KAAK,CAAC;QAEtF,YAAE,CAAC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const diffRouter: import("express-serve-static-core").Router;
|