@toolbaux/guardian 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -79,6 +79,58 @@ Guardian auto-injects architecture context into `CLAUDE.md` so your AI tool read
79
79
 
80
80
  The block between markers is replaced on every save (VSCode extension) and every commit (pre-commit hook). Your manual content outside the markers is never touched.
81
81
 
82
+ ## MCP Server — AI Tools Connect Directly
83
+
84
+ Guardian includes an MCP server that Claude Code and Cursor connect to automatically. The VSCode extension sets this up on first activation — no manual config needed.
85
+
86
+ **6 compact tools available to AI:**
87
+
88
+ | Tool | Tokens | Purpose |
89
+ |------|--------|---------|
90
+ | `guardian_orient` | ~100 | Project summary at session start |
91
+ | `guardian_context` | ~50-80 | File or endpoint dependencies before editing |
92
+ | `guardian_impact` | ~30 | What breaks if you change a file |
93
+ | `guardian_search` | ~70 | Find endpoints, models, modules by keyword |
94
+ | `guardian_model` | ~90 | Full field details (only when needed) |
95
+ | `guardian_metrics` | ~50 | Session usage stats |
96
+
97
+ All responses are compact JSON — no pretty-printing, no verbose keys. Repeated calls are cached (30s TTL). Usage metrics tracked per session.
98
+
99
+ **Manual setup** (if the extension doesn't auto-configure):
100
+
101
+ Create `.mcp.json` at your project root:
102
+ ```json
103
+ {
104
+ "mcpServers": {
105
+ "guardian": {
106
+ "command": "guardian",
107
+ "args": ["mcp-serve", "--specs", ".specs"]
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## VSCode Extension
114
+
115
+ Install from [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=toolbaux.toolbaux-guardian):
116
+
117
+ Search "ToolBaux Guardian" in Extensions, or:
118
+ ```
119
+ Cmd+Shift+P → "Extensions: Install from VSIX"
120
+ ```
121
+
122
+ **What it does automatically:**
123
+ - Creates `.specs/`, config, and pre-commit hook on first activation
124
+ - Configures MCP server for Claude Code and Cursor (`.mcp.json`)
125
+ - Extracts architecture on every file save (5s debounce)
126
+ - Shows drift status in status bar: `✓ Guardian: stable · 35 ep · 8 pg`
127
+
128
+ **Commands** (Cmd+Shift+P):
129
+ - Guardian: Initialize Project
130
+ - Guardian: Generate AI Context
131
+ - Guardian: Drift Check
132
+ - Guardian: Generate Constraints
133
+
82
134
  ## Key Commands
83
135
 
84
136
  ```bash
@@ -98,208 +98,248 @@ async function loadIntel() {
98
98
  }
99
99
  return intel;
100
100
  }
