@infinitedusky/indusk-mcp 1.25.1 → 1.26.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/dist/bin/cli.js CHANGED
@@ -344,4 +344,13 @@ program
344
344
  const { startServer } = await import("../server/index.js");
345
345
  await startServer();
346
346
  });
347
+ program
348
+ .command("ui")
349
+ .description("Open the InDusk admin UI (sidebar of plans, trajectory states, scorecards)")
350
+ .option("--port <port>", "Port to listen on (0 = pick free)", "3939")
351
+ .option("--no-open", "Don't auto-open the browser when the server is ready")
352
+ .action(async (opts) => {
353
+ const { ui } = await import("./commands/ui.js");
354
+ await ui(rootOrExit(), { port: opts.port, open: opts.open });
355
+ });
347
356
  program.parse();
@@ -0,0 +1,22 @@
1
+ export interface UiOptions {
2
+ port: string;
3
+ open: boolean;
4
+ }
5
+ /**
6
+ * Spawn the indusk-admin Next.js dev server in the user's project root.
7
+ *
8
+ * - Runs `pnpm exec next dev` from `apps/indusk-admin/` so admin-ui's deps
9
+ * resolve correctly via the workspace.
10
+ * - Sets `INDUSK_PROJECT_ROOT` to the project root so the admin app's
11
+ * server components read the right `.indusk/planning/` regardless of
12
+ * `process.cwd()` inside the spawned process. (For now, admin-ui reads
13
+ * from `process.cwd()` directly — child process inherits cwd from the
14
+ * indusk CLI's invocation.)
15
+ * - Auto-bumps the port if the requested one is taken.
16
+ * - Optionally opens the browser when stdout reports "ready".
17
+ *
18
+ * v1 ships the source tree (admin-ui as a workspace app, dev mode). v2 may
19
+ * switch to a built static export if startup latency or package size become
20
+ * pain points.
21
+ */
22
+ export declare function ui(projectRoot: string, opts: UiOptions): Promise<void>;
@@ -0,0 +1,118 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { createServer } from "node:net";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ /**
7
+ * Spawn the indusk-admin Next.js dev server in the user's project root.
8
+ *
9
+ * - Runs `pnpm exec next dev` from `apps/indusk-admin/` so admin-ui's deps
10
+ * resolve correctly via the workspace.
11
+ * - Sets `INDUSK_PROJECT_ROOT` to the project root so the admin app's
12
+ * server components read the right `.indusk/planning/` regardless of
13
+ * `process.cwd()` inside the spawned process. (For now, admin-ui reads
14
+ * from `process.cwd()` directly — child process inherits cwd from the
15
+ * indusk CLI's invocation.)
16
+ * - Auto-bumps the port if the requested one is taken.
17
+ * - Optionally opens the browser when stdout reports "ready".
18
+ *
19
+ * v1 ships the source tree (admin-ui as a workspace app, dev mode). v2 may
20
+ * switch to a built static export if startup latency or package size become
21
+ * pain points.
22
+ */
23
+ export async function ui(projectRoot, opts) {
24
+ const requestedPort = Number.parseInt(opts.port, 10);
25
+ if (!Number.isFinite(requestedPort) || requestedPort < 0 || requestedPort > 65535) {
26
+ console.error(`Invalid --port value: ${opts.port}`);
27
+ process.exit(1);
28
+ }
29
+ const adminDir = resolveAdminDir();
30
+ if (!existsSync(adminDir)) {
31
+ console.error(`indusk-admin app not found at ${adminDir}. ` +
32
+ "If running from a published package, ensure indusk-admin is bundled.");
33
+ process.exit(1);
34
+ }
35
+ const port = requestedPort === 0 ? await findFreePort() : await ensureFreePort(requestedPort);
36
+ if (port !== requestedPort && requestedPort !== 0) {
37
+ console.warn(`Port ${requestedPort} is in use; using ${port} instead.`);
38
+ }
39
+ console.info(`Starting indusk-admin on http://localhost:${port}/`);
40
+ console.info(`Project root: ${projectRoot}`);
41
+ const child = spawn("pnpm", ["exec", "next", "dev", "--port", String(port)], {
42
+ cwd: adminDir,
43
+ env: {
44
+ ...process.env,
45
+ INDUSK_PROJECT_ROOT: projectRoot,
46
+ },
47
+ stdio: ["inherit", "pipe", "inherit"],
48
+ });
49
+ let opened = false;
50
+ child.stdout.on("data", (chunk) => {
51
+ const text = chunk.toString();
52
+ process.stdout.write(text);
53
+ if (!opened && opts.open && /\bReady\b|\bready in\b|started server/i.test(text)) {
54
+ opened = true;
55
+ openBrowser(`http://localhost:${port}/`);
56
+ }
57
+ });
58
+ child.on("close", (code) => {
59
+ process.exit(code ?? 0);
60
+ });
61
+ process.on("SIGINT", () => child.kill("SIGINT"));
62
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
63
+ }
64
+ /**
65
+ * Resolve the admin app directory. In the source repo, that's the sibling
66
+ * `apps/indusk-admin/`. When indusk-mcp is npm-installed, this needs to
67
+ * resolve to the bundled path (decided in Phase 6 — for now only the source
68
+ * layout is supported).
69
+ */
70
+ function resolveAdminDir() {
71
+ const here = dirname(fileURLToPath(import.meta.url));
72
+ // here = apps/indusk-mcp/dist/bin/commands/ (or .../src/bin/commands/ in dev)
73
+ // Walk up to apps/, then into indusk-admin
74
+ const candidates = [
75
+ resolve(here, "../../../../indusk-admin"),
76
+ resolve(here, "../../../indusk-admin"),
77
+ ];
78
+ for (const c of candidates) {
79
+ if (existsSync(join(c, "package.json")))
80
+ return c;
81
+ }
82
+ return candidates[0];
83
+ }
84
+ async function ensureFreePort(port) {
85
+ if (await isPortFree(port))
86
+ return port;
87
+ return findFreePort();
88
+ }
89
+ function isPortFree(port) {
90
+ return new Promise((resolveProm) => {
91
+ const server = createServer();
92
+ server.once("error", () => resolveProm(false));
93
+ server.once("listening", () => {
94
+ server.close(() => resolveProm(true));
95
+ });
96
+ server.listen(port);
97
+ });
98
+ }
99
+ function findFreePort() {
100
+ return new Promise((resolveProm, rejectProm) => {
101
+ const server = createServer();
102
+ server.once("error", rejectProm);
103
+ server.listen(0, () => {
104
+ const addr = server.address();
105
+ if (typeof addr === "object" && addr !== null) {
106
+ const port = addr.port;
107
+ server.close(() => resolveProm(port));
108
+ }
109
+ else {
110
+ rejectProm(new Error("Could not determine free port"));
111
+ }
112
+ });
113
+ });
114
+ }
115
+ function openBrowser(url) {
116
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
117
+ spawn(cmd, [url], { stdio: "ignore", detached: true }).unref();
118
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infinitedusky/indusk-mcp",
3
- "version": "1.25.1",
3
+ "version": "1.26.0",
4
4
  "description": "InDusk development system — skills, MCP tools, and CLI for structured AI-assisted development",
5
5
  "type": "module",
6
6
  "files": [
@@ -18,7 +18,15 @@
18
18
  "dev-system": "dist/bin/cli.js"
19
19
  },
20
20
  "exports": {
21
- ".": "./dist/server/index.js"
21
+ ".": "./dist/server/index.js",
22
+ "./trajectory/parser": {
23
+ "types": "./dist/lib/trajectory/parser.d.ts",
24
+ "default": "./dist/lib/trajectory/parser.js"
25
+ },
26
+ "./falsification/log": {
27
+ "types": "./dist/lib/falsification/log.d.ts",
28
+ "default": "./dist/lib/falsification/log.js"
29
+ }
22
30
  },
23
31
  "scripts": {
24
32
  "dev": "tsx watch src/server/index.ts",