declare-cc 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -5
- package/dist/declare-tools.cjs +45 -34
- package/package.json +1 -1
- package/scripts/release.js +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Run it:
|
|
|
37
37
|
npm run plan
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
This auto-initializes a `.planning/` directory if one doesn't exist, starts the Declare server, writes the port to `.planning/server.port`, and
|
|
40
|
+
This auto-initializes a `.planning/` directory if one doesn't exist, starts the Declare server on a random free port, writes the port to `.planning/server.port`, and prints the dashboard URL.
|
|
41
41
|
|
|
42
42
|
Or run directly:
|
|
43
43
|
|
|
@@ -59,7 +59,7 @@ You declare present-tense statements of fact about your project's future. The sy
|
|
|
59
59
|
|
|
60
60
|
## How It Works
|
|
61
61
|
|
|
62
|
-
Everything happens through the dashboard. `dcl`
|
|
62
|
+
Everything happens through the dashboard. `dcl` starts the server and prints the URL — click it to open.
|
|
63
63
|
|
|
64
64
|
### 1. Declare Futures
|
|
65
65
|
|
|
@@ -125,9 +125,94 @@ Cards are grouped into stages at each level:
|
|
|
125
125
|
| **Ctrl+Shift+A** | Approve all visible |
|
|
126
126
|
| **C** | Command bar |
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Server & Port Discovery
|
|
131
|
+
|
|
132
|
+
The Declare server uses OS-assigned ports so multiple projects can run simultaneously without collisions.
|
|
133
|
+
|
|
134
|
+
### How it works
|
|
135
|
+
|
|
136
|
+
1. `dcl` (or `dcl serve`) starts the server on **port 0** — the OS assigns a random free port
|
|
137
|
+
2. After the server is listening, it writes the port number to `.planning/server.port` (plain text, e.g. `62964`)
|
|
138
|
+
3. On shutdown (SIGINT, SIGTERM, process exit), the file is deleted
|
|
139
|
+
4. Next time `dcl` runs, it reads the port file. If the server is still alive, it reuses it. If the file is stale (server crashed), it cleans up and starts fresh.
|
|
140
|
+
|
|
141
|
+
### For external tools
|
|
142
|
+
|
|
143
|
+
To discover a running Declare server for a project:
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
const port = fs.readFileSync('<project>/.planning/server.port', 'utf8').trim();
|
|
147
|
+
// Verify it's alive:
|
|
148
|
+
// GET http://localhost:<port>/api/graph → 200 = running
|
|
149
|
+
// Embed dashboard:
|
|
150
|
+
// <iframe src="http://localhost:<port>/" />
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
If the file doesn't exist, the server isn't running. Start it:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
cd <project> && npx dcl serve
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Then poll for `.planning/server.port` to appear (~1 second).
|
|
160
|
+
|
|
161
|
+
### Explicit port
|
|
162
|
+
|
|
163
|
+
If you need a specific port:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
dcl serve --port 4000
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## For Agents
|
|
172
|
+
|
|
173
|
+
If you're an AI agent (Claude Code, Cursor, etc.) working in a project that uses Declare, here's what you need to know:
|
|
174
|
+
|
|
175
|
+
### Project state lives in `.planning/`
|
|
176
|
+
|
|
177
|
+
- **`FUTURE.md`** — Declared futures (present-tense statements about what's true when the project succeeds)
|
|
178
|
+
- **`MILESTONES.md`** — Milestones derived backward from futures
|
|
179
|
+
- **`.planning/milestones/M-XX-slug/PLAN.md`** — Actions for each milestone (title, produces, causes)
|
|
180
|
+
- **`.planning/STATE.md`** — Current project state and decisions
|
|
181
|
+
- **`.planning/PROJECT.md`** — Project context and background
|
|
182
|
+
|
|
183
|
+
### The DAG structure
|
|
184
|
+
|
|
185
|
+
Declarations (D-XX) → Milestones (M-XX) → Actions (A-XX). Each layer links to the one above via `realizes` (milestones → declarations) and `causes` (actions → milestones). Read the graph with:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
node_modules/.bin/declare-cc load-graph
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Returns JSON with `declarations`, `milestones`, `actions` arrays.
|
|
192
|
+
|
|
193
|
+
### Slash commands available to you
|
|
194
|
+
|
|
195
|
+
If you're running inside Claude Code with declare-cc installed, these slash commands are available:
|
|
196
|
+
|
|
197
|
+
- `/declare:status` — See where the project stands
|
|
198
|
+
- `/declare:execute M-XX` — Execute actions for a milestone
|
|
199
|
+
- `/declare:verify M-XX` — Validate deliverables
|
|
200
|
+
- `/declare:trace A-XX` — Understand why an action exists (walk the why-chain)
|
|
201
|
+
- `/declare:progress` — Find the next thing to work on
|
|
202
|
+
- `/declare:help` — See all commands
|
|
203
|
+
|
|
204
|
+
### Dashboard API
|
|
205
|
+
|
|
206
|
+
If the server is running (check `.planning/server.port`), you can use the HTTP API:
|
|
129
207
|
|
|
130
|
-
|
|
208
|
+
| Method | Path | Returns |
|
|
209
|
+
|--------|------|---------|
|
|
210
|
+
| GET | `/api/graph` | Full DAG (declarations, milestones, actions) |
|
|
211
|
+
| GET | `/api/status` | Integrity/alignment metrics |
|
|
212
|
+
| GET | `/api/agents` | Running/completed agents |
|
|
213
|
+
| GET | `/api/events` | SSE stream (real-time updates) |
|
|
214
|
+
| POST | `/api/review` | Approve/reject a node |
|
|
215
|
+
| POST | `/api/action/:id/execute` | Execute an action |
|
|
131
216
|
|
|
132
217
|
---
|
|
133
218
|
|
|
@@ -145,7 +230,7 @@ The dashboard is the primary interface. All operations are also available as sla
|
|
|
145
230
|
| `/declare:audit M-XX` | Cross-reference against declarations |
|
|
146
231
|
| `/declare:trace A-XX` | Walk the why-chain to its declaration |
|
|
147
232
|
| `/declare:status` | Graph health and layer counts |
|
|
148
|
-
| `/declare:dashboard` |
|
|
233
|
+
| `/declare:dashboard` | Start server and print URL |
|
|
149
234
|
| `/declare:help` | Show all commands |
|
|
150
235
|
|
|
151
236
|
---
|
package/dist/declare-tools.cjs
CHANGED
|
@@ -1552,7 +1552,7 @@ var require_help = __commonJS({
|
|
|
1552
1552
|
usage: "/declare:help"
|
|
1553
1553
|
}
|
|
1554
1554
|
],
|
|
1555
|
-
version: "1.0.
|
|
1555
|
+
version: "1.0.5"
|
|
1556
1556
|
};
|
|
1557
1557
|
}
|
|
1558
1558
|
module2.exports = { runHelp: runHelp2 };
|
|
@@ -8905,13 +8905,17 @@ data: ${JSON.stringify({ reason: "delete", nodeId: id })}
|
|
|
8905
8905
|
});
|
|
8906
8906
|
}
|
|
8907
8907
|
async function startServer(cwd, port) {
|
|
8908
|
-
const preferredPort = port || parseInt(process.env.PORT || "", 10) ||
|
|
8909
|
-
const
|
|
8910
|
-
const server = createServer(cwd,
|
|
8911
|
-
await new Promise((resolve) => {
|
|
8912
|
-
server.listen(
|
|
8908
|
+
const preferredPort = port || parseInt(process.env.PORT || "", 10) || 0;
|
|
8909
|
+
const listenPort = preferredPort === 0 ? 0 : await findFreePort(preferredPort);
|
|
8910
|
+
const server = createServer(cwd, listenPort);
|
|
8911
|
+
const resolvedPort = await new Promise((resolve) => {
|
|
8912
|
+
server.listen(listenPort, "127.0.0.1", () => {
|
|
8913
|
+
const assigned = (
|
|
8914
|
+
/** @type {import('net').AddressInfo} */
|
|
8915
|
+
server.address().port
|
|
8916
|
+
);
|
|
8913
8917
|
watchPlanning(cwd);
|
|
8914
|
-
resolve(
|
|
8918
|
+
resolve(assigned);
|
|
8915
8919
|
});
|
|
8916
8920
|
});
|
|
8917
8921
|
const portFilePath = require("path").join(cwd, ".planning", "server.port");
|
|
@@ -8941,7 +8945,6 @@ data: ${JSON.stringify({ reason: "delete", nodeId: id })}
|
|
|
8941
8945
|
var require_serve = __commonJS({
|
|
8942
8946
|
"src/commands/serve.js"(exports2, module2) {
|
|
8943
8947
|
"use strict";
|
|
8944
|
-
var { spawn } = require("child_process");
|
|
8945
8948
|
var { startServer } = require_server();
|
|
8946
8949
|
function parsePortFlag(args) {
|
|
8947
8950
|
const idx = args.indexOf("--port");
|
|
@@ -8950,13 +8953,8 @@ var require_serve = __commonJS({
|
|
|
8950
8953
|
return Number.isNaN(value) ? void 0 : value;
|
|
8951
8954
|
}
|
|
8952
8955
|
async function runServe2(cwd, args) {
|
|
8953
|
-
const port = parsePortFlag(args)
|
|
8956
|
+
const port = parsePortFlag(args);
|
|
8954
8957
|
const { server, port: resolvedPort, url } = await startServer(cwd, port);
|
|
8955
|
-
const noOpen = args.includes("--no-open") || process.env.DECLARE_NO_OPEN;
|
|
8956
|
-
if (!noOpen) {
|
|
8957
|
-
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
8958
|
-
spawn(opener, [url], { stdio: "ignore", detached: true }).unref();
|
|
8959
|
-
}
|
|
8960
8958
|
process.on("SIGINT", () => {
|
|
8961
8959
|
server.close(() => process.exit(0));
|
|
8962
8960
|
});
|
|
@@ -8991,12 +8989,17 @@ var require_open = __commonJS({
|
|
|
8991
8989
|
});
|
|
8992
8990
|
});
|
|
8993
8991
|
}
|
|
8994
|
-
async function
|
|
8992
|
+
async function waitForPortFile(portFile, maxAttempts = 30, intervalMs = 200) {
|
|
8995
8993
|
for (let i = 0; i < maxAttempts; i++) {
|
|
8996
|
-
|
|
8994
|
+
try {
|
|
8995
|
+
const content = fs.readFileSync(portFile, "utf8").trim();
|
|
8996
|
+
const port = parseInt(content, 10);
|
|
8997
|
+
if (!isNaN(port) && port > 0) return port;
|
|
8998
|
+
} catch (_) {
|
|
8999
|
+
}
|
|
8997
9000
|
await new Promise((r) => setTimeout(r, intervalMs));
|
|
8998
9001
|
}
|
|
8999
|
-
return
|
|
9002
|
+
return null;
|
|
9000
9003
|
}
|
|
9001
9004
|
async function runOpen2(cwd, args) {
|
|
9002
9005
|
const planningDir = path.join(cwd, ".planning");
|
|
@@ -9008,25 +9011,33 @@ var require_open = __commonJS({
|
|
|
9008
9011
|
}
|
|
9009
9012
|
}
|
|
9010
9013
|
const portFile = path.join(cwd, ".planning", "server.port");
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
console.error("[declare] Warning: server may not be ready yet");
|
|
9014
|
+
if (fs.existsSync(portFile)) {
|
|
9015
|
+
const existingPort = parseInt(fs.readFileSync(portFile, "utf8").trim(), 10);
|
|
9016
|
+
if (!isNaN(existingPort) && existingPort > 0) {
|
|
9017
|
+
const isRunning = await checkServer(existingPort);
|
|
9018
|
+
if (isRunning) {
|
|
9019
|
+
console.log(`Dashboard: http://localhost:${existingPort}`);
|
|
9020
|
+
return;
|
|
9021
|
+
}
|
|
9022
|
+
try {
|
|
9023
|
+
fs.unlinkSync(portFile);
|
|
9024
|
+
} catch (_) {
|
|
9025
|
+
}
|
|
9024
9026
|
}
|
|
9025
9027
|
}
|
|
9026
|
-
const
|
|
9027
|
-
const
|
|
9028
|
-
|
|
9029
|
-
|
|
9028
|
+
const bundlePath = path.resolve(__dirname, "declare-tools.cjs");
|
|
9029
|
+
const child = spawn(process.execPath, [bundlePath, "serve"], {
|
|
9030
|
+
cwd,
|
|
9031
|
+
detached: true,
|
|
9032
|
+
stdio: "ignore"
|
|
9033
|
+
});
|
|
9034
|
+
child.unref();
|
|
9035
|
+
const port = await waitForPortFile(portFile);
|
|
9036
|
+
if (!port) {
|
|
9037
|
+
console.error("[declare] Server failed to start (no port file after 6s)");
|
|
9038
|
+
process.exit(1);
|
|
9039
|
+
}
|
|
9040
|
+
console.log(`Dashboard: http://localhost:${port}`);
|
|
9030
9041
|
}
|
|
9031
9042
|
module2.exports = { runOpen: runOpen2 };
|
|
9032
9043
|
}
|
package/package.json
CHANGED
package/scripts/release.js
CHANGED
|
@@ -40,7 +40,7 @@ run('npm run build');
|
|
|
40
40
|
// 3. Commit + tag
|
|
41
41
|
run(`git add package.json package-lock.json dist/`);
|
|
42
42
|
run(`git commit -m "chore: bump version to ${version}"`);
|
|
43
|
-
run(`git tag v${version}`);
|
|
43
|
+
run(`git tag -f v${version}`);
|
|
44
44
|
|
|
45
45
|
console.log(`
|
|
46
46
|
Done. To publish:
|