@whenlabs/when 0.9.1 → 0.9.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/README.md +6 -2
- package/dist/chunk-2A2EZZF4.js +19 -0
- package/dist/index.js +30 -32
- package/dist/install-V24JHOA2.js +183 -0
- package/dist/mcp.js +630 -597
- package/package.json +2 -1
- package/templates/statusline.py +311 -0
- package/dist/install-TFEGFWJ5.js +0 -447
package/dist/mcp.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
findBin
|
|
4
|
+
} from "./chunk-2A2EZZF4.js";
|
|
2
5
|
|
|
3
|
-
// src/mcp.ts
|
|
6
|
+
// src/mcp/index.ts
|
|
4
7
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
8
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { resolve, dirname, join } from "path";
|
|
9
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
9
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
10
|
+
import { resolve, dirname } from "path";
|
|
10
11
|
import { fileURLToPath } from "url";
|
|
11
|
-
import { homedir } from "os";
|
|
12
12
|
import {
|
|
13
13
|
initDb,
|
|
14
14
|
TaskQueries,
|
|
@@ -18,15 +18,15 @@ import {
|
|
|
18
18
|
registerStats,
|
|
19
19
|
registerHistory
|
|
20
20
|
} from "@whenlabs/velocity-mcp/lib";
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
21
|
+
|
|
22
|
+
// src/mcp/stale.ts
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
|
|
25
|
+
// src/mcp/run-cli.ts
|
|
26
|
+
import { spawn } from "child_process";
|
|
27
|
+
import { join } from "path";
|
|
28
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
29
|
+
import { homedir } from "os";
|
|
30
30
|
function runCli(bin, args, cwd) {
|
|
31
31
|
return new Promise((res) => {
|
|
32
32
|
const child = spawn(findBin(bin), args, {
|
|
@@ -64,6 +64,12 @@ function readAwareProjectName(path) {
|
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
function formatOutput(result) {
|
|
68
|
+
const parts = [];
|
|
69
|
+
if (result.stdout.trim()) parts.push(result.stdout.trim());
|
|
70
|
+
if (result.stderr.trim()) parts.push(result.stderr.trim());
|
|
71
|
+
return parts.join("\n") || "No output";
|
|
72
|
+
}
|
|
67
73
|
async function checkTriggers(toolName, result, path) {
|
|
68
74
|
const output = result.stdout || result.stderr || "";
|
|
69
75
|
const extras = [];
|
|
@@ -98,9 +104,612 @@ Note: Conflicts found in project "${projectName}".`);
|
|
|
98
104
|
}
|
|
99
105
|
return extras;
|
|
100
106
|
}
|
|
107
|
+
|
|
108
|
+
// src/mcp/stale.ts
|
|
109
|
+
function registerStaleTools(server2) {
|
|
110
|
+
server2.tool(
|
|
111
|
+
"stale_scan",
|
|
112
|
+
"Scan for documentation drift \u2014 detect when docs say one thing and code says another",
|
|
113
|
+
{
|
|
114
|
+
path: z.string().optional().describe("Project directory to scan (defaults to cwd)"),
|
|
115
|
+
deep: z.coerce.boolean().optional().describe("Enable AI-powered deep analysis"),
|
|
116
|
+
git: z.coerce.boolean().optional().describe("Enable git history staleness checks"),
|
|
117
|
+
format: z.enum(["terminal", "json", "markdown", "sarif"]).optional().describe("Output format")
|
|
118
|
+
},
|
|
119
|
+
async ({ path, deep, git, format }) => {
|
|
120
|
+
const args = ["scan"];
|
|
121
|
+
if (deep) args.push("--deep");
|
|
122
|
+
if (git) args.push("--git");
|
|
123
|
+
if (format) args.push("--format", format);
|
|
124
|
+
const result = await runCli("stale", args, path);
|
|
125
|
+
const output = formatOutput(result);
|
|
126
|
+
writeCache("stale", deriveProject(path), output, result.code);
|
|
127
|
+
const extras = await checkTriggers("stale_scan", result, path);
|
|
128
|
+
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
server2.tool(
|
|
132
|
+
"stale_fix",
|
|
133
|
+
"Auto-fix documentation drift \u2014 generate fixes for wrong file paths, dead links, phantom env vars, outdated scripts",
|
|
134
|
+
{
|
|
135
|
+
path: z.string().optional().describe("Project directory to scan (defaults to cwd)"),
|
|
136
|
+
format: z.enum(["terminal", "diff"]).optional().describe("Output format (default: terminal)"),
|
|
137
|
+
apply: z.coerce.boolean().optional().describe("Apply high-confidence fixes directly"),
|
|
138
|
+
dryRun: z.coerce.boolean().optional().describe("Show what --apply would do without writing")
|
|
139
|
+
},
|
|
140
|
+
async ({ path, format, apply, dryRun }) => {
|
|
141
|
+
const args = ["fix"];
|
|
142
|
+
if (format) args.push("--format", format);
|
|
143
|
+
if (apply) args.push("--apply");
|
|
144
|
+
if (dryRun) args.push("--dry-run");
|
|
145
|
+
const result = await runCli("stale", args, path);
|
|
146
|
+
const output = formatOutput(result);
|
|
147
|
+
return { content: [{ type: "text", text: output }] };
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
server2.tool(
|
|
151
|
+
"stale_init",
|
|
152
|
+
"Generate a .stale.yml config file for customizing documentation drift detection",
|
|
153
|
+
{ path: z.string().optional().describe("Project directory (defaults to cwd)") },
|
|
154
|
+
async ({ path }) => {
|
|
155
|
+
const result = await runCli("stale", ["init"], path);
|
|
156
|
+
const output = formatOutput(result);
|
|
157
|
+
return { content: [{ type: "text", text: output }] };
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/mcp/envalid.ts
|
|
163
|
+
import { z as z2 } from "zod";
|
|
164
|
+
function registerEnvalidTools(server2) {
|
|
165
|
+
server2.tool(
|
|
166
|
+
"envalid_validate",
|
|
167
|
+
"Validate .env files against their schema \u2014 catch missing or invalid environment variables",
|
|
168
|
+
{
|
|
169
|
+
path: z2.string().optional().describe("Project directory (defaults to cwd)"),
|
|
170
|
+
environment: z2.string().optional().describe("Target environment (e.g. production, staging)"),
|
|
171
|
+
format: z2.enum(["terminal", "json", "markdown"]).optional().describe("Output format")
|
|
172
|
+
},
|
|
173
|
+
async ({ path, environment, format }) => {
|
|
174
|
+
const args = ["validate"];
|
|
175
|
+
if (environment) args.push("--environment", environment);
|
|
176
|
+
if (format) args.push("--format", format);
|
|
177
|
+
const result = await runCli("envalid", args, path);
|
|
178
|
+
const output = formatOutput(result);
|
|
179
|
+
writeCache("envalid_validate", deriveProject(path), output, result.code);
|
|
180
|
+
const extras = await checkTriggers("envalid_validate", result, path);
|
|
181
|
+
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
server2.tool(
|
|
185
|
+
"envalid_detect",
|
|
186
|
+
"Scan codebase for env var usage and compare with schema \u2014 find undocumented env vars",
|
|
187
|
+
{
|
|
188
|
+
path: z2.string().optional().describe("Project directory (defaults to cwd)"),
|
|
189
|
+
format: z2.enum(["terminal", "json"]).optional().describe("Output format")
|
|
190
|
+
},
|
|
191
|
+
async ({ path, format }) => {
|
|
192
|
+
const args = ["detect"];
|
|
193
|
+
if (format) args.push("--format", format);
|
|
194
|
+
const result = await runCli("envalid", args, path);
|
|
195
|
+
const output = formatOutput(result);
|
|
196
|
+
writeCache("envalid_detect", deriveProject(path), output, result.code);
|
|
197
|
+
const extras = await checkTriggers("envalid_detect", result, path);
|
|
198
|
+
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
server2.tool(
|
|
202
|
+
"envalid_init",
|
|
203
|
+
"Generate .env.schema from an existing .env file \u2014 bootstrap type-safe env validation",
|
|
204
|
+
{
|
|
205
|
+
path: z2.string().optional().describe("Project directory (defaults to cwd)"),
|
|
206
|
+
force: z2.coerce.boolean().optional().describe("Overwrite existing schema")
|
|
207
|
+
},
|
|
208
|
+
async ({ path, force }) => {
|
|
209
|
+
const args = ["init"];
|
|
210
|
+
if (force) args.push("--force");
|
|
211
|
+
const result = await runCli("envalid", args, path);
|
|
212
|
+
const output = formatOutput(result);
|
|
213
|
+
return { content: [{ type: "text", text: output }] };
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
server2.tool(
|
|
217
|
+
"envalid_diff",
|
|
218
|
+
"Compare two .env files \u2014 show added, removed, and changed variables",
|
|
219
|
+
{
|
|
220
|
+
source: z2.string().describe("Path to source .env file"),
|
|
221
|
+
target: z2.string().describe("Path to target .env file"),
|
|
222
|
+
schema: z2.string().optional().describe("Path to .env.schema for sensitivity info"),
|
|
223
|
+
format: z2.enum(["terminal", "json", "markdown"]).optional().describe("Output format")
|
|
224
|
+
},
|
|
225
|
+
async ({ source, target, schema, format }) => {
|
|
226
|
+
const args = ["diff", source, target];
|
|
227
|
+
if (schema) args.push("--schema", schema);
|
|
228
|
+
if (format) args.push("--format", format);
|
|
229
|
+
const result = await runCli("envalid", args);
|
|
230
|
+
const output = formatOutput(result);
|
|
231
|
+
return { content: [{ type: "text", text: output }] };
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
server2.tool(
|
|
235
|
+
"envalid_sync",
|
|
236
|
+
"Check multiple environment files against schema \u2014 ensure all envs are in sync",
|
|
237
|
+
{
|
|
238
|
+
environments: z2.string().describe('Comma-separated env file paths (e.g. ".env,.env.staging,.env.production")'),
|
|
239
|
+
path: z2.string().optional().describe("Project directory (defaults to cwd)"),
|
|
240
|
+
format: z2.enum(["terminal", "json", "markdown"]).optional().describe("Output format")
|
|
241
|
+
},
|
|
242
|
+
async ({ environments, path, format }) => {
|
|
243
|
+
const args = ["sync", "--environments", environments];
|
|
244
|
+
if (format) args.push("--format", format);
|
|
245
|
+
const result = await runCli("envalid", args, path);
|
|
246
|
+
const output = formatOutput(result);
|
|
247
|
+
return { content: [{ type: "text", text: output }] };
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
server2.tool(
|
|
251
|
+
"envalid_generate",
|
|
252
|
+
"Generate .env.example from schema \u2014 create a safe template without secrets",
|
|
253
|
+
{
|
|
254
|
+
path: z2.string().optional().describe("Project directory (defaults to cwd)"),
|
|
255
|
+
output: z2.string().optional().describe("Output file path (default: .env.example)")
|
|
256
|
+
},
|
|
257
|
+
async ({ path, output }) => {
|
|
258
|
+
const args = ["generate-example"];
|
|
259
|
+
if (output) args.push("--output", output);
|
|
260
|
+
const result = await runCli("envalid", args, path);
|
|
261
|
+
const outputText = formatOutput(result);
|
|
262
|
+
return { content: [{ type: "text", text: outputText }] };
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
server2.tool(
|
|
266
|
+
"envalid_secrets",
|
|
267
|
+
"Scan committed files for leaked secrets \u2014 detect API keys, tokens, passwords in code",
|
|
268
|
+
{
|
|
269
|
+
path: z2.string().optional().describe("Project directory (defaults to cwd)"),
|
|
270
|
+
format: z2.enum(["terminal", "json"]).optional().describe("Output format")
|
|
271
|
+
},
|
|
272
|
+
async ({ path, format }) => {
|
|
273
|
+
const args = ["secrets"];
|
|
274
|
+
if (format) args.push("--format", format);
|
|
275
|
+
const result = await runCli("envalid", args, path);
|
|
276
|
+
const output = formatOutput(result);
|
|
277
|
+
writeCache("envalid_secrets", deriveProject(path), output, result.code);
|
|
278
|
+
return { content: [{ type: "text", text: output }] };
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
server2.tool(
|
|
282
|
+
"envalid_generate_schema",
|
|
283
|
+
"Generate .env.schema from code analysis \u2014 infer types, required-ness, and sensitivity from usage patterns",
|
|
284
|
+
{
|
|
285
|
+
path: z2.string().optional().describe("Project directory (defaults to cwd)"),
|
|
286
|
+
output: z2.string().optional().describe("Output file path (default: .env.schema)")
|
|
287
|
+
},
|
|
288
|
+
async ({ path, output }) => {
|
|
289
|
+
const args = ["detect", "--generate"];
|
|
290
|
+
if (output) args.push("-o", output);
|
|
291
|
+
const result = await runCli("envalid", args, path);
|
|
292
|
+
const outputText = formatOutput(result);
|
|
293
|
+
return { content: [{ type: "text", text: outputText }] };
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
server2.tool(
|
|
297
|
+
"envalid_hook_status",
|
|
298
|
+
"Check if the envalid pre-commit git hook is installed",
|
|
299
|
+
{ path: z2.string().optional().describe("Project directory (defaults to cwd)") },
|
|
300
|
+
async ({ path }) => {
|
|
301
|
+
const result = await runCli("envalid", ["hook", "status"], path);
|
|
302
|
+
const output = formatOutput(result);
|
|
303
|
+
return { content: [{ type: "text", text: output }] };
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/mcp/berth.ts
|
|
309
|
+
import { z as z3 } from "zod";
|
|
310
|
+
function registerBerthTools(server2) {
|
|
311
|
+
server2.tool(
|
|
312
|
+
"berth_status",
|
|
313
|
+
"Show all active ports, Docker ports, and configured ports \u2014 diagnose port conflicts",
|
|
314
|
+
{},
|
|
315
|
+
async () => {
|
|
316
|
+
const result = await runCli("berth", ["status", "--json"]);
|
|
317
|
+
const output = formatOutput(result);
|
|
318
|
+
writeCache("berth_status", deriveProject(), output, result.code);
|
|
319
|
+
const extras = await checkTriggers("berth_status", result);
|
|
320
|
+
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
server2.tool(
|
|
324
|
+
"berth_check",
|
|
325
|
+
"Scan a project directory for port conflicts before starting dev servers",
|
|
326
|
+
{ path: z3.string().optional().describe("Project directory to check (defaults to cwd)") },
|
|
327
|
+
async ({ path }) => {
|
|
328
|
+
const result = await runCli("berth", ["check", path || "."]);
|
|
329
|
+
const output = formatOutput(result);
|
|
330
|
+
writeCache("berth_check", deriveProject(path), output, result.code);
|
|
331
|
+
const extras = await checkTriggers("berth_check", result, path);
|
|
332
|
+
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
server2.tool(
|
|
336
|
+
"berth_kill",
|
|
337
|
+
"Kill processes on a specific port \u2014 free up a port for your dev server",
|
|
338
|
+
{
|
|
339
|
+
port: z3.coerce.number().optional().describe("Port number to free"),
|
|
340
|
+
dev: z3.coerce.boolean().optional().describe("Kill all dev processes (node, python, ruby, etc.)")
|
|
341
|
+
},
|
|
342
|
+
async ({ port, dev }) => {
|
|
343
|
+
const args = ["kill"];
|
|
344
|
+
if (port) args.push(String(port));
|
|
345
|
+
if (dev) args.push("--dev");
|
|
346
|
+
args.push("--force");
|
|
347
|
+
const result = await runCli("berth", args);
|
|
348
|
+
const output = formatOutput(result);
|
|
349
|
+
return { content: [{ type: "text", text: output }] };
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
server2.tool(
|
|
353
|
+
"berth_free",
|
|
354
|
+
"Free all ports for a registered project \u2014 kill every process blocking the project",
|
|
355
|
+
{ project: z3.string().describe("Registered project name") },
|
|
356
|
+
async ({ project }) => {
|
|
357
|
+
const result = await runCli("berth", ["free", project]);
|
|
358
|
+
const output = formatOutput(result);
|
|
359
|
+
return { content: [{ type: "text", text: output }] };
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
server2.tool(
|
|
363
|
+
"berth_register",
|
|
364
|
+
"Register a project directory's port requirements for conflict tracking",
|
|
365
|
+
{
|
|
366
|
+
path: z3.string().optional().describe("Project directory to register (defaults to cwd)")
|
|
367
|
+
},
|
|
368
|
+
async ({ path }) => {
|
|
369
|
+
const args = ["register", "--yes"];
|
|
370
|
+
if (path) args.push("--dir", path);
|
|
371
|
+
const result = await runCli("berth", args);
|
|
372
|
+
const output = formatOutput(result);
|
|
373
|
+
return { content: [{ type: "text", text: output }] };
|
|
374
|
+
}
|
|
375
|
+
);
|
|
376
|
+
server2.tool(
|
|
377
|
+
"berth_list",
|
|
378
|
+
"List all registered projects and their port statuses",
|
|
379
|
+
{},
|
|
380
|
+
async () => {
|
|
381
|
+
const result = await runCli("berth", ["list", "--json"]);
|
|
382
|
+
const output = formatOutput(result);
|
|
383
|
+
return { content: [{ type: "text", text: output }] };
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
server2.tool(
|
|
387
|
+
"berth_reassign",
|
|
388
|
+
"Change a port assignment in project config files (docker-compose, .env, etc.)",
|
|
389
|
+
{
|
|
390
|
+
oldPort: z3.number().describe("Current port number"),
|
|
391
|
+
newPort: z3.number().describe("New port number"),
|
|
392
|
+
project: z3.string().optional().describe("Project name from registry")
|
|
393
|
+
},
|
|
394
|
+
async ({ oldPort, newPort, project }) => {
|
|
395
|
+
const args = ["reassign", String(oldPort), String(newPort)];
|
|
396
|
+
if (project) args.push("--project", project);
|
|
397
|
+
const result = await runCli("berth", args);
|
|
398
|
+
const output = formatOutput(result);
|
|
399
|
+
return { content: [{ type: "text", text: output }] };
|
|
400
|
+
}
|
|
401
|
+
);
|
|
402
|
+
server2.tool(
|
|
403
|
+
"berth_start",
|
|
404
|
+
"Auto-resolve all port conflicts and prepare a project to start cleanly",
|
|
405
|
+
{
|
|
406
|
+
project: z3.string().describe("Registered project name"),
|
|
407
|
+
dryRun: z3.coerce.boolean().optional().describe("Show what would be done without making changes")
|
|
408
|
+
},
|
|
409
|
+
async ({ project, dryRun }) => {
|
|
410
|
+
const args = ["start", project];
|
|
411
|
+
if (dryRun) args.push("--dry-run");
|
|
412
|
+
const result = await runCli("berth", args);
|
|
413
|
+
const output = formatOutput(result);
|
|
414
|
+
return { content: [{ type: "text", text: output }] };
|
|
415
|
+
}
|
|
416
|
+
);
|
|
417
|
+
server2.tool(
|
|
418
|
+
"berth_resolve",
|
|
419
|
+
"Auto-resolve port conflicts \u2014 detect conflicts and fix via kill or reassign strategy",
|
|
420
|
+
{
|
|
421
|
+
path: z3.string().optional().describe("Project directory (defaults to cwd)"),
|
|
422
|
+
strategy: z3.enum(["kill", "reassign", "auto"]).optional().describe("Resolution strategy (default: auto)"),
|
|
423
|
+
kill: z3.coerce.boolean().optional().describe("Allow killing processes (required for kill/auto strategies)"),
|
|
424
|
+
dryRun: z3.coerce.boolean().optional().describe("Show what would be done without making changes")
|
|
425
|
+
},
|
|
426
|
+
async ({ path, strategy, kill, dryRun }) => {
|
|
427
|
+
const args = ["resolve"];
|
|
428
|
+
if (strategy) args.push("--strategy", strategy);
|
|
429
|
+
if (kill) args.push("--kill");
|
|
430
|
+
if (dryRun) args.push("--dry-run");
|
|
431
|
+
const result = await runCli("berth", args, path);
|
|
432
|
+
const output = formatOutput(result);
|
|
433
|
+
return { content: [{ type: "text", text: output }] };
|
|
434
|
+
}
|
|
435
|
+
);
|
|
436
|
+
server2.tool(
|
|
437
|
+
"berth_predict",
|
|
438
|
+
"Predict port conflicts from project config files before starting \u2014 dry-run conflict check",
|
|
439
|
+
{ path: z3.string().optional().describe("Project directory (defaults to cwd)") },
|
|
440
|
+
async ({ path }) => {
|
|
441
|
+
const result = await runCli("berth", ["predict", path || "."]);
|
|
442
|
+
const output = formatOutput(result);
|
|
443
|
+
return { content: [{ type: "text", text: output }] };
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// src/mcp/aware.ts
|
|
449
|
+
import { z as z4 } from "zod";
|
|
450
|
+
function registerAwareTools(server2) {
|
|
451
|
+
server2.tool(
|
|
452
|
+
"aware_init",
|
|
453
|
+
"Auto-detect project stack and generate AI context files (CLAUDE.md, .cursorrules, etc.)",
|
|
454
|
+
{
|
|
455
|
+
path: z4.string().optional().describe("Project directory (defaults to cwd)"),
|
|
456
|
+
targets: z4.string().optional().describe("Comma-separated targets: claude,cursor,copilot,agents,all"),
|
|
457
|
+
force: z4.coerce.boolean().optional().describe("Overwrite existing files without prompting")
|
|
458
|
+
},
|
|
459
|
+
async ({ path, targets, force }) => {
|
|
460
|
+
const args = ["init"];
|
|
461
|
+
if (targets) args.push("--targets", targets);
|
|
462
|
+
if (force) args.push("--force");
|
|
463
|
+
const result = await runCli("aware", args, path);
|
|
464
|
+
const output = formatOutput(result);
|
|
465
|
+
writeCache("aware_init", deriveProject(path), output, result.code);
|
|
466
|
+
const extras = await checkTriggers("aware_init", result, path);
|
|
467
|
+
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
468
|
+
}
|
|
469
|
+
);
|
|
470
|
+
server2.tool(
|
|
471
|
+
"aware_sync",
|
|
472
|
+
"Regenerate AI context files from .aware.json \u2014 update CLAUDE.md, .cursorrules, etc.",
|
|
473
|
+
{
|
|
474
|
+
path: z4.string().optional().describe("Project directory (defaults to cwd)"),
|
|
475
|
+
dryRun: z4.coerce.boolean().optional().describe("Show what would change without writing files")
|
|
476
|
+
},
|
|
477
|
+
async ({ path, dryRun }) => {
|
|
478
|
+
const args = ["sync"];
|
|
479
|
+
if (dryRun) args.push("--dry-run");
|
|
480
|
+
const result = await runCli("aware", args, path);
|
|
481
|
+
const output = formatOutput(result);
|
|
482
|
+
writeCache("aware_sync", deriveProject(path), output, result.code);
|
|
483
|
+
return { content: [{ type: "text", text: output }] };
|
|
484
|
+
}
|
|
485
|
+
);
|
|
486
|
+
server2.tool(
|
|
487
|
+
"aware_diff",
|
|
488
|
+
"Show project changes since last sync \u2014 see what drifted in your codebase",
|
|
489
|
+
{
|
|
490
|
+
path: z4.string().optional().describe("Project directory (defaults to cwd)"),
|
|
491
|
+
exitCode: z4.coerce.boolean().optional().describe("Return exit code 1 if changes detected (useful for CI)")
|
|
492
|
+
},
|
|
493
|
+
async ({ path, exitCode }) => {
|
|
494
|
+
const args = ["diff"];
|
|
495
|
+
if (exitCode) args.push("--exit-code");
|
|
496
|
+
const result = await runCli("aware", args, path);
|
|
497
|
+
const output = formatOutput(result);
|
|
498
|
+
return { content: [{ type: "text", text: output }] };
|
|
499
|
+
}
|
|
500
|
+
);
|
|
501
|
+
server2.tool(
|
|
502
|
+
"aware_validate",
|
|
503
|
+
"Validate .aware.json schema and content \u2014 check for config errors",
|
|
504
|
+
{ path: z4.string().optional().describe("Project directory (defaults to cwd)") },
|
|
505
|
+
async ({ path }) => {
|
|
506
|
+
const result = await runCli("aware", ["validate"], path);
|
|
507
|
+
const output = formatOutput(result);
|
|
508
|
+
return { content: [{ type: "text", text: output }] };
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
server2.tool(
|
|
512
|
+
"aware_doctor",
|
|
513
|
+
"Diagnose project health \u2014 check config issues, stack drift, stale AI context files",
|
|
514
|
+
{ path: z4.string().optional().describe("Project directory (defaults to cwd)") },
|
|
515
|
+
async ({ path }) => {
|
|
516
|
+
const result = await runCli("aware", ["doctor"], path);
|
|
517
|
+
const output = formatOutput(result);
|
|
518
|
+
writeCache("aware_doctor", deriveProject(path), output, result.code);
|
|
519
|
+
const extras = await checkTriggers("aware_doctor", result, path);
|
|
520
|
+
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
server2.tool(
|
|
524
|
+
"aware_add",
|
|
525
|
+
"Add a rule, convention, or structure entry to .aware.json",
|
|
526
|
+
{
|
|
527
|
+
path: z4.string().optional().describe("Project directory (defaults to cwd)"),
|
|
528
|
+
type: z4.enum(["rule", "convention", "structure"]).describe("Type to add")
|
|
529
|
+
},
|
|
530
|
+
async ({ path, type }) => {
|
|
531
|
+
const args = ["add", "--type", type];
|
|
532
|
+
const result = await runCli("aware", args, path);
|
|
533
|
+
const output = formatOutput(result);
|
|
534
|
+
return { content: [{ type: "text", text: output }] };
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// src/mcp/vow.ts
|
|
540
|
+
import { z as z5 } from "zod";
|
|
541
|
+
function registerVowTools(server2) {
|
|
542
|
+
server2.tool(
|
|
543
|
+
"vow_scan",
|
|
544
|
+
"Scan dependency licenses \u2014 summarize all licenses in the project",
|
|
545
|
+
{
|
|
546
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)"),
|
|
547
|
+
production: z5.coerce.boolean().optional().describe("Skip devDependencies"),
|
|
548
|
+
format: z5.enum(["terminal", "json"]).optional().describe("Output format")
|
|
549
|
+
},
|
|
550
|
+
async ({ path, production, format }) => {
|
|
551
|
+
const args = ["scan"];
|
|
552
|
+
if (production) args.push("--production");
|
|
553
|
+
if (format) args.push("--format", format);
|
|
554
|
+
const result = await runCli("vow", args, path);
|
|
555
|
+
const output = formatOutput(result);
|
|
556
|
+
writeCache("vow_scan", deriveProject(path), output, result.code);
|
|
557
|
+
const extras = await checkTriggers("vow_scan", result, path);
|
|
558
|
+
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
559
|
+
}
|
|
560
|
+
);
|
|
561
|
+
server2.tool(
|
|
562
|
+
"vow_check",
|
|
563
|
+
"Validate dependency licenses against policy \u2014 flag violations before release",
|
|
564
|
+
{
|
|
565
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)"),
|
|
566
|
+
production: z5.coerce.boolean().optional().describe("Skip devDependencies")
|
|
567
|
+
},
|
|
568
|
+
async ({ path, production }) => {
|
|
569
|
+
const args = ["check"];
|
|
570
|
+
if (production) args.push("--production");
|
|
571
|
+
const result = await runCli("vow", args, path);
|
|
572
|
+
const output = formatOutput(result);
|
|
573
|
+
writeCache("vow_check", deriveProject(path), output, result.code);
|
|
574
|
+
const extras = await checkTriggers("vow_check", result, path);
|
|
575
|
+
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
576
|
+
}
|
|
577
|
+
);
|
|
578
|
+
server2.tool(
|
|
579
|
+
"vow_init",
|
|
580
|
+
"Generate a license policy file (.vow.json) \u2014 choose from commercial, opensource, or strict templates",
|
|
581
|
+
{
|
|
582
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)"),
|
|
583
|
+
template: z5.enum(["commercial", "opensource", "strict"]).optional().describe("Policy template")
|
|
584
|
+
},
|
|
585
|
+
async ({ path, template }) => {
|
|
586
|
+
const args = ["init"];
|
|
587
|
+
if (template) args.push("--template", template);
|
|
588
|
+
const result = await runCli("vow", args, path);
|
|
589
|
+
const output = formatOutput(result);
|
|
590
|
+
return { content: [{ type: "text", text: output }] };
|
|
591
|
+
}
|
|
592
|
+
);
|
|
593
|
+
server2.tool(
|
|
594
|
+
"vow_tree",
|
|
595
|
+
"Display dependency tree with license annotations \u2014 trace license inheritance",
|
|
596
|
+
{
|
|
597
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)"),
|
|
598
|
+
filter: z5.string().optional().describe('Show only subtrees containing this license (e.g. "GPL")'),
|
|
599
|
+
depth: z5.coerce.number().optional().describe("Max tree depth"),
|
|
600
|
+
production: z5.coerce.boolean().optional().describe("Skip devDependencies")
|
|
601
|
+
},
|
|
602
|
+
async ({ path, filter, depth, production }) => {
|
|
603
|
+
const args = ["tree"];
|
|
604
|
+
if (path) args.push("--path", path);
|
|
605
|
+
if (filter) args.push("--filter", filter);
|
|
606
|
+
if (depth) args.push("--depth", String(depth));
|
|
607
|
+
if (production) args.push("--production");
|
|
608
|
+
const result = await runCli("vow", args, path);
|
|
609
|
+
const output = formatOutput(result);
|
|
610
|
+
return { content: [{ type: "text", text: output }] };
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
server2.tool(
|
|
614
|
+
"vow_fix",
|
|
615
|
+
"Suggest alternative packages for license policy violations \u2014 find compliant replacements",
|
|
616
|
+
{
|
|
617
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)"),
|
|
618
|
+
production: z5.coerce.boolean().optional().describe("Skip devDependencies"),
|
|
619
|
+
limit: z5.coerce.number().optional().describe("Max alternatives per package")
|
|
620
|
+
},
|
|
621
|
+
async ({ path, production, limit }) => {
|
|
622
|
+
const args = ["fix"];
|
|
623
|
+
if (path) args.push("--path", path);
|
|
624
|
+
if (production) args.push("--production");
|
|
625
|
+
if (limit) args.push("--limit", String(limit));
|
|
626
|
+
const result = await runCli("vow", args, path);
|
|
627
|
+
const output = formatOutput(result);
|
|
628
|
+
return { content: [{ type: "text", text: output }] };
|
|
629
|
+
}
|
|
630
|
+
);
|
|
631
|
+
server2.tool(
|
|
632
|
+
"vow_export",
|
|
633
|
+
"Export full license report as JSON, CSV, or Markdown",
|
|
634
|
+
{
|
|
635
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)"),
|
|
636
|
+
format: z5.enum(["json", "csv", "markdown"]).optional().describe("Export format (default: json)"),
|
|
637
|
+
output: z5.string().optional().describe("Output file path"),
|
|
638
|
+
production: z5.coerce.boolean().optional().describe("Skip devDependencies")
|
|
639
|
+
},
|
|
640
|
+
async ({ path, format, output, production }) => {
|
|
641
|
+
const args = ["export"];
|
|
642
|
+
if (path) args.push("--path", path);
|
|
643
|
+
if (format) args.push("--format", format);
|
|
644
|
+
if (output) args.push("--output", output);
|
|
645
|
+
if (production) args.push("--production");
|
|
646
|
+
const result = await runCli("vow", args, path);
|
|
647
|
+
const outputText = formatOutput(result);
|
|
648
|
+
return { content: [{ type: "text", text: outputText }] };
|
|
649
|
+
}
|
|
650
|
+
);
|
|
651
|
+
server2.tool(
|
|
652
|
+
"vow_hook_install",
|
|
653
|
+
"Install a pre-commit git hook that checks dependency licenses before each commit",
|
|
654
|
+
{
|
|
655
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)")
|
|
656
|
+
},
|
|
657
|
+
async ({ path }) => {
|
|
658
|
+
const result = await runCli("vow", ["hook", "install"], path);
|
|
659
|
+
const output = formatOutput(result);
|
|
660
|
+
return { content: [{ type: "text", text: output }] };
|
|
661
|
+
}
|
|
662
|
+
);
|
|
663
|
+
server2.tool(
|
|
664
|
+
"vow_hook_uninstall",
|
|
665
|
+
"Remove the vow pre-commit license check hook",
|
|
666
|
+
{
|
|
667
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)")
|
|
668
|
+
},
|
|
669
|
+
async ({ path }) => {
|
|
670
|
+
const result = await runCli("vow", ["hook", "uninstall"], path);
|
|
671
|
+
const output = formatOutput(result);
|
|
672
|
+
return { content: [{ type: "text", text: output }] };
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
server2.tool(
|
|
676
|
+
"vow_hook_status",
|
|
677
|
+
"Check if the vow pre-commit license check hook is installed",
|
|
678
|
+
{
|
|
679
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)")
|
|
680
|
+
},
|
|
681
|
+
async ({ path }) => {
|
|
682
|
+
const result = await runCli("vow", ["hook", "status"], path);
|
|
683
|
+
const output = formatOutput(result);
|
|
684
|
+
return { content: [{ type: "text", text: output }] };
|
|
685
|
+
}
|
|
686
|
+
);
|
|
687
|
+
server2.tool(
|
|
688
|
+
"vow_attribution",
|
|
689
|
+
"Generate THIRD_PARTY_LICENSES.md \u2014 list all dependencies with their licenses for compliance",
|
|
690
|
+
{
|
|
691
|
+
path: z5.string().optional().describe("Project directory (defaults to cwd)"),
|
|
692
|
+
output: z5.string().optional().describe("Output file (default: THIRD_PARTY_LICENSES.md)"),
|
|
693
|
+
production: z5.coerce.boolean().optional().describe("Skip devDependencies")
|
|
694
|
+
},
|
|
695
|
+
async ({ path, output, production }) => {
|
|
696
|
+
const args = ["attribution"];
|
|
697
|
+
if (path) args.push("--path", path);
|
|
698
|
+
if (output) args.push("--output", output);
|
|
699
|
+
if (production) args.push("--production");
|
|
700
|
+
const result = await runCli("vow", args, path);
|
|
701
|
+
const outputText = formatOutput(result);
|
|
702
|
+
return { content: [{ type: "text", text: outputText }] };
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/mcp/index.ts
|
|
708
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
709
|
+
var { version } = JSON.parse(readFileSync2(resolve(__dirname, "..", "package.json"), "utf8"));
|
|
101
710
|
var server = new McpServer({
|
|
102
711
|
name: "whenlabs",
|
|
103
|
-
version
|
|
712
|
+
version
|
|
104
713
|
});
|
|
105
714
|
var velocityDb = initDb();
|
|
106
715
|
var velocityQueries = new TaskQueries(velocityDb);
|
|
@@ -110,587 +719,11 @@ registerEndTask(s, velocityQueries);
|
|
|
110
719
|
registerEstimate(s, velocityQueries);
|
|
111
720
|
registerStats(s, velocityQueries);
|
|
112
721
|
registerHistory(s, velocityQueries);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
server.tool(
|
|
120
|
-
"stale_scan",
|
|
121
|
-
"Scan for documentation drift \u2014 detect when docs say one thing and code says another",
|
|
122
|
-
{
|
|
123
|
-
path: z.string().optional().describe("Project directory to scan (defaults to cwd)"),
|
|
124
|
-
deep: z.coerce.boolean().optional().describe("Enable AI-powered deep analysis"),
|
|
125
|
-
git: z.coerce.boolean().optional().describe("Enable git history staleness checks"),
|
|
126
|
-
format: z.enum(["terminal", "json", "markdown", "sarif"]).optional().describe("Output format")
|
|
127
|
-
},
|
|
128
|
-
async ({ path, deep, git, format }) => {
|
|
129
|
-
const args = ["scan"];
|
|
130
|
-
if (deep) args.push("--deep");
|
|
131
|
-
if (git) args.push("--git");
|
|
132
|
-
if (format) args.push("--format", format);
|
|
133
|
-
const result = await runCli("stale", args, path);
|
|
134
|
-
const output = formatOutput(result);
|
|
135
|
-
writeCache("stale", deriveProject(path), output, result.code);
|
|
136
|
-
const extras = await checkTriggers("stale_scan", result, path);
|
|
137
|
-
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
138
|
-
}
|
|
139
|
-
);
|
|
140
|
-
server.tool(
|
|
141
|
-
"stale_fix",
|
|
142
|
-
"Auto-fix documentation drift \u2014 generate fixes for wrong file paths, dead links, phantom env vars, outdated scripts",
|
|
143
|
-
{
|
|
144
|
-
path: z.string().optional().describe("Project directory to scan (defaults to cwd)"),
|
|
145
|
-
format: z.enum(["terminal", "diff"]).optional().describe("Output format (default: terminal)"),
|
|
146
|
-
apply: z.coerce.boolean().optional().describe("Apply high-confidence fixes directly"),
|
|
147
|
-
dryRun: z.coerce.boolean().optional().describe("Show what --apply would do without writing")
|
|
148
|
-
},
|
|
149
|
-
async ({ path, format, apply, dryRun }) => {
|
|
150
|
-
const args = ["fix"];
|
|
151
|
-
if (format) args.push("--format", format);
|
|
152
|
-
if (apply) args.push("--apply");
|
|
153
|
-
if (dryRun) args.push("--dry-run");
|
|
154
|
-
const result = await runCli("stale", args, path);
|
|
155
|
-
const output = formatOutput(result);
|
|
156
|
-
return { content: [{ type: "text", text: output }] };
|
|
157
|
-
}
|
|
158
|
-
);
|
|
159
|
-
server.tool(
|
|
160
|
-
"stale_init",
|
|
161
|
-
"Generate a .stale.yml config file for customizing documentation drift detection",
|
|
162
|
-
{ path: z.string().optional().describe("Project directory (defaults to cwd)") },
|
|
163
|
-
async ({ path }) => {
|
|
164
|
-
const result = await runCli("stale", ["init"], path);
|
|
165
|
-
const output = formatOutput(result);
|
|
166
|
-
return { content: [{ type: "text", text: output }] };
|
|
167
|
-
}
|
|
168
|
-
);
|
|
169
|
-
server.tool(
|
|
170
|
-
"envalid_validate",
|
|
171
|
-
"Validate .env files against their schema \u2014 catch missing or invalid environment variables",
|
|
172
|
-
{
|
|
173
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
174
|
-
environment: z.string().optional().describe("Target environment (e.g. production, staging)"),
|
|
175
|
-
format: z.enum(["terminal", "json", "markdown"]).optional().describe("Output format")
|
|
176
|
-
},
|
|
177
|
-
async ({ path, environment, format }) => {
|
|
178
|
-
const args = ["validate"];
|
|
179
|
-
if (environment) args.push("--environment", environment);
|
|
180
|
-
if (format) args.push("--format", format);
|
|
181
|
-
const result = await runCli("envalid", args, path);
|
|
182
|
-
const output = formatOutput(result);
|
|
183
|
-
writeCache("envalid_validate", deriveProject(path), output, result.code);
|
|
184
|
-
const extras = await checkTriggers("envalid_validate", result, path);
|
|
185
|
-
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
186
|
-
}
|
|
187
|
-
);
|
|
188
|
-
server.tool(
|
|
189
|
-
"envalid_detect",
|
|
190
|
-
"Scan codebase for env var usage and compare with schema \u2014 find undocumented env vars",
|
|
191
|
-
{
|
|
192
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
193
|
-
format: z.enum(["terminal", "json"]).optional().describe("Output format")
|
|
194
|
-
},
|
|
195
|
-
async ({ path, format }) => {
|
|
196
|
-
const args = ["detect"];
|
|
197
|
-
if (format) args.push("--format", format);
|
|
198
|
-
const result = await runCli("envalid", args, path);
|
|
199
|
-
const output = formatOutput(result);
|
|
200
|
-
writeCache("envalid_detect", deriveProject(path), output, result.code);
|
|
201
|
-
const extras = await checkTriggers("envalid_detect", result, path);
|
|
202
|
-
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
203
|
-
}
|
|
204
|
-
);
|
|
205
|
-
server.tool(
|
|
206
|
-
"envalid_init",
|
|
207
|
-
"Generate .env.schema from an existing .env file \u2014 bootstrap type-safe env validation",
|
|
208
|
-
{
|
|
209
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
210
|
-
force: z.coerce.boolean().optional().describe("Overwrite existing schema")
|
|
211
|
-
},
|
|
212
|
-
async ({ path, force }) => {
|
|
213
|
-
const args = ["init"];
|
|
214
|
-
if (force) args.push("--force");
|
|
215
|
-
const result = await runCli("envalid", args, path);
|
|
216
|
-
const output = formatOutput(result);
|
|
217
|
-
return { content: [{ type: "text", text: output }] };
|
|
218
|
-
}
|
|
219
|
-
);
|
|
220
|
-
server.tool(
|
|
221
|
-
"envalid_diff",
|
|
222
|
-
"Compare two .env files \u2014 show added, removed, and changed variables",
|
|
223
|
-
{
|
|
224
|
-
source: z.string().describe("Path to source .env file"),
|
|
225
|
-
target: z.string().describe("Path to target .env file"),
|
|
226
|
-
schema: z.string().optional().describe("Path to .env.schema for sensitivity info"),
|
|
227
|
-
format: z.enum(["terminal", "json", "markdown"]).optional().describe("Output format")
|
|
228
|
-
},
|
|
229
|
-
async ({ source, target, schema, format }) => {
|
|
230
|
-
const args = ["diff", source, target];
|
|
231
|
-
if (schema) args.push("--schema", schema);
|
|
232
|
-
if (format) args.push("--format", format);
|
|
233
|
-
const result = await runCli("envalid", args);
|
|
234
|
-
const output = formatOutput(result);
|
|
235
|
-
return { content: [{ type: "text", text: output }] };
|
|
236
|
-
}
|
|
237
|
-
);
|
|
238
|
-
server.tool(
|
|
239
|
-
"envalid_sync",
|
|
240
|
-
"Check multiple environment files against schema \u2014 ensure all envs are in sync",
|
|
241
|
-
{
|
|
242
|
-
environments: z.string().describe('Comma-separated env file paths (e.g. ".env,.env.staging,.env.production")'),
|
|
243
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
244
|
-
format: z.enum(["terminal", "json", "markdown"]).optional().describe("Output format")
|
|
245
|
-
},
|
|
246
|
-
async ({ environments, path, format }) => {
|
|
247
|
-
const args = ["sync", "--environments", environments];
|
|
248
|
-
if (format) args.push("--format", format);
|
|
249
|
-
const result = await runCli("envalid", args, path);
|
|
250
|
-
const output = formatOutput(result);
|
|
251
|
-
return { content: [{ type: "text", text: output }] };
|
|
252
|
-
}
|
|
253
|
-
);
|
|
254
|
-
server.tool(
|
|
255
|
-
"envalid_generate",
|
|
256
|
-
"Generate .env.example from schema \u2014 create a safe template without secrets",
|
|
257
|
-
{
|
|
258
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
259
|
-
output: z.string().optional().describe("Output file path (default: .env.example)")
|
|
260
|
-
},
|
|
261
|
-
async ({ path, output }) => {
|
|
262
|
-
const args = ["generate-example"];
|
|
263
|
-
if (output) args.push("--output", output);
|
|
264
|
-
const result = await runCli("envalid", args, path);
|
|
265
|
-
const outputText = formatOutput(result);
|
|
266
|
-
return { content: [{ type: "text", text: outputText }] };
|
|
267
|
-
}
|
|
268
|
-
);
|
|
269
|
-
server.tool(
|
|
270
|
-
"envalid_secrets",
|
|
271
|
-
"Scan committed files for leaked secrets \u2014 detect API keys, tokens, passwords in code",
|
|
272
|
-
{
|
|
273
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
274
|
-
format: z.enum(["terminal", "json"]).optional().describe("Output format")
|
|
275
|
-
},
|
|
276
|
-
async ({ path, format }) => {
|
|
277
|
-
const args = ["secrets"];
|
|
278
|
-
if (format) args.push("--format", format);
|
|
279
|
-
const result = await runCli("envalid", args, path);
|
|
280
|
-
const output = formatOutput(result);
|
|
281
|
-
writeCache("envalid_secrets", deriveProject(path), output, result.code);
|
|
282
|
-
return { content: [{ type: "text", text: output }] };
|
|
283
|
-
}
|
|
284
|
-
);
|
|
285
|
-
server.tool(
|
|
286
|
-
"envalid_generate_schema",
|
|
287
|
-
"Generate .env.schema from code analysis \u2014 infer types, required-ness, and sensitivity from usage patterns",
|
|
288
|
-
{
|
|
289
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
290
|
-
output: z.string().optional().describe("Output file path (default: .env.schema)")
|
|
291
|
-
},
|
|
292
|
-
async ({ path, output }) => {
|
|
293
|
-
const args = ["detect", "--generate"];
|
|
294
|
-
if (output) args.push("-o", output);
|
|
295
|
-
const result = await runCli("envalid", args, path);
|
|
296
|
-
const outputText = formatOutput(result);
|
|
297
|
-
return { content: [{ type: "text", text: outputText }] };
|
|
298
|
-
}
|
|
299
|
-
);
|
|
300
|
-
server.tool(
|
|
301
|
-
"envalid_hook_status",
|
|
302
|
-
"Check if the envalid pre-commit git hook is installed",
|
|
303
|
-
{ path: z.string().optional().describe("Project directory (defaults to cwd)") },
|
|
304
|
-
async ({ path }) => {
|
|
305
|
-
const result = await runCli("envalid", ["hook", "status"], path);
|
|
306
|
-
const output = formatOutput(result);
|
|
307
|
-
return { content: [{ type: "text", text: output }] };
|
|
308
|
-
}
|
|
309
|
-
);
|
|
310
|
-
server.tool(
|
|
311
|
-
"berth_status",
|
|
312
|
-
"Show all active ports, Docker ports, and configured ports \u2014 diagnose port conflicts",
|
|
313
|
-
{},
|
|
314
|
-
async () => {
|
|
315
|
-
const result = await runCli("berth", ["status", "--json"]);
|
|
316
|
-
const output = formatOutput(result);
|
|
317
|
-
writeCache("berth_status", deriveProject(), output, result.code);
|
|
318
|
-
const extras = await checkTriggers("berth_status", result);
|
|
319
|
-
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
320
|
-
}
|
|
321
|
-
);
|
|
322
|
-
server.tool(
|
|
323
|
-
"berth_check",
|
|
324
|
-
"Scan a project directory for port conflicts before starting dev servers",
|
|
325
|
-
{ path: z.string().optional().describe("Project directory to check (defaults to cwd)") },
|
|
326
|
-
async ({ path }) => {
|
|
327
|
-
const result = await runCli("berth", ["check", path || "."]);
|
|
328
|
-
const output = formatOutput(result);
|
|
329
|
-
writeCache("berth_check", deriveProject(path), output, result.code);
|
|
330
|
-
const extras = await checkTriggers("berth_check", result, path);
|
|
331
|
-
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
332
|
-
}
|
|
333
|
-
);
|
|
334
|
-
server.tool(
|
|
335
|
-
"berth_kill",
|
|
336
|
-
"Kill processes on a specific port \u2014 free up a port for your dev server",
|
|
337
|
-
{
|
|
338
|
-
port: z.coerce.number().optional().describe("Port number to free"),
|
|
339
|
-
dev: z.coerce.boolean().optional().describe("Kill all dev processes (node, python, ruby, etc.)")
|
|
340
|
-
},
|
|
341
|
-
async ({ port, dev }) => {
|
|
342
|
-
const args = ["kill"];
|
|
343
|
-
if (port) args.push(String(port));
|
|
344
|
-
if (dev) args.push("--dev");
|
|
345
|
-
args.push("--force");
|
|
346
|
-
const result = await runCli("berth", args);
|
|
347
|
-
const output = formatOutput(result);
|
|
348
|
-
return { content: [{ type: "text", text: output }] };
|
|
349
|
-
}
|
|
350
|
-
);
|
|
351
|
-
server.tool(
|
|
352
|
-
"berth_free",
|
|
353
|
-
"Free all ports for a registered project \u2014 kill every process blocking the project",
|
|
354
|
-
{ project: z.string().describe("Registered project name") },
|
|
355
|
-
async ({ project }) => {
|
|
356
|
-
const result = await runCli("berth", ["free", project]);
|
|
357
|
-
const output = formatOutput(result);
|
|
358
|
-
return { content: [{ type: "text", text: output }] };
|
|
359
|
-
}
|
|
360
|
-
);
|
|
361
|
-
server.tool(
|
|
362
|
-
"berth_register",
|
|
363
|
-
"Register a project directory's port requirements for conflict tracking",
|
|
364
|
-
{
|
|
365
|
-
path: z.string().optional().describe("Project directory to register (defaults to cwd)")
|
|
366
|
-
},
|
|
367
|
-
async ({ path }) => {
|
|
368
|
-
const args = ["register", "--yes"];
|
|
369
|
-
if (path) args.push("--dir", path);
|
|
370
|
-
const result = await runCli("berth", args);
|
|
371
|
-
const output = formatOutput(result);
|
|
372
|
-
return { content: [{ type: "text", text: output }] };
|
|
373
|
-
}
|
|
374
|
-
);
|
|
375
|
-
server.tool(
|
|
376
|
-
"berth_list",
|
|
377
|
-
"List all registered projects and their port statuses",
|
|
378
|
-
{},
|
|
379
|
-
async () => {
|
|
380
|
-
const result = await runCli("berth", ["list", "--json"]);
|
|
381
|
-
const output = formatOutput(result);
|
|
382
|
-
return { content: [{ type: "text", text: output }] };
|
|
383
|
-
}
|
|
384
|
-
);
|
|
385
|
-
server.tool(
|
|
386
|
-
"berth_reassign",
|
|
387
|
-
"Change a port assignment in project config files (docker-compose, .env, etc.)",
|
|
388
|
-
{
|
|
389
|
-
oldPort: z.number().describe("Current port number"),
|
|
390
|
-
newPort: z.number().describe("New port number"),
|
|
391
|
-
project: z.string().optional().describe("Project name from registry")
|
|
392
|
-
},
|
|
393
|
-
async ({ oldPort, newPort, project }) => {
|
|
394
|
-
const args = ["reassign", String(oldPort), String(newPort)];
|
|
395
|
-
if (project) args.push("--project", project);
|
|
396
|
-
const result = await runCli("berth", args);
|
|
397
|
-
const output = formatOutput(result);
|
|
398
|
-
return { content: [{ type: "text", text: output }] };
|
|
399
|
-
}
|
|
400
|
-
);
|
|
401
|
-
server.tool(
|
|
402
|
-
"berth_start",
|
|
403
|
-
"Auto-resolve all port conflicts and prepare a project to start cleanly",
|
|
404
|
-
{
|
|
405
|
-
project: z.string().describe("Registered project name"),
|
|
406
|
-
dryRun: z.coerce.boolean().optional().describe("Show what would be done without making changes")
|
|
407
|
-
},
|
|
408
|
-
async ({ project, dryRun }) => {
|
|
409
|
-
const args = ["start", project];
|
|
410
|
-
if (dryRun) args.push("--dry-run");
|
|
411
|
-
const result = await runCli("berth", args);
|
|
412
|
-
const output = formatOutput(result);
|
|
413
|
-
return { content: [{ type: "text", text: output }] };
|
|
414
|
-
}
|
|
415
|
-
);
|
|
416
|
-
server.tool(
|
|
417
|
-
"berth_resolve",
|
|
418
|
-
"Auto-resolve port conflicts \u2014 detect conflicts and fix via kill or reassign strategy",
|
|
419
|
-
{
|
|
420
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
421
|
-
strategy: z.enum(["kill", "reassign", "auto"]).optional().describe("Resolution strategy (default: auto)"),
|
|
422
|
-
kill: z.coerce.boolean().optional().describe("Allow killing processes (required for kill/auto strategies)"),
|
|
423
|
-
dryRun: z.coerce.boolean().optional().describe("Show what would be done without making changes")
|
|
424
|
-
},
|
|
425
|
-
async ({ path, strategy, kill, dryRun }) => {
|
|
426
|
-
const args = ["resolve"];
|
|
427
|
-
if (strategy) args.push("--strategy", strategy);
|
|
428
|
-
if (kill) args.push("--kill");
|
|
429
|
-
if (dryRun) args.push("--dry-run");
|
|
430
|
-
const result = await runCli("berth", args, path);
|
|
431
|
-
const output = formatOutput(result);
|
|
432
|
-
return { content: [{ type: "text", text: output }] };
|
|
433
|
-
}
|
|
434
|
-
);
|
|
435
|
-
server.tool(
|
|
436
|
-
"berth_predict",
|
|
437
|
-
"Predict port conflicts from project config files before starting \u2014 dry-run conflict check",
|
|
438
|
-
{ path: z.string().optional().describe("Project directory (defaults to cwd)") },
|
|
439
|
-
async ({ path }) => {
|
|
440
|
-
const result = await runCli("berth", ["predict", path || "."]);
|
|
441
|
-
const output = formatOutput(result);
|
|
442
|
-
return { content: [{ type: "text", text: output }] };
|
|
443
|
-
}
|
|
444
|
-
);
|
|
445
|
-
server.tool(
|
|
446
|
-
"aware_init",
|
|
447
|
-
"Auto-detect project stack and generate AI context files (CLAUDE.md, .cursorrules, etc.)",
|
|
448
|
-
{
|
|
449
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
450
|
-
targets: z.string().optional().describe("Comma-separated targets: claude,cursor,copilot,agents,all"),
|
|
451
|
-
force: z.coerce.boolean().optional().describe("Overwrite existing files without prompting")
|
|
452
|
-
},
|
|
453
|
-
async ({ path, targets, force }) => {
|
|
454
|
-
const args = ["init"];
|
|
455
|
-
if (targets) args.push("--targets", targets);
|
|
456
|
-
if (force) args.push("--force");
|
|
457
|
-
const result = await runCli("aware", args, path);
|
|
458
|
-
const output = formatOutput(result);
|
|
459
|
-
writeCache("aware_init", deriveProject(path), output, result.code);
|
|
460
|
-
const extras = await checkTriggers("aware_init", result, path);
|
|
461
|
-
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
462
|
-
}
|
|
463
|
-
);
|
|
464
|
-
server.tool(
|
|
465
|
-
"aware_sync",
|
|
466
|
-
"Regenerate AI context files from .aware.json \u2014 update CLAUDE.md, .cursorrules, etc.",
|
|
467
|
-
{
|
|
468
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
469
|
-
dryRun: z.coerce.boolean().optional().describe("Show what would change without writing files")
|
|
470
|
-
},
|
|
471
|
-
async ({ path, dryRun }) => {
|
|
472
|
-
const args = ["sync"];
|
|
473
|
-
if (dryRun) args.push("--dry-run");
|
|
474
|
-
const result = await runCli("aware", args, path);
|
|
475
|
-
const output = formatOutput(result);
|
|
476
|
-
writeCache("aware_sync", deriveProject(path), output, result.code);
|
|
477
|
-
return { content: [{ type: "text", text: output }] };
|
|
478
|
-
}
|
|
479
|
-
);
|
|
480
|
-
server.tool(
|
|
481
|
-
"aware_diff",
|
|
482
|
-
"Show project changes since last sync \u2014 see what drifted in your codebase",
|
|
483
|
-
{
|
|
484
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
485
|
-
exitCode: z.coerce.boolean().optional().describe("Return exit code 1 if changes detected (useful for CI)")
|
|
486
|
-
},
|
|
487
|
-
async ({ path, exitCode }) => {
|
|
488
|
-
const args = ["diff"];
|
|
489
|
-
if (exitCode) args.push("--exit-code");
|
|
490
|
-
const result = await runCli("aware", args, path);
|
|
491
|
-
const output = formatOutput(result);
|
|
492
|
-
return { content: [{ type: "text", text: output }] };
|
|
493
|
-
}
|
|
494
|
-
);
|
|
495
|
-
server.tool(
|
|
496
|
-
"aware_validate",
|
|
497
|
-
"Validate .aware.json schema and content \u2014 check for config errors",
|
|
498
|
-
{ path: z.string().optional().describe("Project directory (defaults to cwd)") },
|
|
499
|
-
async ({ path }) => {
|
|
500
|
-
const result = await runCli("aware", ["validate"], path);
|
|
501
|
-
const output = formatOutput(result);
|
|
502
|
-
return { content: [{ type: "text", text: output }] };
|
|
503
|
-
}
|
|
504
|
-
);
|
|
505
|
-
server.tool(
|
|
506
|
-
"aware_doctor",
|
|
507
|
-
"Diagnose project health \u2014 check config issues, stack drift, stale AI context files",
|
|
508
|
-
{ path: z.string().optional().describe("Project directory (defaults to cwd)") },
|
|
509
|
-
async ({ path }) => {
|
|
510
|
-
const result = await runCli("aware", ["doctor"], path);
|
|
511
|
-
const output = formatOutput(result);
|
|
512
|
-
writeCache("aware_doctor", deriveProject(path), output, result.code);
|
|
513
|
-
const extras = await checkTriggers("aware_doctor", result, path);
|
|
514
|
-
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
515
|
-
}
|
|
516
|
-
);
|
|
517
|
-
server.tool(
|
|
518
|
-
"aware_add",
|
|
519
|
-
"Add a rule, convention, or structure entry to .aware.json",
|
|
520
|
-
{
|
|
521
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
522
|
-
type: z.enum(["rule", "convention", "structure"]).describe("Type to add")
|
|
523
|
-
},
|
|
524
|
-
async ({ path, type }) => {
|
|
525
|
-
const args = ["add", "--type", type];
|
|
526
|
-
const result = await runCli("aware", args, path);
|
|
527
|
-
const output = formatOutput(result);
|
|
528
|
-
return { content: [{ type: "text", text: output }] };
|
|
529
|
-
}
|
|
530
|
-
);
|
|
531
|
-
server.tool(
|
|
532
|
-
"vow_scan",
|
|
533
|
-
"Scan dependency licenses \u2014 summarize all licenses in the project",
|
|
534
|
-
{
|
|
535
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
536
|
-
production: z.coerce.boolean().optional().describe("Skip devDependencies"),
|
|
537
|
-
format: z.enum(["terminal", "json"]).optional().describe("Output format")
|
|
538
|
-
},
|
|
539
|
-
async ({ path, production, format }) => {
|
|
540
|
-
const args = ["scan"];
|
|
541
|
-
if (production) args.push("--production");
|
|
542
|
-
if (format) args.push("--format", format);
|
|
543
|
-
const result = await runCli("vow", args, path);
|
|
544
|
-
const output = formatOutput(result);
|
|
545
|
-
writeCache("vow_scan", deriveProject(path), output, result.code);
|
|
546
|
-
const extras = await checkTriggers("vow_scan", result, path);
|
|
547
|
-
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
548
|
-
}
|
|
549
|
-
);
|
|
550
|
-
server.tool(
|
|
551
|
-
"vow_check",
|
|
552
|
-
"Validate dependency licenses against policy \u2014 flag violations before release",
|
|
553
|
-
{
|
|
554
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
555
|
-
production: z.coerce.boolean().optional().describe("Skip devDependencies")
|
|
556
|
-
},
|
|
557
|
-
async ({ path, production }) => {
|
|
558
|
-
const args = ["check"];
|
|
559
|
-
if (production) args.push("--production");
|
|
560
|
-
const result = await runCli("vow", args, path);
|
|
561
|
-
const output = formatOutput(result);
|
|
562
|
-
writeCache("vow_check", deriveProject(path), output, result.code);
|
|
563
|
-
const extras = await checkTriggers("vow_check", result, path);
|
|
564
|
-
return { content: [{ type: "text", text: output + extras.join("") }] };
|
|
565
|
-
}
|
|
566
|
-
);
|
|
567
|
-
server.tool(
|
|
568
|
-
"vow_init",
|
|
569
|
-
"Generate a license policy file (.vow.json) \u2014 choose from commercial, opensource, or strict templates",
|
|
570
|
-
{
|
|
571
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
572
|
-
template: z.enum(["commercial", "opensource", "strict"]).optional().describe("Policy template")
|
|
573
|
-
},
|
|
574
|
-
async ({ path, template }) => {
|
|
575
|
-
const args = ["init"];
|
|
576
|
-
if (template) args.push("--template", template);
|
|
577
|
-
const result = await runCli("vow", args, path);
|
|
578
|
-
const output = formatOutput(result);
|
|
579
|
-
return { content: [{ type: "text", text: output }] };
|
|
580
|
-
}
|
|
581
|
-
);
|
|
582
|
-
server.tool(
|
|
583
|
-
"vow_tree",
|
|
584
|
-
"Display dependency tree with license annotations \u2014 trace license inheritance",
|
|
585
|
-
{
|
|
586
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
587
|
-
filter: z.string().optional().describe('Show only subtrees containing this license (e.g. "GPL")'),
|
|
588
|
-
depth: z.coerce.number().optional().describe("Max tree depth"),
|
|
589
|
-
production: z.coerce.boolean().optional().describe("Skip devDependencies")
|
|
590
|
-
},
|
|
591
|
-
async ({ path, filter, depth, production }) => {
|
|
592
|
-
const args = ["tree"];
|
|
593
|
-
if (path) args.push("--path", path);
|
|
594
|
-
if (filter) args.push("--filter", filter);
|
|
595
|
-
if (depth) args.push("--depth", String(depth));
|
|
596
|
-
if (production) args.push("--production");
|
|
597
|
-
const result = await runCli("vow", args, path);
|
|
598
|
-
const output = formatOutput(result);
|
|
599
|
-
return { content: [{ type: "text", text: output }] };
|
|
600
|
-
}
|
|
601
|
-
);
|
|
602
|
-
server.tool(
|
|
603
|
-
"vow_fix",
|
|
604
|
-
"Suggest alternative packages for license policy violations \u2014 find compliant replacements",
|
|
605
|
-
{
|
|
606
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
607
|
-
production: z.coerce.boolean().optional().describe("Skip devDependencies"),
|
|
608
|
-
limit: z.coerce.number().optional().describe("Max alternatives per package")
|
|
609
|
-
},
|
|
610
|
-
async ({ path, production, limit }) => {
|
|
611
|
-
const args = ["fix"];
|
|
612
|
-
if (path) args.push("--path", path);
|
|
613
|
-
if (production) args.push("--production");
|
|
614
|
-
if (limit) args.push("--limit", String(limit));
|
|
615
|
-
const result = await runCli("vow", args, path);
|
|
616
|
-
const output = formatOutput(result);
|
|
617
|
-
return { content: [{ type: "text", text: output }] };
|
|
618
|
-
}
|
|
619
|
-
);
|
|
620
|
-
server.tool(
|
|
621
|
-
"vow_export",
|
|
622
|
-
"Export full license report as JSON, CSV, or Markdown",
|
|
623
|
-
{
|
|
624
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
625
|
-
format: z.enum(["json", "csv", "markdown"]).optional().describe("Export format (default: json)"),
|
|
626
|
-
output: z.string().optional().describe("Output file path"),
|
|
627
|
-
production: z.coerce.boolean().optional().describe("Skip devDependencies")
|
|
628
|
-
},
|
|
629
|
-
async ({ path, format, output, production }) => {
|
|
630
|
-
const args = ["export"];
|
|
631
|
-
if (path) args.push("--path", path);
|
|
632
|
-
if (format) args.push("--format", format);
|
|
633
|
-
if (output) args.push("--output", output);
|
|
634
|
-
if (production) args.push("--production");
|
|
635
|
-
const result = await runCli("vow", args, path);
|
|
636
|
-
const outputText = formatOutput(result);
|
|
637
|
-
return { content: [{ type: "text", text: outputText }] };
|
|
638
|
-
}
|
|
639
|
-
);
|
|
640
|
-
server.tool(
|
|
641
|
-
"vow_hook_install",
|
|
642
|
-
"Install a pre-commit git hook that checks dependency licenses before each commit",
|
|
643
|
-
{
|
|
644
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)")
|
|
645
|
-
},
|
|
646
|
-
async ({ path }) => {
|
|
647
|
-
const result = await runCli("vow", ["hook", "install"], path);
|
|
648
|
-
const output = formatOutput(result);
|
|
649
|
-
return { content: [{ type: "text", text: output }] };
|
|
650
|
-
}
|
|
651
|
-
);
|
|
652
|
-
server.tool(
|
|
653
|
-
"vow_hook_uninstall",
|
|
654
|
-
"Remove the vow pre-commit license check hook",
|
|
655
|
-
{
|
|
656
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)")
|
|
657
|
-
},
|
|
658
|
-
async ({ path }) => {
|
|
659
|
-
const result = await runCli("vow", ["hook", "uninstall"], path);
|
|
660
|
-
const output = formatOutput(result);
|
|
661
|
-
return { content: [{ type: "text", text: output }] };
|
|
662
|
-
}
|
|
663
|
-
);
|
|
664
|
-
server.tool(
|
|
665
|
-
"vow_hook_status",
|
|
666
|
-
"Check if the vow pre-commit license check hook is installed",
|
|
667
|
-
{
|
|
668
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)")
|
|
669
|
-
},
|
|
670
|
-
async ({ path }) => {
|
|
671
|
-
const result = await runCli("vow", ["hook", "status"], path);
|
|
672
|
-
const output = formatOutput(result);
|
|
673
|
-
return { content: [{ type: "text", text: output }] };
|
|
674
|
-
}
|
|
675
|
-
);
|
|
676
|
-
server.tool(
|
|
677
|
-
"vow_attribution",
|
|
678
|
-
"Generate THIRD_PARTY_LICENSES.md \u2014 list all dependencies with their licenses for compliance",
|
|
679
|
-
{
|
|
680
|
-
path: z.string().optional().describe("Project directory (defaults to cwd)"),
|
|
681
|
-
output: z.string().optional().describe("Output file (default: THIRD_PARTY_LICENSES.md)"),
|
|
682
|
-
production: z.coerce.boolean().optional().describe("Skip devDependencies")
|
|
683
|
-
},
|
|
684
|
-
async ({ path, output, production }) => {
|
|
685
|
-
const args = ["attribution"];
|
|
686
|
-
if (path) args.push("--path", path);
|
|
687
|
-
if (output) args.push("--output", output);
|
|
688
|
-
if (production) args.push("--production");
|
|
689
|
-
const result = await runCli("vow", args, path);
|
|
690
|
-
const outputText = formatOutput(result);
|
|
691
|
-
return { content: [{ type: "text", text: outputText }] };
|
|
692
|
-
}
|
|
693
|
-
);
|
|
722
|
+
registerStaleTools(server);
|
|
723
|
+
registerEnvalidTools(server);
|
|
724
|
+
registerBerthTools(server);
|
|
725
|
+
registerAwareTools(server);
|
|
726
|
+
registerVowTools(server);
|
|
694
727
|
process.on("SIGINT", () => {
|
|
695
728
|
velocityDb.close();
|
|
696
729
|
process.exit(0);
|