@esneiderbravo/speclaw 0.3.4 → 0.3.7
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 +66 -15
- package/dist/cli/commands/budget.js +36 -0
- package/dist/cli/commands/doctor.js +53 -11
- package/dist/cli/commands/init.js +3 -1
- package/dist/cli/commands/telemetry.js +16 -0
- package/dist/cli/commands/update.js +36 -3
- package/dist/cli/index.js +19 -4
- package/dist/modules/compass/indexer.js +10 -0
- package/dist/modules/compass/map.js +114 -0
- package/dist/modules/compass/register.js +30 -64
- package/dist/modules/foundation/assets/docs/compass.template.md +3 -0
- package/dist/modules/foundation/context-budget.js +58 -0
- package/dist/modules/foundation/doctor.js +626 -161
- package/dist/modules/foundation/graph.js +7 -4
- package/dist/modules/foundation/hooks.js +12 -0
- package/dist/modules/foundation/laws.js +1 -0
- package/dist/modules/foundation/register-core.js +108 -0
- package/dist/modules/foundation/register.js +22 -159
- package/dist/modules/foundation/scaffold.js +1 -1
- package/dist/modules/lawbook/assets/skills/archive/SKILL.md +1 -31
- package/dist/modules/lawbook/assets/skills/archive/steps/01-confirm-done.md +7 -0
- package/dist/modules/lawbook/assets/skills/archive/steps/02-reconcile.md +15 -0
- package/dist/modules/lawbook/assets/skills/archive/steps/03-validate-and-sync.md +7 -0
- package/dist/modules/lawbook/assets/skills/archive/steps/04-archive.md +9 -0
- package/dist/modules/lawbook/assets/skills/archive/steps/05-report.md +7 -0
- package/dist/modules/lawbook/assets/skills/build/SKILL.md +2 -100
- package/dist/modules/lawbook/assets/skills/build/steps/01-load-change.md +7 -0
- package/dist/modules/lawbook/assets/skills/build/steps/02-branch.md +6 -0
- package/dist/modules/lawbook/assets/skills/build/steps/03-implement.md +11 -0
- package/dist/modules/lawbook/assets/skills/build/steps/04-quality-gates.md +10 -0
- package/dist/modules/lawbook/assets/skills/build/steps/05-manual-verification.md +22 -0
- package/dist/modules/lawbook/assets/skills/build/steps/06-discipline-reports.md +44 -0
- package/dist/modules/lawbook/assets/skills/build/steps/07-hand-off.md +9 -0
- package/dist/modules/lawbook/assets/skills/draft/SKILL.md +3 -80
- package/dist/modules/lawbook/assets/skills/draft/steps/01-ensure-workspace.md +5 -0
- package/dist/modules/lawbook/assets/skills/draft/steps/02-understand.md +12 -0
- package/dist/modules/lawbook/assets/skills/draft/steps/03-name-capabilities.md +14 -0
- package/dist/modules/lawbook/assets/skills/draft/steps/04-write-artifacts.md +41 -0
- package/dist/modules/lawbook/assets/skills/draft/steps/05-validate.md +11 -0
- package/dist/modules/lawbook/assets/skills/draft/steps/06-hand-off.md +5 -0
- package/dist/modules/lawbook/assets/skills/explore/SKILL.md +6 -22
- package/dist/modules/lawbook/assets/skills/explore/steps/01-investigate.md +16 -0
- package/dist/modules/lawbook/assets/skills/explore/steps/02-summarize.md +7 -0
- package/dist/modules/lawbook/assets/skills/sync/SKILL.md +1 -30
- package/dist/modules/lawbook/assets/skills/sync/steps/01-confirm.md +5 -0
- package/dist/modules/lawbook/assets/skills/sync/steps/02-reconcile.md +16 -0
- package/dist/modules/lawbook/assets/skills/sync/steps/03-validate.md +6 -0
- package/dist/modules/lawbook/assets/skills/sync/steps/04-promote.md +10 -0
- package/dist/modules/lawbook/assets/skills/sync/steps/05-report.md +7 -0
- package/dist/modules/lawbook/register.js +17 -36
- package/dist/modules/tools/register.js +15 -15
- package/dist/server.js +13 -7
- package/dist/shared/budget.js +159 -0
- package/dist/shared/exposure.js +110 -0
- package/dist/shared/manifest.js +11 -2
- package/dist/shared/mcp.js +33 -0
- package/dist/shared/redact.js +90 -0
- package/dist/shared/schema-tokens.js +86 -0
- package/dist/shared/tokens.js +41 -0
- package/package.json +2 -1
|
@@ -1,78 +1,44 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { text } from "../../shared/mcp.js";
|
|
2
|
+
import { defineTool, text } from "../../shared/mcp.js";
|
|
3
|
+
import { shouldExpose } from "../../shared/exposure.js";
|
|
3
4
|
import { buildIndex } from "./indexer.js";
|
|
4
5
|
import { explore, search, recall, impact, trace } from "./query.js";
|
|
5
6
|
import { startWatch, stopWatch, watchStatus } from "./watcher.js";
|
|
6
7
|
import { visualize } from "./visualize.js";
|
|
7
8
|
// ─── Compass: speclaw's own code-intelligence engine (no external deps) ───
|
|
8
|
-
// A local graph of the codebase (nodes = definitions, edges = calls/imports)
|
|
9
|
-
// plus a local vector store for semantic recall. Lives in .speclaw/ (gitignored).
|
|
10
9
|
/**
|
|
11
|
-
* Register
|
|
12
|
-
* trace, watch) on the given server.
|
|
10
|
+
* Register Compass MCP tools on the given server.
|
|
13
11
|
*
|
|
14
|
-
* @param server - The MCP server to register
|
|
12
|
+
* @param server - The MCP server to register on.
|
|
13
|
+
* @param opts - Exposure options (`minimal` omits setup/specialized tools).
|
|
15
14
|
*/
|
|
16
|
-
export function registerCompass(server) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}, async ({ projectPath, node }) => text(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
limit: z.number().optional().describe("Max results (default 25)"),
|
|
34
|
-
},
|
|
35
|
-
}, async ({ projectPath, query, limit }) => text(search(projectPath, query, limit ?? 25)));
|
|
36
|
-
server.registerTool("compass_recall", {
|
|
37
|
-
description: "Semantic search of the Compass: describe what you're looking for in natural language ('where auth tokens are validated') and get the nodes ranked by meaning, using the local vector store — even when the identifier names don't contain your words. Requires compass_index to have run.",
|
|
38
|
-
inputSchema: {
|
|
39
|
-
projectPath: z.string().describe("Absolute path to the project"),
|
|
40
|
-
query: z.string().describe("Natural-language description of the code you want"),
|
|
41
|
-
limit: z.number().optional().describe("Max results (default 15)"),
|
|
42
|
-
},
|
|
43
|
-
}, async ({ projectPath, query, limit }) => text(await recall(projectPath, query, limit ?? 15)));
|
|
44
|
-
server.registerTool("compass_impact", {
|
|
45
|
-
description: "Blast radius: every node that transitively calls the target, up to a depth. Answers 'what could break if I change this?' before editing. Includes dynamic-dispatch callers (matched by name). Requires compass_index.",
|
|
46
|
-
inputSchema: {
|
|
47
|
-
projectPath: z.string().describe("Absolute path to the project"),
|
|
48
|
-
node: z.string().describe("Node name whose dependents you want"),
|
|
49
|
-
maxDepth: z.number().optional().describe("Max hops to traverse (default 4)"),
|
|
50
|
-
},
|
|
51
|
-
}, async ({ projectPath, node, maxDepth }) => text(impact(projectPath, node, maxDepth ?? 4)));
|
|
52
|
-
server.registerTool("compass_trace", {
|
|
53
|
-
description: "Trace a call path from one node to another: returns the chain of calls linking them (or null if none within depth). Useful to understand how an entrypoint reaches a sink. Requires compass_index.",
|
|
54
|
-
inputSchema: {
|
|
55
|
-
projectPath: z.string().describe("Absolute path to the project"),
|
|
56
|
-
from: z.string().describe("Starting node name"),
|
|
57
|
-
to: z.string().describe("Target node name"),
|
|
58
|
-
maxDepth: z.number().optional().describe("Max hops to search (default 8)"),
|
|
59
|
-
},
|
|
15
|
+
export function registerCompass(server, opts = {}) {
|
|
16
|
+
const minimal = Boolean(opts.minimal);
|
|
17
|
+
const add = (name, description, inputSchema, handler) => {
|
|
18
|
+
if (!shouldExpose(name, minimal))
|
|
19
|
+
return;
|
|
20
|
+
defineTool(server, { name, description, inputSchema, handler });
|
|
21
|
+
};
|
|
22
|
+
add("compass_index", "Build or refresh the local code graph index. Run once per project, then on demand.", { projectPath: z.string() }, async ({ projectPath }) => text(await buildIndex(projectPath)));
|
|
23
|
+
add("compass_explore", "Read a symbol's source plus callers and callees. Prefer this before grep or Read.", { projectPath: z.string(), node: z.string() }, async ({ projectPath, node }) => text(explore(projectPath, node)));
|
|
24
|
+
add("compass_search", "Find symbols by name or keyword (substring). Cheaper structural search than grep.", { projectPath: z.string(), query: z.string(), limit: z.number().optional() }, async ({ projectPath, query, limit }) => text(search(projectPath, query, limit ?? 25)));
|
|
25
|
+
add("compass_recall", "Find symbols by meaning via local embeddings. Use when names are unknown.", { projectPath: z.string(), query: z.string(), limit: z.number().optional() }, async ({ projectPath, query, limit }) => text(await recall(projectPath, query, limit ?? 15)));
|
|
26
|
+
add("compass_impact", "List transitive callers of a symbol (blast radius) before editing.", { projectPath: z.string(), node: z.string(), maxDepth: z.number().optional() }, async ({ projectPath, node, maxDepth }) => text(impact(projectPath, node, maxDepth ?? 4)));
|
|
27
|
+
add("compass_trace", "Find a call path between two symbols within a depth limit.", {
|
|
28
|
+
projectPath: z.string(),
|
|
29
|
+
from: z.string(),
|
|
30
|
+
to: z.string(),
|
|
31
|
+
maxDepth: z.number().optional(),
|
|
60
32
|
}, async ({ projectPath, from, to, maxDepth }) => text(trace(projectPath, from, to, maxDepth ?? 8)));
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
depth: z.number().optional().describe("BFS depth around the focus node (default 2)"),
|
|
67
|
-
limit: z.number().optional().describe("Max nodes for the whole-graph view (default 300)"),
|
|
68
|
-
},
|
|
33
|
+
add("compass_visualize", "Write an offline HTML graph to .speclaw/graph.html for interactive exploration.", {
|
|
34
|
+
projectPath: z.string(),
|
|
35
|
+
node: z.string().optional(),
|
|
36
|
+
depth: z.number().optional(),
|
|
37
|
+
limit: z.number().optional(),
|
|
69
38
|
}, async ({ projectPath, node, depth, limit }) => text(visualize(projectPath, { focus: node, depth, limit })));
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
projectPath: z.string().describe("Absolute path to the project"),
|
|
74
|
-
action: z.enum(["start", "stop", "status"]).describe("start, stop, or status"),
|
|
75
|
-
},
|
|
39
|
+
add("compass_watch", "Start, stop, or status a debounced file watcher that re-indexes on change.", {
|
|
40
|
+
projectPath: z.string(),
|
|
41
|
+
action: z.enum(["start", "stop", "status"]),
|
|
76
42
|
}, async ({ projectPath, action }) => {
|
|
77
43
|
const result = action === "start"
|
|
78
44
|
? startWatch(projectPath)
|
|
@@ -42,6 +42,9 @@ a missing graph is not license to skip Compass. The only legitimate fallbacks
|
|
|
42
42
|
to Grep/Read: a Compass call returned nothing useful for your query, or the
|
|
43
43
|
target isn't indexed code (stylesheets, JSON/config, markdown, logs).
|
|
44
44
|
|
|
45
|
+
<!-- speclaw:map:start -->
|
|
46
|
+
<!-- speclaw:map:end -->
|
|
47
|
+
|
|
45
48
|
## Project-specific starting points
|
|
46
49
|
|
|
47
50
|
<!-- Filled in during speclaw init: the project's real entrypoints, core
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { registerCompass } from "../compass/register.js";
|
|
3
|
+
import { registerSpec } from "../lawbook/register.js";
|
|
4
|
+
import { registerTools } from "../tools/register.js";
|
|
5
|
+
import { registerFoundationCore } from "./register-core.js";
|
|
6
|
+
import { measureBudget } from "../../shared/budget.js";
|
|
7
|
+
import { isMinimalMode, packageRoot, shouldExpose } from "../../shared/exposure.js";
|
|
8
|
+
/** Mirrors the `doctor` tool surface without importing `doctor.ts` (avoids a cycle). */
|
|
9
|
+
const DOCTOR_TOOL_FOR_BUDGET = {
|
|
10
|
+
name: "doctor",
|
|
11
|
+
description: "Verify the speclaw install; returns a versioned DoctorReport (schemaVersion 1).",
|
|
12
|
+
inputSchema: { projectPath: z.string() },
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Collect tool definitions as the MCP server would register them for a profile.
|
|
16
|
+
*
|
|
17
|
+
* Uses `registerFoundationCore` plus a static `doctor` stub so budget/doctor
|
|
18
|
+
* measurement never imports the live `doctor` implementation (module cycle).
|
|
19
|
+
*
|
|
20
|
+
* @param minimal - Exposure profile.
|
|
21
|
+
*/
|
|
22
|
+
export function collectRegisteredTools(minimal) {
|
|
23
|
+
const tools = [];
|
|
24
|
+
const server = {
|
|
25
|
+
registerTool(name, config) {
|
|
26
|
+
tools.push({
|
|
27
|
+
name,
|
|
28
|
+
description: config.description ?? "",
|
|
29
|
+
inputSchema: config.inputSchema,
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
const opts = { minimal };
|
|
34
|
+
const stub = server;
|
|
35
|
+
registerFoundationCore(stub, opts);
|
|
36
|
+
if (shouldExpose("doctor", minimal)) {
|
|
37
|
+
tools.push(DOCTOR_TOOL_FOR_BUDGET);
|
|
38
|
+
}
|
|
39
|
+
registerSpec(stub, opts);
|
|
40
|
+
registerCompass(stub, opts);
|
|
41
|
+
registerTools(stub, opts);
|
|
42
|
+
return tools;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Measure context budget for an install (registered tools + project files).
|
|
46
|
+
*
|
|
47
|
+
* @param projectPath - Project root.
|
|
48
|
+
* @param minimal - Optional forced profile.
|
|
49
|
+
*/
|
|
50
|
+
export function measureInstallBudget(projectPath, minimal) {
|
|
51
|
+
const profile = minimal ?? isMinimalMode(projectPath);
|
|
52
|
+
return measureBudget({
|
|
53
|
+
projectPath,
|
|
54
|
+
packagePath: packageRoot(),
|
|
55
|
+
tools: collectRegisteredTools(profile),
|
|
56
|
+
minimal: profile,
|
|
57
|
+
});
|
|
58
|
+
}
|