Skip to content

Commit dab2922

Browse files
committed
chore: release v0.8.0 - long-lived worker, gateway, CI, docs
- TCP daemon, worker.lock, worker start/stop/status exit codes - Gateway + socket client; graph export/map via CLI; MCP version 0.8.0 - E2E tests, GitHub Actions CI; README/INSTALL/MCP guide/PLAN updates - Remove tracked .npmrc (use local file only; rotate token if exposed) - release-notes-v0.8.0.md Made-with: Cursor
1 parent 8aa2ad6 commit dab2922

32 files changed

Lines changed: 1805 additions & 370 deletions

.github/workflows/ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
9+
jobs:
10+
build-test:
11+
runs-on: windows-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-node@v4
15+
with:
16+
node-version: '20'
17+
cache: npm
18+
- run: npm ci
19+
- run: npm run build
20+
- run: npm test
21+
- run: npm run test:daemon

.npmrc

Lines changed: 0 additions & 2 deletions
This file was deleted.

README.md

Lines changed: 154 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,77 +5,200 @@ Path-only SysML indexer: builds a knowledge graph from `.sysml` files and expose
55
- **npm:** [sysmledgraph](https://www.npmjs.com/package/sysmledgraph)`npm install sysmledgraph`
66
- **Repo:** [github.com/chouswei/codebase-sysmledgraph](https://github.com/chouswei/codebase-sysmledgraph)
77

8+
## Contents
9+
10+
- [Version](#version)
11+
- [Requirements](#requirements)
12+
- [Install](#install)
13+
- [Quick start](#quick-start)
14+
- [Usage](#usage)
15+
- [Environment variables](#environment-variables)
16+
- [CLI (`sysmledgraph`)](#cli-sysmledgraph)
17+
- [MCP binary (`sysmledgraph-mcp`)](#mcp-binary-sysmledgraph-mcp)
18+
- [npm scripts](#npm-scripts-repository--packagejson)
19+
- [Node scripts in `scripts/`](#node-scripts-in-scripts-after-npm-run-build-where-noted)
20+
- [MCP tools and resources](#mcp-tools-and-resources)
21+
- [Documentation](#documentation)
22+
- [Continuous integration](#continuous-integration)
23+
- [Publishing (npm)](#publishing-npm)
24+
- [License](#license)
25+
26+
## Version
27+
28+
- **Current package version:** **0.8.0** — bump this line when you release; [`package.json`](package.json) `"version"` is what npm publishes.
29+
- **Policy:** [Semantic versioning](https://semver.org/)**MAJOR** / **MINOR** / **PATCH** as usual. CLI and MCP tool names and shapes are treated as stable within a major line unless release notes say otherwise.
30+
31+
To ship: set `"version"` in `package.json`, note changes (e.g. `release-notes-v*.md`), run `npm run build`, `npm test`, and `npm run test:daemon`, then `npm publish` — see [Publishing (npm)](#publishing-npm).
32+
833
## Requirements
934

10-
- **Node.js 20+**
11-
- **Kuzu** (built via `npm install` or `node node_modules/kuzu/install.js` if you use `--ignore-scripts`)
12-
- **LSP** for indexing: [sysml-v2-lsp](https://www.npmjs.com/package/sysml-v2-lsp), installed in **`lsp/`** (see below)
35+
- **Node.js****20+** (`package.json` `"engines"`: `>=20`).
36+
- **Kuzu** — Native addon built during `npm install`, or run `node node_modules/kuzu/install.js` if you used `--ignore-scripts`.
37+
- **LSP** [sysml-v2-lsp](https://www.npmjs.com/package/sysml-v2-lsp) for indexing, normally installed under **`lsp/`** via `npm run setup-lsp`.
1338

1439
## Install
1540

16-
**From npm (use as CLI/MCP):**
41+
**From npm (CLI + MCP):**
42+
1743
```bash
1844
npm install sysmledgraph
1945
cd node_modules/sysmledgraph && npm run setup-lsp
2046
```
2147

22-
**From source (develop or run from repo):**
48+
**From source:**
49+
2350
```bash
2451
git clone https://github.com/chouswei/codebase-sysmledgraph.git && cd codebase-sysmledgraph
2552
npm install
2653
npm run build
2754
npm run setup-lsp
2855
```
2956

30-
- **setup-lsp** installs the SysML LSP in **`lsp/`** so the indexer can use it. No need to set `SYSMLLSP_SERVER_PATH` when using this default. See [docs/INSTALL.md](docs/INSTALL.md) and [docs/PLAN_INDEPENDENT_LSP.md](docs/PLAN_INDEPENDENT_LSP.md).
57+
After **setup-lsp**, you usually do **not** need **`SYSMLLSP_SERVER_PATH`**. Details: [docs/INSTALL.md](docs/INSTALL.md), [docs/PLAN_INDEPENDENT_LSP.md](docs/PLAN_INDEPENDENT_LSP.md).
58+
59+
## Quick start
60+
61+
From a **clone** (after install + build + setup-lsp):
62+
63+
```bash
64+
npx sysmledgraph analyze test/fixtures/sysml
65+
npx sysmledgraph graph map graph-map.md
66+
```
67+
68+
Or one step from repo root: `npm run index-and-map` (optional path argument).
3169

3270
## Usage
3371

34-
### CLI
72+
### Environment variables
3573

36-
| Command | Description |
74+
| Variable | Purpose |
75+
|----------|---------|
76+
| **`SYSMEDGRAPH_STORAGE_ROOT`** | Graph storage directory (default `~/.sysmledgraph`). Merged DB: `<root>/db/graph.kuzu`. |
77+
| **`SYSMLEGRAPH_WORKER_URL`** | Long-lived worker endpoint, e.g. `127.0.0.1:PORT` (overrides reading `worker.port` from storage root). |
78+
| **`SYSMLEGRAPH_WORKER_STRICT`** | Set to **`1`** so graph clients **fail** if the TCP worker is unreachable (no silent in-process fallback). |
79+
| **`SYSMLEGRAPH_WORKER_PORT`** | Daemon bind port; **`0`** = OS-assigned (default when unset). Used when running `worker:daemon` / `daemon.js` directly. |
80+
| **`SYSMLLSP_SERVER_PATH`** | Path to sysml-v2-lsp **`server.js`** if not using default `lsp/` or `node_modules` resolution. |
81+
| **`SYSMEDGRAPH_USE_MCP_SYMBOLS`** | Set to **`1`** to fall back to MCP `getSymbols` when LSP returns no symbols (indexing). |
82+
| **`SYSMLEDGRAPH_USE_WORKER`** | Set to **`1`** to use a **per-command** stdio graph worker instead of in-process Kuzu (short-lived child). |
83+
84+
### Global CLI option
85+
86+
| Option | Description |
3787
|--------|-------------|
38-
| `npx sysmledgraph analyze <path>` | Index path(s); discover `.sysml`, build graph (uses LSP in `lsp/` by default) |
39-
| `npx sysmledgraph list` | List indexed root paths |
40-
| `npx sysmledgraph clean [path]` | Remove index for path or all |
88+
| `--storage <path>` | Same as **`SYSMEDGRAPH_STORAGE_ROOT`** for this invocation. |
89+
90+
Use it on **`worker`** and **`graph`** subcommands (and anywhere storage applies).
91+
92+
### CLI (`sysmledgraph`)
93+
94+
Use **`npx sysmledgraph`**, a global install, or **`node node_modules/sysmledgraph/dist/bin/cli.js`**.
4195

42-
**Scripts (from repo root):**
96+
| Command | Description |
97+
|---------|-------------|
98+
| `sysmledgraph analyze <paths...>` | Index path(s). **Default command** if you omit the subcommand name. |
99+
| `sysmledgraph list` | Print indexed root paths. |
100+
| `sysmledgraph clean [path]` | Drop one path from the index, or all paths if omitted. |
101+
| `sysmledgraph worker start [--detach]` | TCP daemon; writes **`worker.port`** + PID under the storage root. |
102+
| `sysmledgraph worker stop` | Shutdown RPC + cleanup; removes **`worker.port`**. |
103+
| `sysmledgraph worker status` | Exit **0** if TCP responds; **1** if not (stderr notes stale **`worker.port`** when applicable). |
104+
| `sysmledgraph graph export [file]` | JSON export (default **`graph-export.json`** in cwd). |
105+
| `sysmledgraph graph map [file]` | Markdown map (default **`graph-map.md`** in cwd). |
106+
107+
**`worker start` exit codes:** **0** started · **2** already running (TCP up) · **1** other failure (e.g. not built, stale port + live PID). **`worker stop`:** **1** if no **`worker.port`**. **`worker status`:** **1** when not running.
43108

44-
- `npm run index-and-map [path]` — Index path (default `test/fixtures/sysml`), then write **graph-map.md**
45-
- `npm run generate-map [out.md]` — Generate map from existing DB
46-
- `npm run setup-lsp` — Install LSP in `lsp/` (Option C, see plan)
109+
**Examples:** `npx sysmledgraph analyze ./models` · `npx sysmledgraph --storage D:\store list` · `npx sysmledgraph worker start --detach`
47110

48-
### MCP (Cursor)
111+
**Help:** `sysmledgraph --help` · `sysmledgraph worker --help` · `sysmledgraph graph --help`
49112

50-
This repo runs as an **MCP server** so Cursor AI can query the graph (query, context, impact, generate_map, indexDbGraph, cypher, etc.). Add **sysmledgraph** to `.cursor/mcp.json`; see [docs/MCP_SERVER_FOR_CURSOR.md](docs/MCP_SERVER_FOR_CURSOR.md).
113+
If **`worker.port`** exists or **`SYSMLEGRAPH_WORKER_URL`** is set, the CLI uses the **long-lived worker** and avoids opening Kuzu in-process. See [docs/INSTALL.md](docs/INSTALL.md).
51114

52-
**Storage:** Default `~/.sysmledgraph`. Override with `SYSMEDGRAPH_STORAGE_ROOT`. Only one process should open the same DB at a time (see [docs/MCP_INTERACTION_GUIDE.md](docs/MCP_INTERACTION_GUIDE.md) §8).
115+
### MCP binary (`sysmledgraph-mcp`)
53116

54-
## Docs
117+
| Command | Description |
118+
|---------|-------------|
119+
| `npx sysmledgraph-mcp` | MCP server on **stdio** (Cursor, etc.). |
120+
| `npm run mcp` | Same from a clone (**requires** `npm run build`). |
121+
122+
### npm scripts (repository / package.json)
123+
124+
| Script | Command | Description |
125+
|--------|---------|-------------|
126+
| `build` | `tsc` | Compile → **`dist/`**. |
127+
| `clean` | `node scripts/clean.mjs` | Clean artifacts (see script). |
128+
| `test` | `vitest run` | Default unit/integration tests. |
129+
| `test:daemon` | `vitest run --config vitest.e2e.config.ts` | Long-lived worker E2E. |
130+
| `test:watch` | `vitest` | Watch mode. |
131+
| `worker:daemon` | `node dist/src/worker/daemon.js` | Run daemon only (**after** `build`). |
132+
| `analyze` | `node dist/bin/cli.js analyze` | Shortcut. |
133+
| `export-graph` | `node dist/bin/cli.js graph export` | Shortcut. |
134+
| `generate-map` | `node dist/bin/cli.js graph map` | Shortcut. |
135+
| `index-and-map` | `node scripts/index-and-map.mjs` | Index then **`graph-map.md`**. |
136+
| `deploy-skills` | `node scripts/deploy-skills.mjs` | Maintainer: deploy Cursor skills. |
137+
| `mcp` | `node dist/mcp/index.js` | MCP stdio server. |
138+
| `check:sysml-lsp` | `node scripts/check-sysml-v2-lsp-version.mjs` | Version alignment check. |
139+
| `setup-lsp` | `node scripts/setup-lsp.mjs` | Install LSP under **`lsp/`**. |
140+
| `prepublishOnly` | `npm run build` | Runs before **`npm publish`**. |
141+
142+
**Args through npm:** `npm run generate-map -- custom.md` · `npm run export-graph -- out.json`
143+
144+
### Node scripts in `scripts/` (after `npm run build` where noted)
145+
146+
| Script | Purpose |
147+
|--------|---------|
148+
| `node scripts/index-and-map.mjs [path]` | Index (default **`test/fixtures/sysml`**), then map → **`graph-map.md`**. |
149+
| `node scripts/index-and-query.mjs <path>` | Index one path, then node count via gateway (**merged** **`graph.kuzu`**). |
150+
| `node scripts/export-graph.mjs` | Delegates to CLI **`graph export`**. |
151+
| `node scripts/generate-map.mjs` | Delegates to CLI **`graph map`**. |
152+
| `node scripts/validate-sysml-file.mjs <file.sysml>` | Validate via MCP; exit **0** / **1**. |
153+
| `node scripts/setup-lsp.mjs` | Same as **`npm run setup-lsp`**. |
154+
| `node scripts/clean.mjs` | Clean helper. |
155+
| `node scripts/check-sysml-v2-lsp-version.mjs` | LSP version check. |
156+
| `node scripts/deploy-skills.mjs` | Skills deploy. |
157+
| **Dev / debug** | `access-sysml-mcp.mjs`, `example-sysml-mcp.mjs`, `debug-lsp-symbols.mjs`, `debug-index.mjs`, `compare-mcp-vs-lsp-symbols.mjs`, `query-one.mjs`, `test-lsp.mjs`, `test-mcp-*.mjs`, `test-sysml-mcp-from-node_modules.mjs` — see headers in each file. |
158+
159+
## MCP tools and resources
160+
161+
Configure **sysmledgraph** in **`.cursor/mcp.json`** (see [docs/MCP_SERVER_FOR_CURSOR.md](docs/MCP_SERVER_FOR_CURSOR.md)).
162+
163+
**Tools:** `indexDbGraph`, `list_indexed`, `clean_index`, `cypher`, `query`, `context`, `impact`, `rename`, `generate_map`.
164+
165+
**Resources:** `sysmledgraph://context`, `sysmledgraph://schema`, plus per-indexed-path **`sysmledgraph://context/...`** and **`sysmledgraph://schema/...`**.
166+
167+
**Kuzu lock:** Prefer one mode per storage root — e.g. run **`sysmledgraph worker start --detach`** and share **`SYSMEDGRAPH_STORAGE_ROOT`** with MCP, or see [docs/MCP_INTERACTION_GUIDE.md](docs/MCP_INTERACTION_GUIDE.md) §6.1 and §8.
168+
169+
## Documentation
55170

56171
| Doc | Content |
57-
|-----|--------|
58-
| [docs/INSTALL.md](docs/INSTALL.md) | Install steps, LSP, Kuzu |
59-
| [docs/MCP_SERVER_FOR_CURSOR.md](docs/MCP_SERVER_FOR_CURSOR.md) | Enable sysmledgraph MCP in Cursor, tools, checklist |
172+
|-----|---------|
173+
| [docs/INSTALL.md](docs/INSTALL.md) | Install, LSP, Kuzu, worker |
174+
| [docs/MCP_SERVER_FOR_CURSOR.md](docs/MCP_SERVER_FOR_CURSOR.md) | Cursor MCP config, tools |
60175
| [docs/MCP_INTERACTION_GUIDE.md](docs/MCP_INTERACTION_GUIDE.md) | LSP vs MCP, indexing, troubleshooting |
61-
| [docs/PLAN_INDEPENDENT_LSP.md](docs/PLAN_INDEPENDENT_LSP.md) | Why LSP is in `lsp/` only for sysmledgraph |
62-
| [lsp/README.md](lsp/README.md) | LSP folder setup and cwd |
176+
| [docs/PLAN.md](docs/PLAN.md) | Roadmap and status |
177+
| [docs/PLAN_IMPLEMENT_LONG_LIVED_WORKER.md](docs/PLAN_IMPLEMENT_LONG_LIVED_WORKER.md) | Worker implementation plan |
178+
| [docs/DESIGN_LONG_LIVED_WORKER.md](docs/DESIGN_LONG_LIVED_WORKER.md) | Worker design, errors, exit semantics |
179+
| [docs/PLAN_INDEPENDENT_LSP.md](docs/PLAN_INDEPENDENT_LSP.md) | Why **`lsp/`** exists |
180+
| [docs/TOOLS.md](docs/TOOLS.md) | Tool-oriented notes |
181+
| [lsp/README.md](lsp/README.md) | LSP folder layout |
182+
183+
## Continuous integration
184+
185+
[`.github/workflows/ci.yml`](.github/workflows/ci.yml) runs **`npm ci`**, **`npm run build`**, **`npm test`**, and **`npm run test:daemon`** on **windows-latest** (Node 20).
63186

64187
## Publishing (npm)
65188

66-
You can publish this package to npm so others can install it with `npm install sysmledgraph` or `npx sysmledgraph analyze <path>`.
189+
Others can install with **`npm install sysmledgraph`** or run **`npx sysmledgraph analyze <path>`**.
67190

68-
1. **Name:** The package name is **`sysmledgraph`**. If it is already taken, use a scoped name (e.g. `@yourusername/sysmledgraph`) and set it in `package.json` `"name"`.
69-
2. **Build:** `prepublishOnly` runs `npm run build` before packing, so **dist/** is included in the tarball.
70-
3. **Included files:** Only **dist**, **scripts**, **lsp**, **README.md**, and **docs** are published (see `package.json` `"files"`). Users run **`npm run setup-lsp`** after install to install the LSP in **lsp/**.
71-
4. **Publish:** From a clean build, run:
191+
1. **Name:** **`sysmledgraph`** in `package.json` (or a scoped name if needed).
192+
2. **Build:** **`prepublishOnly`** runs **`npm run build`** so **`dist/`** ships in the tarball.
193+
3. **Files:** **`package.json`** **`files`** lists **`dist`**, **`scripts`**, **`lsp`**, **`README.md`**, **`docs`**. Consumers run **`npm run setup-lsp`** inside the package for **`lsp/`**.
194+
4. **Publish:**
72195
```bash
73196
npm login
74197
npm publish
75198
```
76-
For a scoped package (e.g. `@user/sysmledgraph`), use `npm publish --access public` the first time.
199+
Scoped packages: **`npm publish --access public`** the first time.
77200

78-
After publish, users can install with `npm install sysmledgraph`, then run **`npm run setup-lsp`** from the installed package directory (e.g. `cd node_modules/sysmledgraph && npm run setup-lsp`) so the LSP is available. When using **npx sysmledgraph** from another project, the default LSP path is resolved from the **current working directory** (that project’s `lsp/` or `node_modules/sysml-v2-lsp`). So either run setup-lsp inside the sysmledgraph package and set **SYSMLLSP_SERVER_PATH** to that `lsp/node_modules/.../server.js`, or install **sysml-v2-lsp** in the consumer project so the fallback finds it.
201+
**Consumer LSP resolution:** **`npx sysmledgraph`** from another project resolves LSP paths from **that project’s cwd** (`lsp/` or **`node_modules/sysml-v2-lsp`**). Either run **setup-lsp** inside the installed package and set **`SYSMLLSP_SERVER_PATH`**, or add **sysml-v2-lsp** to the consumer project.
79202

80203
## License
81204

bin/cli.ts

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
#!/usr/bin/env node
22
/**
3-
* CLI entrypoint: analyze, list, clean.
3+
* CLI entrypoint: analyze, list, clean, worker, graph.
44
*/
55

6+
import { resolve } from 'path';
67
import { program } from 'commander';
78
import {
89
configureStorageRoot,
910
cmdAnalyze,
1011
cmdList,
1112
cmdClean,
1213
} from '../src/cli/commands.js';
14+
import { cmdWorkerStart, cmdWorkerStop, cmdWorkerStatus } from '../src/cli/worker-commands.js';
15+
import { runExportGraphJson, runGenerateGraphMap } from '../src/cli/graph-artifacts.js';
16+
17+
function applyGlobalStorage(): void {
18+
const opts = typeof program.optsWithGlobals === 'function' ? program.optsWithGlobals() : program.opts();
19+
configureStorageRoot((opts as { storage?: string }).storage);
20+
}
1321

1422
program
1523
.name('sysmledgraph')
@@ -53,6 +61,93 @@ program
5361
}
5462
});
5563

64+
const workerCmd = program.command('worker').description('Long-lived graph worker (TCP; single Kuzu process)');
65+
66+
workerCmd
67+
.command('start')
68+
.description('Start worker (foreground). Use --detach to run in background.')
69+
.option('--detach', 'Run daemon detached')
70+
.action(async (opts: { detach?: boolean }) => {
71+
applyGlobalStorage();
72+
const r = await cmdWorkerStart(opts.detach === true);
73+
if (!r.ok) {
74+
process.stderr.write((r.error ?? 'Failed') + '\n');
75+
process.exit(r.exitCode ?? 1);
76+
}
77+
if (opts.detach) {
78+
process.stderr.write('Worker started in background.\n');
79+
}
80+
});
81+
82+
workerCmd
83+
.command('stop')
84+
.description('Stop worker (graceful shutdown + remove worker.port)')
85+
.action(async () => {
86+
applyGlobalStorage();
87+
const r = await cmdWorkerStop();
88+
if (!r.ok) {
89+
process.stderr.write((r.error ?? 'Failed') + '\n');
90+
process.exit(r.exitCode ?? 1);
91+
}
92+
process.stderr.write('Worker stopped.\n');
93+
});
94+
95+
workerCmd
96+
.command('status')
97+
.description('Print whether worker.port responds on TCP')
98+
.action(async () => {
99+
applyGlobalStorage();
100+
const r = await cmdWorkerStatus();
101+
if (!r.ok) {
102+
process.stderr.write((r.error ?? 'Failed') + '\n');
103+
process.exit(1);
104+
}
105+
if (r.running) {
106+
console.log(`running 127.0.0.1:${r.port}`);
107+
process.exit(0);
108+
}
109+
if (r.stalePortFile) {
110+
process.stderr.write(
111+
`not running (stale worker.port: TCP closed on port ${r.port ?? '?'}; run worker stop or delete worker.port)\n`
112+
);
113+
} else {
114+
process.stderr.write('not running\n');
115+
}
116+
process.exit(1);
117+
});
118+
119+
const graphCmd = program.command('graph').description('Export / map graph (uses long-lived worker when configured)');
120+
121+
graphCmd
122+
.command('export [file]')
123+
.description('Export nodes and edges to JSON (default: graph-export.json)')
124+
.action(async (file?: string) => {
125+
applyGlobalStorage();
126+
const out = resolve(process.cwd(), file || 'graph-export.json');
127+
const r = await runExportGraphJson(out);
128+
if (!r.ok) {
129+
process.stderr.write((r.error ?? 'Export failed') + '\n');
130+
process.exit(1);
131+
}
132+
process.stderr.write(
133+
`Wrote ${r.nodesCount ?? 0} nodes, ${r.edgeCount ?? 0} edges to ${out}\nOpen viewer/view.html in a browser to view.\n`
134+
);
135+
});
136+
137+
graphCmd
138+
.command('map [file]')
139+
.description('Write Markdown interconnection map (default: graph-map.md)')
140+
.action(async (file?: string) => {
141+
applyGlobalStorage();
142+
const out = resolve(process.cwd(), file || 'graph-map.md');
143+
const r = await runGenerateGraphMap(out);
144+
if (!r.ok) {
145+
process.stderr.write((r.error ?? 'Map failed') + '\n');
146+
process.exit(1);
147+
}
148+
process.stderr.write(`Wrote ${out}\n`);
149+
});
150+
56151
program
57152
.command('clean [path]')
58153
.description('Remove index for path or all')

0 commit comments

Comments
 (0)