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 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 opens the dashboard in your browser.
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` opens it, and you drive the workflow with keyboard shortcuts and card-based UI.
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
- ### Server Discovery
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
- On startup, the server writes the port number to `.planning/server.port` (plain text, e.g. `3847`). On shutdown, it deletes this file. External tools can read this file to embed the dashboard in an iframe.
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` | Open the dashboard |
233
+ | `/declare:dashboard` | Start server and print URL |
149
234
  | `/declare:help` | Show all commands |
150
235
 
151
236
  ---
@@ -1552,7 +1552,7 @@ var require_help = __commonJS({
1552
1552
  usage: "/declare:help"
1553
1553
  }
1554
1554
  ],
1555
- version: "1.0.3"
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) || 3847;
8909
- const resolvedPort = await findFreePort(preferredPort);
8910
- const server = createServer(cwd, resolvedPort);
8911
- await new Promise((resolve) => {
8912
- server.listen(resolvedPort, "127.0.0.1", () => {
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(void 0);
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) || parseInt(process.env.PORT || "", 10) || 3847;
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 waitForServer(port, maxAttempts = 10, intervalMs = 100) {
8992
+ async function waitForPortFile(portFile, maxAttempts = 30, intervalMs = 200) {
8995
8993
  for (let i = 0; i < maxAttempts; i++) {
8996
- if (await checkServer(port)) return true;
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 false;
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
- const port = fs.existsSync(portFile) ? parseInt(fs.readFileSync(portFile, "utf8").trim(), 10) : 3847;
9012
- const isRunning = await checkServer(port);
9013
- if (!isRunning) {
9014
- const bundlePath = path.resolve(__dirname, "declare-tools.cjs");
9015
- const child = spawn(process.execPath, [bundlePath, "serve", "--port", String(port)], {
9016
- cwd,
9017
- detached: true,
9018
- stdio: "ignore"
9019
- });
9020
- child.unref();
9021
- const ready = await waitForServer(port);
9022
- if (!ready) {
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 url = `http://localhost:${port}`;
9027
- const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
9028
- spawn(opener, [url], { stdio: "ignore", detached: true }).unref();
9029
- console.log(`Dashboard: ${url}`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "declare-cc",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "A future-driven meta-prompting engine for agentic development, rooted in declared futures and causal graph structure.",
5
5
  "bin": {
6
6
  "declare-cc": "bin/install.js",
@@ -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: