declare-cc 0.5.2 → 0.5.3
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/bin/install.js +15 -6
- package/commands/declare/dashboard.md +26 -37
- package/dist/declare-tools.cjs +1 -1
- package/hooks/declare-server.js +116 -0
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -1567,6 +1567,9 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1567
1567
|
const activityCommand = isGlobal
|
|
1568
1568
|
? buildHookCommand(targetDir, 'declare-activity.js')
|
|
1569
1569
|
: 'node ' + dirName + '/hooks/declare-activity.js';
|
|
1570
|
+
const serverCommand = isGlobal
|
|
1571
|
+
? buildHookCommand(targetDir, 'declare-server.js')
|
|
1572
|
+
: 'node ' + dirName + '/hooks/declare-server.js';
|
|
1570
1573
|
|
|
1571
1574
|
// Enable experimental agents for Gemini CLI (required for custom sub-agents)
|
|
1572
1575
|
if (isGemini) {
|
|
@@ -1594,16 +1597,22 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1594
1597
|
|
|
1595
1598
|
if (!hasGsdUpdateHook) {
|
|
1596
1599
|
settings.hooks.SessionStart.push({
|
|
1597
|
-
hooks: [
|
|
1598
|
-
{
|
|
1599
|
-
type: 'command',
|
|
1600
|
-
command: updateCheckCommand
|
|
1601
|
-
}
|
|
1602
|
-
]
|
|
1600
|
+
hooks: [{ type: 'command', command: updateCheckCommand }]
|
|
1603
1601
|
});
|
|
1604
1602
|
console.log(` ${green}✓${reset} Configured update check hook`);
|
|
1605
1603
|
}
|
|
1606
1604
|
|
|
1605
|
+
// Dashboard server hook — starts/restarts server for this project on SessionStart
|
|
1606
|
+
const hasServerHook = settings.hooks.SessionStart.some(entry =>
|
|
1607
|
+
entry.hooks && entry.hooks.some(h => h.command && h.command.includes('declare-server'))
|
|
1608
|
+
);
|
|
1609
|
+
if (!hasServerHook) {
|
|
1610
|
+
settings.hooks.SessionStart.push({
|
|
1611
|
+
hooks: [{ type: 'command', command: serverCommand }]
|
|
1612
|
+
});
|
|
1613
|
+
console.log(` ${green}✓${reset} Configured dashboard server hook`);
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1607
1616
|
// Configure PreToolUse + PostToolUse hooks for activity feed (Claude Code only)
|
|
1608
1617
|
if (!isOpencode && !isGemini) {
|
|
1609
1618
|
if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
|
|
@@ -6,71 +6,60 @@ allowed-tools:
|
|
|
6
6
|
|
|
7
7
|
Open the Declare interactive DAG dashboard — a live web UI showing declarations, milestones, and actions as a navigable graph.
|
|
8
8
|
|
|
9
|
-
**Step 1:
|
|
9
|
+
**Step 1: Resolve the port for this project.**
|
|
10
|
+
|
|
11
|
+
Each project gets its own stable port derived from the project path. Check if it's already been assigned:
|
|
10
12
|
|
|
11
13
|
```bash
|
|
12
|
-
|
|
14
|
+
cat .planning/server.port 2>/dev/null || echo "NOT_SET"
|
|
13
15
|
```
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
If a port file exists, use that port. If not, the SessionStart hook hasn't fired yet — use the default port 3847 and set PORT to it.
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
```bash
|
|
20
|
+
PORT=$(cat .planning/server.port 2>/dev/null || echo "3847")
|
|
21
|
+
echo "PORT=$PORT"
|
|
22
|
+
```
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
**Step 2: Check if the server is already running on that port.**
|
|
20
25
|
|
|
21
26
|
```bash
|
|
22
|
-
|
|
23
|
-
echo $!
|
|
27
|
+
curl -sf http://localhost:${PORT}/api/graph -o /dev/null && echo "RUNNING" || echo "NOT_RUNNING"
|
|
24
28
|
```
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
**Step 3: Start the server if it is not running.**
|
|
31
|
+
|
|
32
|
+
If `NOT_RUNNING`:
|
|
27
33
|
|
|
28
34
|
```bash
|
|
29
|
-
|
|
35
|
+
nohup node dist/declare-tools.cjs serve --port ${PORT} > /tmp/declare-dashboard.log 2>&1 &
|
|
36
|
+
sleep 1 && curl -sf http://localhost:${PORT}/api/graph -o /dev/null && echo "STARTED" || echo "FAILED"
|
|
30
37
|
```
|
|
31
38
|
|
|
32
|
-
If
|
|
33
|
-
|
|
39
|
+
If `FAILED`:
|
|
34
40
|
```bash
|
|
35
41
|
tail -20 /tmp/declare-dashboard.log
|
|
36
42
|
```
|
|
37
43
|
|
|
38
|
-
**Step
|
|
39
|
-
|
|
40
|
-
Detect the OS and open the URL:
|
|
44
|
+
**Step 4: Open the dashboard in the browser.**
|
|
41
45
|
|
|
42
46
|
```bash
|
|
43
47
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
44
|
-
open http://localhost
|
|
48
|
+
open http://localhost:${PORT}
|
|
45
49
|
else
|
|
46
|
-
xdg-open http://localhost
|
|
50
|
+
xdg-open http://localhost:${PORT} 2>/dev/null || echo "Visit http://localhost:${PORT} in your browser"
|
|
47
51
|
fi
|
|
48
52
|
```
|
|
49
53
|
|
|
50
|
-
**Step
|
|
51
|
-
|
|
52
|
-
Show the user:
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
Dashboard running at http://localhost:3847
|
|
56
|
-
|
|
57
|
-
The graph auto-refreshes every 5 seconds.
|
|
58
|
-
Click any node to inspect its details.
|
|
54
|
+
**Step 5: Confirm to the user.**
|
|
59
55
|
|
|
60
|
-
Server log: /tmp/declare-dashboard.log
|
|
61
|
-
To stop: kill <PID>
|
|
62
56
|
```
|
|
57
|
+
Dashboard running at http://localhost:[PORT]
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
If the server was already running (Step 1 returned `RUNNING`), say "Server was already running."
|
|
59
|
+
The graph updates live as agents run and files change.
|
|
60
|
+
Click any node to inspect details and exec-plan.
|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
If `$ARGUMENTS` contains `--tail` or `--log`:
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
tail -f /tmp/declare-dashboard.log
|
|
62
|
+
To stop: kill $(lsof -ti :[PORT])
|
|
74
63
|
```
|
|
75
64
|
|
|
76
|
-
|
|
65
|
+
If the server was already running (Step 2 returned `RUNNING`), say "Server was already running."
|
package/dist/declare-tools.cjs
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Declare dashboard server — SessionStart hook
|
|
3
|
+
//
|
|
4
|
+
// On every Claude Code session start (for a Declare project):
|
|
5
|
+
// 1. Derive a stable port for this project (hash of cwd, range 3847-4846)
|
|
6
|
+
// 2. If a server is already running on that port for THIS project, leave it
|
|
7
|
+
// 3. If a server is running on that port for a DIFFERENT project, kill it
|
|
8
|
+
// 4. Start a fresh server for the current project
|
|
9
|
+
// 5. Write the port to .planning/server.port for /declare:dashboard to read
|
|
10
|
+
//
|
|
11
|
+
// This means each project gets its own port, servers survive between sessions,
|
|
12
|
+
// and switching projects always gives you the right server.
|
|
13
|
+
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const http = require('http');
|
|
19
|
+
const cp = require('child_process');
|
|
20
|
+
|
|
21
|
+
const cwd = process.cwd();
|
|
22
|
+
const planningDir = path.join(cwd, '.planning');
|
|
23
|
+
|
|
24
|
+
// Only run for Declare projects
|
|
25
|
+
if (!fs.existsSync(planningDir)) process.exit(0);
|
|
26
|
+
|
|
27
|
+
const PORT_BASE = 3847;
|
|
28
|
+
const PORT_RANGE = 1000; // ports 3847–4846
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Simple djb2 hash to derive a stable port from the project path.
|
|
32
|
+
* @param {string} str
|
|
33
|
+
* @returns {number} port in [PORT_BASE, PORT_BASE + PORT_RANGE)
|
|
34
|
+
*/
|
|
35
|
+
function projectPort(str) {
|
|
36
|
+
let h = 5381;
|
|
37
|
+
for (let i = 0; i < str.length; i++) {
|
|
38
|
+
h = ((h << 5) + h) ^ str.charCodeAt(i);
|
|
39
|
+
h = h >>> 0; // keep unsigned 32-bit
|
|
40
|
+
}
|
|
41
|
+
return PORT_BASE + (h % PORT_RANGE);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const port = projectPort(cwd);
|
|
45
|
+
const portFile = path.join(planningDir, 'server.port');
|
|
46
|
+
const bundle = path.join(cwd, '.claude', 'declare-tools.cjs');
|
|
47
|
+
|
|
48
|
+
// If bundle doesn't exist, nothing to do
|
|
49
|
+
if (!fs.existsSync(bundle)) process.exit(0);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Ask the running server on `port` which cwd it's serving, via /api/graph.
|
|
53
|
+
* Returns the project name or null if nothing is running / not a Declare server.
|
|
54
|
+
*/
|
|
55
|
+
function checkRunningServer(port, callback) {
|
|
56
|
+
const req = http.get(`http://127.0.0.1:${port}/api/graph`, { timeout: 1500 }, res => {
|
|
57
|
+
let body = '';
|
|
58
|
+
res.on('data', c => body += c);
|
|
59
|
+
res.on('end', () => {
|
|
60
|
+
try {
|
|
61
|
+
const data = JSON.parse(body);
|
|
62
|
+
// If it has a valid graph response it's our server
|
|
63
|
+
callback(data && !data.error ? 'declare' : null);
|
|
64
|
+
} catch { callback(null); }
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
req.on('error', () => callback(null));
|
|
68
|
+
req.on('timeout', () => { req.destroy(); callback(null); });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Kill any process currently listening on the given port.
|
|
73
|
+
*/
|
|
74
|
+
function killPort(port) {
|
|
75
|
+
try {
|
|
76
|
+
const pid = cp.execSync(`lsof -ti :${port} 2>/dev/null`, { encoding: 'utf8' }).trim();
|
|
77
|
+
if (pid) {
|
|
78
|
+
pid.split('\n').forEach(p => {
|
|
79
|
+
try { process.kill(parseInt(p), 'SIGTERM'); } catch {}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
} catch {}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Start the dashboard server for this project in the background.
|
|
87
|
+
*/
|
|
88
|
+
function startServer() {
|
|
89
|
+
const child = cp.spawn(process.execPath, [bundle, 'serve', '--port', String(port)], {
|
|
90
|
+
cwd,
|
|
91
|
+
detached: true,
|
|
92
|
+
stdio: 'ignore',
|
|
93
|
+
});
|
|
94
|
+
child.unref();
|
|
95
|
+
|
|
96
|
+
// Write port file so /declare:dashboard knows where to open
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
try { fs.writeFileSync(portFile, String(port)); } catch {}
|
|
99
|
+
}, 800);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check if something is already running on this port
|
|
103
|
+
checkRunningServer(port, status => {
|
|
104
|
+
if (status === 'declare') {
|
|
105
|
+
// A Declare server is already up on our port.
|
|
106
|
+
// It might be from a previous session for this same project — reuse it.
|
|
107
|
+
// Write port file to make sure /declare:dashboard finds it.
|
|
108
|
+
try { fs.writeFileSync(portFile, String(port)); } catch {}
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Nothing running (or non-Declare process) — kill whatever is there and start fresh
|
|
113
|
+
killPort(port);
|
|
114
|
+
setTimeout(startServer, 300);
|
|
115
|
+
process.exit(0);
|
|
116
|
+
});
|
package/package.json
CHANGED