101
- // ── Tool implementations ──
102
- async function fileContext(args) {
103
- const data = await loadIntel();
104
- const file = args.file.replace(/^\.\//, "");
105
- // Find which module this file belongs to
106
- const module = data.service_map?.find((m) => m.path && file.startsWith(m.path.replace(/^\.\//, "")));
107
- // Find endpoints in this file
108
- const endpoints = Object.values(data.api_registry || {}).filter((ep) => ep.file && file.includes(ep.file.replace(/^\.\//, "")));
109
- // Find models in this file
110
- const models = Object.values(data.model_registry || {}).filter((m) => m.file && file.includes(m.file.replace(/^\.\//, "")));
111
- // Find which endpoints call services defined in this file
112
- const calledBy = [];
113
- const fileName = path.basename(file, path.extname(file));
114
- for (const [key, ep] of Object.entries(data.api_registry || {})) {
115
- const e = ep;
116
- if (e.service_calls?.some((s) => s.toLowerCase().includes(fileName.toLowerCase()))) {
117
- calledBy.push(`${e.method} ${e.path} (${e.handler})`);
101
+ // ── Helpers ──
102
+ const SKIP_SERVICES = new Set(["str", "dict", "int", "len", "float", "max", "join", "getattr", "lower", "open", "params.append", "updates.append"]);
103
+ function compact(obj) {
104
+ return JSON.stringify(obj);
105
+ }
106
+ function normalize(p) {
107
+ return p.replace(/^\.\//, "").replace(/\/\//g, "/");
108
+ }
109
+ function findModule(data, file) {
110
+ const f = normalize(file);
111
+ return data.service_map?.find((m) => {
112
+ const mp = normalize(m.path || "");
113
+ return mp && (f.startsWith(mp + "/") || f === mp);
114
+ }) || data.service_map?.find((m) => {
115
+ // Fallback: match by module ID (handles doubled paths)
116
+ const mid = normalize(m.id || "");
117
+ return mid && f.includes(mid);
118
+ });
119
+ }
120
+ function findEndpointsInFile(data, file) {
121
+ const f = normalize(file);
122
+ const basename = path.basename(f);
123
+ return Object.values(data.api_registry || {}).filter((ep) => {
124
+ const ef = normalize(ep.file || "");
125
+ return ef && (f.includes(ef) || ef.includes(f) || ef.endsWith(basename));
126
+ });
127
+ }
128
+ function findModelsInFile(data, file) {
129
+ const f = normalize(file);
130
+ const basename = path.basename(f);
131
+ return Object.values(data.model_registry || {}).filter((m) => {
132
+ const mf = normalize(m.file || "");
133
+ return mf && (f.includes(mf) || mf.includes(f) || mf.endsWith(basename));
134
+ });
135
+ }
136
+ // ── Tool implementations (compact JSON, no redundancy) ──
137
+ async function orient() {
138
+ // Read architecture-context.md first — it has the richest summary
139
+ const contextPath = path.join(path.dirname(intelPath), "architecture-context.md");
140
+ try {
141
+ const raw = await fs.readFile(contextPath, "utf8");
142
+ // Extract the content between guardian:context markers
143
+ const match = raw.match(/<!-- guardian:context[^>]*-->([\s\S]*?)<!-- \/guardian:context -->/);
144
+ if (match) {
145
+ // Parse the markdown into compact structured data
146
+ const content = match[1];
147
+ const lines = content.split("\n").map((l) => l.trim()).filter(Boolean);
148
+ // Extract key sections
149
+ const desc = raw.match(/Description: (.+)/)?.[1] || "";
150
+ const codeMap = lines.find((l) => l.startsWith("**Backend:**")) || "";
151
+ // Module map with exports
152
+ const moduleLines = lines.filter((l) => l.startsWith("- **backend/") || l.startsWith("- **frontend/"));
153
+ const modules = moduleLines.map((l) => {
154
+ const m = l.match(/\*\*([^*]+)\*\*\s*\(([^)]+)\)\s*[—–-]\s*(.*)/);
155
+ return m ? [m[1], m[2], m[3].slice(0, 60)] : null;
156
+ }).filter(Boolean);
157
+ // Dependencies
158
+ const deps = lines.filter((l) => l.includes("→")).map((l) => l.replace(/^- /, ""));
159
+ // High-coupling files
160
+ const coupling = lines.filter((l) => l.match(/score \d/)).map((l) => l.replace(/^- /, ""));
161
+ // Structural intelligence
162
+ const si = lines.filter((l) => l.includes("depth=")).map((l) => l.replace(/^- /, ""));
163
+ // Model-endpoint map
164
+ const modelEp = lines.filter((l) => l.includes("endpoints) ->")).map((l) => l.replace(/^- /, ""));
165
+ return compact({
166
+ desc: desc.slice(0, 120),
167
+ map: codeMap,
168
+ modules,
169
+ deps,
170
+ coupling: coupling.slice(0, 5),
171
+ si: si.slice(0, 5),
172
+ modelEp,
173
+ });
118
174
  }
119
175
  }
120
- // Find what this file's endpoints call
121
- const calls = endpoints.flatMap((ep) => (ep.service_calls || []).filter((s) => !["str", "dict", "int", "len", "float", "max", "join", "getattr"].includes(s)));
122
- // Find frontend pages that use APIs from this module
123
- const pages = (data.frontend_pages || []).filter((p) => p.api_calls?.some((call) => endpoints.some((ep) => call.includes(ep.path?.split("{")[0]))));
124
- return JSON.stringify({
125
- file,
126
- module: module ? { id: module.id, layer: module.layer, file_count: module.file_count, imports: module.imports } : null,
127
- endpoints_in_file: endpoints.map((ep) => `${ep.method} ${ep.path} ${ep.handler}`),
128
- models_in_file: models.map((m) => `${m.name} (${m.framework}, ${m.fields?.length || 0} fields)`),
129
- calls_downstream: [...new Set(calls)],
130
- called_by_upstream: calledBy.slice(0, 10),
131
- frontend_pages_using: pages.map((p) => p.path),
132
- coupling: module?.coupling_score ?? null,
133
- }, null, 2);
134
- }
135
- async function search(args) {
136
- const data = await loadIntel();
137
- const q = args.query.toLowerCase();
138
- const types = (args.types || "models,endpoints,modules").split(",").map((t) => t.trim());
139
- const results = {};
140
- if (types.includes("endpoints")) {
141
- results.endpoints = Object.values(data.api_registry || {})
142
- .filter((ep) => ep.path?.toLowerCase().includes(q) ||
143
- ep.handler?.toLowerCase().includes(q) ||
144
- ep.service_calls?.some((s) => s.toLowerCase().includes(q)))
145
- .slice(0, 10)
146
- .map((ep) => `${ep.method} ${ep.path} → ${ep.handler} [${ep.module}]`);
176
+ catch { }
177
+ // Fallback: build from codebase-intelligence.json
178
+ const d = await loadIntel();
179
+ const c = d.meta?.counts || {};
180
+ // Compute endpoint counts from api_registry (service_map counts are often 0)
181
+ const epByMod = {};
182
+ for (const ep of Object.values(d.api_registry || {})) {
183
+ epByMod[ep.module] = (epByMod[ep.module] || 0) + 1;
147
184
  }
148
- if (types.includes("models")) {
149
- results.models = Object.values(data.model_registry || {})
150
- .filter((m) => m.name?.toLowerCase().includes(q) ||
151
- m.fields?.some((f) => f.toLowerCase().includes(q)))
152
- .slice(0, 10)
153
- .map((m) => `${m.name} (${m.framework}, ${m.fields?.length} fields, ${m.file})`);
185
+ const mods = (d.service_map || []).filter((m) => m.file_count > 0);
186
+ const topMods = mods
187
+ .map((m) => ({ ...m, ep_count: epByMod[m.id] || 0 }))
188
+ .sort((a, b) => b.ep_count - a.ep_count)
189
+ .slice(0, 6);
190
+ return compact({
191
+ p: d.meta?.project,
192
+ ep: c.endpoints, mod: c.models, pg: c.pages, m: c.modules,
193
+ top: topMods.map((m) => [m.id, m.ep_count, m.layer]),
194
+ pages: (d.frontend_pages || []).map((p) => p.path),
195
+ });
196
+ }
197
+ async function context(args) {
198
+ const d = await loadIntel();
199
+ const t = args.target;
200
+ // Check if target is an endpoint (e.g. "POST /sessions/start")
201
+ const epMatch = t.match(/^(GET|POST|PUT|DELETE|PATCH)\s+(.+)$/i);
202
+ if (epMatch) {
203
+ const ep = d.api_registry?.[`${epMatch[1].toUpperCase()} ${epMatch[2]}`]
204
+ || Object.values(d.api_registry || {}).find((e) => e.method === epMatch[1].toUpperCase() && e.path === epMatch[2]);
205
+ if (!ep)
206
+ return compact({ err: "not found" });
207
+ const svcs = (ep.service_calls || []).filter((s) => !SKIP_SERVICES.has(s));
208
+ return compact({
209
+ ep: `${ep.method} ${ep.path}`, h: ep.handler, f: ep.file, m: ep.module,
210
+ req: ep.request_schema, res: ep.response_schema,
211
+ calls: svcs, ai: ep.ai_operations?.length || 0,
212
+ });
154
213
  }
155
- if (types.includes("modules")) {
156
- results.modules = (data.service_map || [])
157
- .filter((m) => m.id?.toLowerCase().includes(q) ||
158
- m.path?.toLowerCase().includes(q))
159
- .slice(0, 10)
160
- .map((m) => `${m.id} (${m.type}, ${m.endpoint_count} eps, ${m.file_count} files, imports: ${m.imports?.join(",") || "none"})`);
214
+ // Otherwise treat as file path
215
+ const file = t.replace(/^\.\//, "");
216
+ const mod = findModule(d, file);
217
+ const eps = findEndpointsInFile(d, file);
218
+ const models = findModelsInFile(d, file);
219
+ const fileName = path.basename(file, path.extname(file));
220
+ const calledBy = [];
221
+ for (const ep of Object.values(d.api_registry || {})) {
222
+ if (ep.service_calls?.some((s) => s.toLowerCase().includes(fileName.toLowerCase()))) {
223
+ calledBy.push(`${ep.method} ${ep.path}`);
224
+ }
161
225
  }
162
- return JSON.stringify(results, null, 2);
163
- }
164
- async function endpointTrace(args) {
165
- const data = await loadIntel();
166
- const key = `${args.method.toUpperCase()} ${args.path}`;
167
- const ep = data.api_registry?.[key] || Object.values(data.api_registry || {}).find((e) => e.method === args.method.toUpperCase() && e.path === args.path);
168
- if (!ep)
169
- return JSON.stringify({ error: `Endpoint ${key} not found` });
170
- // Find which frontend pages call this endpoint
171
- const frontendCallers = (data.frontend_pages || []).filter((p) => p.api_calls?.some((call) => call.includes(args.path.split("{")[0])));
172
- // Find what models this endpoint uses
173
- const models = Object.values(data.model_registry || {}).filter((m) => ep.request_schema === m.name || ep.response_schema === m.name);
174
- return JSON.stringify({
175
- endpoint: `${ep.method} ${ep.path}`,
176
- handler: ep.handler,
177
- file: ep.file,
178
- module: ep.module,
179
- request_schema: ep.request_schema,
180
- response_schema: ep.response_schema,
181
- service_calls: ep.service_calls,
182
- ai_operations: ep.ai_operations,
183
- patterns: ep.patterns,
184
- models_used: models.map((m) => ({ name: m.name, fields: m.fields })),
185
- frontend_callers: frontendCallers.map((p) => p.path),
186
- }, null, 2);
226
+ const calls = eps.flatMap((ep) => (ep.service_calls || []).filter((s) => !SKIP_SERVICES.has(s)));
227
+ return compact({
228
+ f: file,
229
+ mod: mod ? [mod.id, mod.layer] : null,
230
+ ep: eps.map((e) => `${e.method} ${e.path}`),
231
+ models: models.map((m) => [m.name, m.fields?.length || 0]),
232
+ calls: [...new Set(calls)],
233
+ calledBy: calledBy.slice(0, 8),
234
+ });
187
235
  }
188
- async function impactCheck(args) {
189
- const data = await loadIntel();
190
- const file = args.file.replace(/^\.\//, "");
191
- // Find all endpoints in this file
192
- const endpoints = Object.values(data.api_registry || {}).filter((ep) => ep.file && file.includes(ep.file.replace(/^\.\//, "")));
193
- // Find all models in this file
194
- const models = Object.values(data.model_registry || {}).filter((m) => m.file && file.includes(m.file.replace(/^\.\//, "")));
195
- // Find endpoints that USE these models
236
+ async function impact(args) {
237
+ const d = await loadIntel();
238
+ const file = args.target.replace(/^\.\//, "");
239
+ const eps = findEndpointsInFile(d, file);
240
+ const models = findModelsInFile(d, file);
196
241
  const modelNames = new Set(models.map((m) => m.name));
197
- const affectedEndpoints = Object.values(data.api_registry || {}).filter((ep) => ep.request_schema && modelNames.has(ep.request_schema) ||
198
- ep.response_schema && modelNames.has(ep.response_schema));
199
- // Find modules that import from this file's module
200
- const fileModule = data.service_map?.find((m) => m.path && file.startsWith(m.path.replace(/^\.\//, "")));
201
- const dependentModules = fileModule
202
- ? (data.service_map || []).filter((m) => m.imports?.includes(fileModule.id))
203
- : [];
204
- // Find frontend pages affected
205
- const affectedPages = (data.frontend_pages || []).filter((p) => p.api_calls?.some((call) => endpoints.some((ep) => call.includes(ep.path?.split("{")[0]))));
206
- return JSON.stringify({
207
- file,
208
- direct_endpoints: endpoints.map((ep) => `${ep.method} ${ep.path}`),
209
- models_defined: models.map((m) => m.name),
210
- endpoints_using_these_models: affectedEndpoints.map((ep) => `${ep.method} ${ep.path}`),
211
- dependent_modules: dependentModules.map((m) => m.id),
212
- affected_frontend_pages: affectedPages.map((p) => p.path),
213
- risk: endpoints.length + affectedEndpoints.length + dependentModules.length > 5 ? "HIGH" : "LOW",
214
- }, null, 2);
242
+ const affectedEps = Object.values(d.api_registry || {}).filter((ep) => (ep.request_schema && modelNames.has(ep.request_schema)) ||
243
+ (ep.response_schema && modelNames.has(ep.response_schema)));
244
+ const mod = findModule(d, file);
245
+ const depMods = mod ? (d.service_map || []).filter((m) => m.imports?.includes(mod.id)) : [];
246
+ const affectedPages = (d.frontend_pages || []).filter((p) => p.api_calls?.some((call) => eps.some((ep) => call.includes(ep.path?.split("{")[0]))));
247
+ const total = eps.length + affectedEps.length + depMods.length + affectedPages.length;
248
+ return compact({
249
+ f: file,
250
+ risk: total > 5 ? "HIGH" : total > 2 ? "MED" : "LOW",
251
+ ep: eps.map((e) => `${e.method} ${e.path}`),
252
+ models: models.map((m) => m.name),
253
+ affectedEp: affectedEps.map((e) => `${e.method} ${e.path}`),
254
+ depMods: depMods.map((m) => m.id),
255
+ pages: affectedPages.map((p) => p.path),
256
+ });
215
257
  }
216
- async function overview() {
217
- const data = await loadIntel();
218
- return JSON.stringify({
219
- project: data.meta?.project,
220
- counts: data.meta?.counts,
221
- modules: (data.service_map || [])
222
- .filter((m) => m.file_count > 0)
223
- .map((m) => ({ id: m.id, type: m.type, layer: m.layer, endpoints: m.endpoint_count, files: m.file_count, imports: m.imports })),
224
- pages: (data.frontend_pages || []).map((p) => ({ route: p.path, component: p.component })),
225
- top_endpoints: Object.values(data.api_registry || {})
226
- .sort((a, b) => (b.service_calls?.length || 0) - (a.service_calls?.length || 0))
227
- .slice(0, 5)
228
- .map((ep) => `${ep.method} ${ep.path} (${ep.service_calls?.length || 0} service calls)`),
229
- }, null, 2);
258
+ async function search(args) {
259
+ const d = await loadIntel();
260
+ const q = args.query.toLowerCase();
261
+ const eps = Object.values(d.api_registry || {}).filter((ep) => ep.path?.toLowerCase().includes(q) || ep.handler?.toLowerCase().includes(q) ||
262
+ ep.service_calls?.some((s) => s.toLowerCase().includes(q))).slice(0, 8).map((ep) => `${ep.method} ${ep.path} [${ep.module}]`);
263
+ const models = Object.values(d.model_registry || {}).filter((m) => m.name?.toLowerCase().includes(q) || m.fields?.some((f) => f.toLowerCase().includes(q))).slice(0, 8).map((m) => `${m.name}:${m.fields?.length}f`);
264
+ const mods = (d.service_map || []).filter((m) => m.id?.toLowerCase().includes(q)).slice(0, 5).map((m) => `${m.id}:${m.endpoint_count}ep`);
265
+ return compact({ ep: eps, mod: models, m: mods });
266
+ }
267
+ async function model(args) {
268
+ const d = await loadIntel();
269
+ const m = d.model_registry?.[args.name];
270
+ if (!m)
271
+ return compact({ err: "not found" });
272
+ const usedBy = Object.values(d.api_registry || {}).filter((ep) => ep.request_schema === args.name || ep.response_schema === args.name).map((ep) => `${ep.method} ${ep.path}`);
273
+ return compact({
274
+ name: m.name, fw: m.framework, f: m.file,
275
+ fields: m.fields, rels: m.relationships,
276
+ usedBy,
277
+ });
230
278
  }
231
279
  // ── MCP protocol ──
232
280
  const TOOLS = [
233
281
  {
234
- name: "guardian_file_context",
235
- description: "Get upstream/downstream dependencies, endpoints, models, and coupling for a file. Call this BEFORE editing any file.",
236
- inputSchema: {
237
- type: "object",
238
- properties: {
239
- file: { type: "string", description: "File path relative to project root (e.g. 'backend/service-conversation/engine.py')" },
240
- },
241
- required: ["file"],
242
- },
282
+ name: "guardian_orient",
283
+ description: "Compact project summary. Call at session start. Returns: project name, counts, top modules, page routes.",
284
+ inputSchema: { type: "object", properties: {} },
243
285
  },
244
286
  {
245
- name: "guardian_search",
246
- description: "Search the codebase for endpoints, models, or modules matching a keyword.",
287
+ name: "guardian_context",
288
+ description: "Get dependencies for a file or endpoint. Pass a file path (e.g. 'backend/service-conversation/engine.py') or an endpoint (e.g. 'POST /sessions/start').",
247
289
  inputSchema: {
248
290
  type: "object",
249
291
  properties: {
250
- query: { type: "string", description: "Search keyword (e.g. 'session', 'auth', 'TTS')" },
251
- types: { type: "string", description: "Comma-separated: models,endpoints,modules (default: all)" },
292
+ target: { type: "string", description: "File path or 'METHOD /path' endpoint" },
252
293
  },
253
- required: ["query"],
294
+ required: ["target"],
254
295
  },
255
296
  },
256
297
  {
257
- name: "guardian_endpoint_trace",
258
- description: "Trace an API endpoint's full chain: frontend callers, handler, service calls, models, AI operations.",
298
+ name: "guardian_impact",
299
+ description: "What breaks if you change this file? Returns affected endpoints, models, modules, pages, and risk level.",
259
300
  inputSchema: {
260
301
  type: "object",
261
302
  properties: {
262
- method: { type: "string", description: "HTTP method (GET, POST, PUT, DELETE)" },
263
- path: { type: "string", description: "Endpoint path (e.g. '/sessions/start')" },
303
+ target: { type: "string", description: "File path to check" },
264
304
  },
265
- required: ["method", "path"],
305
+ required: ["target"],
266
306
  },
267
307
  },
268
308
  {
269
- name: "guardian_impact_check",
270
- description: "Check what endpoints, models, modules, and pages are affected if you change a file. Call this BEFORE making changes to high-coupling files.",
309
+ name: "guardian_search",
310
+ description: "Find endpoints, models, modules by keyword. Returns compact one-line results.",
271
311
  inputSchema: {
272
312
  type: "object",
273
313
  properties: {
274
- file: { type: "string", description: "File path to check impact for" },
314
+ query: { type: "string", description: "Search keyword" },
275
315
  },
276
- required: ["file"],
316
+ required: ["query"],
277
317
  },
278
318
  },
279
319
  {
280
- name: "guardian_overview",
281
- description: "Get project summary: modules, pages, top endpoints, counts. Call this at session start for orientation.",
320
+ name: "guardian_model",
321
+ description: "Get full field list and usage for a specific model. Only call when you need field details.",
282
322
  inputSchema: {
283
323
  type: "object",
284
- properties: {},
324
+ properties: {
325
+ name: { type: "string", description: "Model name (e.g. 'StartSessionRequest')" },
326
+ },
327
+ required: ["name"],
285
328
  },
286
329
  },
287
330
  {
288
331
  name: "guardian_metrics",
289
- description: "Get MCP usage metrics for this session: calls made, tokens spent, tokens saved, cache hits. Call at end of session to evaluate guardian's usefulness.",
290
- inputSchema: {
291
- type: "object",
292
- properties: {},
293
- },
332
+ description: "MCP usage stats for this session. Call at end to evaluate guardian's usefulness.",
333
+ inputSchema: { type: "object", properties: {} },
294
334
  },
295
335
  ];
296
336
  const TOOL_HANDLERS = {
297
- guardian_file_context: fileContext,
337
+ guardian_orient: orient,
338
+ guardian_context: context,
339
+ guardian_impact: impact,
298
340
  guardian_search: search,
299
- guardian_endpoint_trace: endpointTrace,
300
- guardian_impact_check: impactCheck,
301
- guardian_overview: overview,
302
- guardian_metrics: async () => JSON.stringify(metrics.summary(), null, 2),
341
+ guardian_model: model,
342
+ guardian_metrics: async () => compact(metrics.summary()),
303
343
  };
304
344
  function respond(id, result) {
305
345
  const msg = JSON.stringify({ jsonrpc: "2.0", id, result });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolbaux/guardian",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "type": "module",
5
5
  "description": "Architectural intelligence for codebases. Verify that AI-generated code matches your architectural intent.",
6
6
  "keywords": [